mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-22 13:45:38 +00:00
nodedev: Introduce the mdev capability to a PCI parent device
The parent device needs to report the generic stuff about the supported mediated devices types, like device API, available instances, type name, etc. Therefore this patch introduces a new nested capability element of type 'mdev_types' with the resulting XML of the following format: <device> ... <capability type='pci'> ... <capability type='mdev_types'> <type id='vendor_supplied_id'> <name>optional_vendor_supplied_codename</name> <deviceAPI>vfio-pci</deviceAPI> <availableInstances>NUM</availableInstances> </type> ... <type> ... </type> </capability> </capability> ... </device> https://bugzilla.redhat.com/show_bug.cgi?id=1452072 Signed-off-by: Erik Skultety <eskultet@redhat.com>
This commit is contained in:
parent
4385df97fe
commit
500cbc066a
@ -204,6 +204,32 @@
|
||||
</element>
|
||||
</optional>
|
||||
|
||||
<optional>
|
||||
<element name='capability'>
|
||||
<attribute name='type'>
|
||||
<value>mdev_types</value>
|
||||
</attribute>
|
||||
<oneOrMore>
|
||||
<element name='type'>
|
||||
<attribute name='id'>
|
||||
<data type='string'/>
|
||||
</attribute>
|
||||
<optional>
|
||||
<element name='name'><text/></element>
|
||||
</optional>
|
||||
<element name='deviceAPI'>
|
||||
<choice>
|
||||
<value>vfio-pci</value>
|
||||
</choice>
|
||||
</element>
|
||||
<element name='availableInstances'>
|
||||
<ref name='unsignedInt'/>
|
||||
</element>
|
||||
</element>
|
||||
</oneOrMore>
|
||||
</element>
|
||||
</optional>
|
||||
|
||||
<optional>
|
||||
<element name='iommuGroup'>
|
||||
<attribute name='number'>
|
||||
|
@ -88,6 +88,19 @@ virNodeDevCapsDefParseString(const char *xpath,
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
virNodeDevCapMdevTypeFree(virNodeDevCapMdevTypePtr type)
|
||||
{
|
||||
if (!type)
|
||||
return;
|
||||
|
||||
VIR_FREE(type->id);
|
||||
VIR_FREE(type->name);
|
||||
VIR_FREE(type->device_api);
|
||||
VIR_FREE(type);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
virNodeDeviceDefFree(virNodeDeviceDefPtr def)
|
||||
{
|
||||
@ -265,6 +278,27 @@ virNodeDeviceCapPCIDefFormat(virBufferPtr buf,
|
||||
virBufferAsprintf(buf, "<capability type='%s'/>\n",
|
||||
virPCIHeaderTypeToString(data->pci_dev.hdrType));
|
||||
}
|
||||
if (data->pci_dev.flags & VIR_NODE_DEV_CAP_FLAG_PCI_MDEV) {
|
||||
virBufferAddLit(buf, "<capability type='mdev_types'>\n");
|
||||
virBufferAdjustIndent(buf, 2);
|
||||
for (i = 0; i < data->pci_dev.nmdev_types; i++) {
|
||||
virNodeDevCapMdevTypePtr type = data->pci_dev.mdev_types[i];
|
||||
virBufferEscapeString(buf, "<type id='%s'>\n", type->id);
|
||||
virBufferAdjustIndent(buf, 2);
|
||||
if (type->name)
|
||||
virBufferEscapeString(buf, "<name>%s</name>\n",
|
||||
type->name);
|
||||
virBufferEscapeString(buf, "<deviceAPI>%s</deviceAPI>\n",
|
||||
type->device_api);
|
||||
virBufferAsprintf(buf,
|
||||
"<availableInstances>%u</availableInstances>\n",
|
||||
type->available_instances);
|
||||
virBufferAdjustIndent(buf, -2);
|
||||
virBufferAddLit(buf, "</type>\n");
|
||||
}
|
||||
virBufferAdjustIndent(buf, -2);
|
||||
virBufferAddLit(buf, "</capability>\n");
|
||||
}
|
||||
if (data->pci_dev.nIommuGroupDevices) {
|
||||
virBufferAsprintf(buf, "<iommuGroup number='%d'>\n",
|
||||
data->pci_dev.iommuGroupNumber);
|
||||
@ -1364,6 +1398,67 @@ virNodeDevPCICapSRIOVVirtualParseXML(xmlXPathContextPtr ctxt,
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
virNodeDevPCICapMdevTypesParseXML(xmlXPathContextPtr ctxt,
|
||||
virNodeDevCapPCIDevPtr pci_dev)
|
||||
{
|
||||
int ret = -1;
|
||||
xmlNodePtr orignode = NULL;
|
||||
xmlNodePtr *nodes = NULL;
|
||||
int nmdev_types = -1;
|
||||
virNodeDevCapMdevTypePtr type = NULL;
|
||||
size_t i;
|
||||
|
||||
if ((nmdev_types = virXPathNodeSet("./type", ctxt, &nodes)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
orignode = ctxt->node;
|
||||
for (i = 0; i < nmdev_types; i++) {
|
||||
ctxt->node = nodes[i];
|
||||
|
||||
if (VIR_ALLOC(type) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (!(type->id = virXPathString("string(./@id[1])", ctxt))) {
|
||||
virReportError(VIR_ERR_XML_ERROR, "%s",
|
||||
_("missing 'id' attribute for mediated device's "
|
||||
"<type> element"));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (!(type->device_api = virXPathString("string(./deviceAPI[1])", ctxt))) {
|
||||
virReportError(VIR_ERR_XML_ERROR,
|
||||
_("missing device API for mediated device type '%s'"),
|
||||
type->id);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (virXPathUInt("number(./availableInstances)", ctxt,
|
||||
&type->available_instances) < 0) {
|
||||
virReportError(VIR_ERR_XML_ERROR,
|
||||
_("missing number of available instances for "
|
||||
"mediated device type '%s'"),
|
||||
type->id);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
type->name = virXPathString("string(./name)", ctxt);
|
||||
|
||||
if (VIR_APPEND_ELEMENT(pci_dev->mdev_types,
|
||||
pci_dev->nmdev_types, type) < 0)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
pci_dev->flags |= VIR_NODE_DEV_CAP_FLAG_PCI_MDEV;
|
||||
ret = 0;
|
||||
cleanup:
|
||||
VIR_FREE(nodes);
|
||||
virNodeDevCapMdevTypeFree(type);
|
||||
ctxt->node = orignode;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
virNodeDevPCICapabilityParseXML(xmlXPathContextPtr ctxt,
|
||||
xmlNodePtr node,
|
||||
@ -1386,6 +1481,9 @@ virNodeDevPCICapabilityParseXML(xmlXPathContextPtr ctxt,
|
||||
} else if (STREQ(type, "virt_functions") &&
|
||||
virNodeDevPCICapSRIOVVirtualParseXML(ctxt, pci_dev) < 0) {
|
||||
goto cleanup;
|
||||
} else if (STREQ(type, "mdev_types") &&
|
||||
virNodeDevPCICapMdevTypesParseXML(ctxt, pci_dev) < 0) {
|
||||
goto cleanup;
|
||||
} else {
|
||||
int hdrType = virPCIHeaderTypeFromString(type);
|
||||
|
||||
@ -1899,6 +1997,9 @@ virNodeDevCapsDefFree(virNodeDevCapsDefPtr caps)
|
||||
VIR_FREE(data->pci_dev.iommuGroupDevices[i]);
|
||||
VIR_FREE(data->pci_dev.iommuGroupDevices);
|
||||
virPCIEDeviceInfoFree(data->pci_dev.pci_express);
|
||||
for (i = 0; i < data->pci_dev.nmdev_types; i++)
|
||||
virNodeDevCapMdevTypeFree(data->pci_dev.mdev_types[i]);
|
||||
VIR_FREE(data->pci_dev.mdev_types);
|
||||
break;
|
||||
case VIR_NODE_DEV_CAP_USB_DEV:
|
||||
VIR_FREE(data->usb_dev.product_name);
|
||||
|
@ -95,6 +95,7 @@ typedef enum {
|
||||
VIR_NODE_DEV_CAP_FLAG_PCI_PHYSICAL_FUNCTION = (1 << 0),
|
||||
VIR_NODE_DEV_CAP_FLAG_PCI_VIRTUAL_FUNCTION = (1 << 1),
|
||||
VIR_NODE_DEV_CAP_FLAG_PCIE = (1 << 2),
|
||||
VIR_NODE_DEV_CAP_FLAG_PCI_MDEV = (1 << 3),
|
||||
} virNodeDevPCICapFlags;
|
||||
|
||||
typedef enum {
|
||||
@ -133,6 +134,15 @@ struct _virNodeDevCapSystem {
|
||||
virNodeDevCapSystemFirmware firmware;
|
||||
};
|
||||
|
||||
typedef struct _virNodeDevCapMdevType virNodeDevCapMdevType;
|
||||
typedef virNodeDevCapMdevType *virNodeDevCapMdevTypePtr;
|
||||
struct _virNodeDevCapMdevType {
|
||||
char *id;
|
||||
char *name;
|
||||
char *device_api;
|
||||
unsigned int available_instances;
|
||||
};
|
||||
|
||||
typedef struct _virNodeDevCapPCIDev virNodeDevCapPCIDev;
|
||||
typedef virNodeDevCapPCIDev *virNodeDevCapPCIDevPtr;
|
||||
struct _virNodeDevCapPCIDev {
|
||||
@ -156,6 +166,8 @@ struct _virNodeDevCapPCIDev {
|
||||
int numa_node;
|
||||
virPCIEDeviceInfoPtr pci_express;
|
||||
int hdrType; /* enum virPCIHeaderType or -1 */
|
||||
virNodeDevCapMdevTypePtr *mdev_types;
|
||||
size_t nmdev_types;
|
||||
};
|
||||
|
||||
typedef struct _virNodeDevCapUSBDev virNodeDevCapUSBDev;
|
||||
@ -340,6 +352,9 @@ virNodeDeviceDefFree(virNodeDeviceDefPtr def);
|
||||
void
|
||||
virNodeDevCapsDefFree(virNodeDevCapsDefPtr caps);
|
||||
|
||||
void
|
||||
virNodeDevCapMdevTypeFree(virNodeDevCapMdevTypePtr type);
|
||||
|
||||
# define VIR_CONNECT_LIST_NODE_DEVICES_FILTERS_CAP \
|
||||
(VIR_CONNECT_LIST_NODE_DEVICES_CAP_SYSTEM | \
|
||||
VIR_CONNECT_LIST_NODE_DEVICES_CAP_PCI_DEV | \
|
||||
|
@ -42,11 +42,13 @@ virNodeDeviceObjHasCap(const virNodeDeviceObj *dev,
|
||||
virNodeDevCapTypeToString(VIR_NODE_DEV_CAP_FC_HOST);
|
||||
const char *vports_cap =
|
||||
virNodeDevCapTypeToString(VIR_NODE_DEV_CAP_VPORTS);
|
||||
const char *mdev_types =
|
||||
virNodeDevCapTypeToString(VIR_NODE_DEV_CAP_MDEV_TYPES);
|
||||
|
||||
while (caps) {
|
||||
if (STREQ(cap, virNodeDevCapTypeToString(caps->data.type)))
|
||||
if (STREQ(cap, virNodeDevCapTypeToString(caps->data.type))) {
|
||||
return 1;
|
||||
else if (caps->data.type == VIR_NODE_DEV_CAP_SCSI_HOST)
|
||||
} else if (caps->data.type == VIR_NODE_DEV_CAP_SCSI_HOST) {
|
||||
if ((STREQ(cap, fc_host_cap) &&
|
||||
(caps->data.scsi_host.flags &
|
||||
VIR_NODE_DEV_CAP_FLAG_HBA_FC_HOST)) ||
|
||||
@ -54,6 +56,13 @@ virNodeDeviceObjHasCap(const virNodeDeviceObj *dev,
|
||||
(caps->data.scsi_host.flags &
|
||||
VIR_NODE_DEV_CAP_FLAG_HBA_VPORT_OPS)))
|
||||
return 1;
|
||||
} else if (caps->data.type == VIR_NODE_DEV_CAP_PCI_DEV) {
|
||||
if ((STREQ(cap, mdev_types)) &&
|
||||
(caps->data.pci_dev.flags &
|
||||
VIR_NODE_DEV_CAP_FLAG_PCI_MDEV))
|
||||
return 1;
|
||||
}
|
||||
|
||||
caps = caps->next;
|
||||
}
|
||||
return 0;
|
||||
@ -468,6 +477,13 @@ virNodeDeviceCapMatch(virNodeDeviceObjPtr devobj,
|
||||
VIR_NODE_DEV_CAP_FLAG_HBA_VPORT_OPS))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cap->data.type == VIR_NODE_DEV_CAP_PCI_DEV) {
|
||||
if (type == VIR_NODE_DEV_CAP_MDEV_TYPES &&
|
||||
(cap->data.pci_dev.flags &
|
||||
VIR_NODE_DEV_CAP_FLAG_PCI_MDEV))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -668,6 +668,7 @@ virNetDevIPRouteParseXML;
|
||||
|
||||
|
||||
# conf/node_device_conf.h
|
||||
virNodeDevCapMdevTypeFree;
|
||||
virNodeDevCapsDefFree;
|
||||
virNodeDevCapTypeFromString;
|
||||
virNodeDevCapTypeToString;
|
||||
|
@ -314,6 +314,119 @@ static int udevTranslatePCIIds(unsigned int vendor,
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
udevFillMdevType(struct udev_device *device,
|
||||
const char *dir,
|
||||
virNodeDevCapMdevTypePtr type)
|
||||
{
|
||||
int ret = -1;
|
||||
char *attrpath = NULL;
|
||||
|
||||
#define MDEV_GET_SYSFS_ATTR(attr_name, cb, ...) \
|
||||
do { \
|
||||
if (virAsprintf(&attrpath, "%s/%s", dir, #attr_name) < 0) \
|
||||
goto cleanup; \
|
||||
\
|
||||
if (cb(device, attrpath, __VA_ARGS__) < 0) \
|
||||
goto cleanup; \
|
||||
\
|
||||
VIR_FREE(attrpath); \
|
||||
} while (0) \
|
||||
|
||||
if (VIR_STRDUP(type->id, last_component(dir)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
/* query udev for the attributes under subdirectories using the relative
|
||||
* path stored in @dir, i.e. 'mdev_supported_types/<type_id>'
|
||||
*/
|
||||
MDEV_GET_SYSFS_ATTR(name, udevGetStringSysfsAttr, &type->name);
|
||||
MDEV_GET_SYSFS_ATTR(device_api, udevGetStringSysfsAttr, &type->device_api);
|
||||
MDEV_GET_SYSFS_ATTR(available_instances, udevGetUintSysfsAttr,
|
||||
&type->available_instances, 10);
|
||||
|
||||
#undef MDEV_GET_SYSFS_ATTR
|
||||
|
||||
ret = 0;
|
||||
cleanup:
|
||||
VIR_FREE(attrpath);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
udevPCIGetMdevTypesCap(struct udev_device *device,
|
||||
virNodeDevCapPCIDevPtr pcidata)
|
||||
{
|
||||
int ret = -1;
|
||||
int dirret = -1;
|
||||
DIR *dir = NULL;
|
||||
struct dirent *entry;
|
||||
char *path = NULL;
|
||||
char *tmppath = NULL;
|
||||
virNodeDevCapMdevTypePtr type = NULL;
|
||||
virNodeDevCapMdevTypePtr *types = NULL;
|
||||
size_t ntypes = 0;
|
||||
size_t i;
|
||||
|
||||
if (virAsprintf(&path, "%s/mdev_supported_types",
|
||||
udev_device_get_syspath(device)) < 0)
|
||||
return -1;
|
||||
|
||||
if ((dirret = virDirOpenIfExists(&dir, path)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (dirret == 0) {
|
||||
ret = 0;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (VIR_ALLOC(types) < 0)
|
||||
goto cleanup;
|
||||
|
||||
/* UDEV doesn't report attributes under subdirectories by default but is
|
||||
* able to query them if the path to the attribute is relative to the
|
||||
* device's base path, e.g. /sys/devices/../0000:00:01.0/ is the device's
|
||||
* base path as udev reports it, but we're interested in attributes under
|
||||
* /sys/devices/../0000:00:01.0/mdev_supported_types/<type>/. So, we need to
|
||||
* scan the subdirectories ourselves.
|
||||
*/
|
||||
while ((dirret = virDirRead(dir, &entry, path)) > 0) {
|
||||
if (VIR_ALLOC(type) < 0)
|
||||
goto cleanup;
|
||||
|
||||
/* construct the relative mdev type path bit for udev */
|
||||
if (virAsprintf(&tmppath, "mdev_supported_types/%s", entry->d_name) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (udevFillMdevType(device, tmppath, type) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (VIR_APPEND_ELEMENT(types, ntypes, type) < 0)
|
||||
goto cleanup;
|
||||
|
||||
VIR_FREE(tmppath);
|
||||
}
|
||||
|
||||
if (dirret < 0)
|
||||
goto cleanup;
|
||||
|
||||
VIR_STEAL_PTR(pcidata->mdev_types, types);
|
||||
pcidata->nmdev_types = ntypes;
|
||||
pcidata->flags |= VIR_NODE_DEV_CAP_FLAG_PCI_MDEV;
|
||||
ntypes = 0;
|
||||
ret = 0;
|
||||
cleanup:
|
||||
virNodeDevCapMdevTypeFree(type);
|
||||
for (i = 0; i < ntypes; i++)
|
||||
virNodeDevCapMdevTypeFree(types[i]);
|
||||
VIR_FREE(types);
|
||||
VIR_FREE(path);
|
||||
VIR_FREE(tmppath);
|
||||
VIR_DIR_CLOSE(dir);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int udevProcessPCI(struct udev_device *device,
|
||||
virNodeDeviceDefPtr def)
|
||||
{
|
||||
@ -400,6 +513,12 @@ static int udevProcessPCI(struct udev_device *device,
|
||||
}
|
||||
}
|
||||
|
||||
/* check whether the device is mediated devices framework capable, if so,
|
||||
* process it
|
||||
*/
|
||||
if (udevPCIGetMdevTypesCap(device, pci_dev) < 0)
|
||||
goto cleanup;
|
||||
|
||||
ret = 0;
|
||||
|
||||
cleanup:
|
||||
|
32
tests/nodedevschemadata/pci_0000_02_10_7_mdev_types.xml
Normal file
32
tests/nodedevschemadata/pci_0000_02_10_7_mdev_types.xml
Normal file
@ -0,0 +1,32 @@
|
||||
<device>
|
||||
<name>pci_0000_02_10_7</name>
|
||||
<parent>pci_0000_00_04_0</parent>
|
||||
<capability type='pci'>
|
||||
<domain>0</domain>
|
||||
<bus>2</bus>
|
||||
<slot>16</slot>
|
||||
<function>7</function>
|
||||
<product id='0x10ca'>82576 Virtual Function</product>
|
||||
<vendor id='0x8086'>Intel Corporation</vendor>
|
||||
<capability type='mdev_types'>
|
||||
<type id='foo1'>
|
||||
<name>bar1</name>
|
||||
<deviceAPI>vfio-pci</deviceAPI>
|
||||
<availableInstances>1</availableInstances>
|
||||
</type>
|
||||
<type id='foo2'>
|
||||
<name>bar2</name>
|
||||
<deviceAPI>vfio-pci</deviceAPI>
|
||||
<availableInstances>2</availableInstances>
|
||||
</type>
|
||||
</capability>
|
||||
<iommuGroup number='31'>
|
||||
<address domain='0x0000' bus='0x02' slot='0x10' function='0x7'/>
|
||||
</iommuGroup>
|
||||
<numa node='0'/>
|
||||
<pci-express>
|
||||
<link validity='cap' port='0' speed='2.5' width='4'/>
|
||||
<link validity='sta' width='0'/>
|
||||
</pci-express>
|
||||
</capability>
|
||||
</device>
|
@ -101,6 +101,7 @@ mymain(void)
|
||||
DO_TEST("pci_0000_02_10_7_sriov_pf_vfs_all");
|
||||
DO_TEST("pci_0000_02_10_7_sriov_pf_vfs_all_header_type");
|
||||
DO_TEST("drm_renderD129");
|
||||
DO_TEST("pci_0000_02_10_7_mdev_types");
|
||||
|
||||
return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user