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:
Erik Skultety 2017-03-06 17:17:34 +01:00
parent 4385df97fe
commit 500cbc066a
8 changed files with 313 additions and 2 deletions

View File

@ -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'>

View File

@ -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);

View File

@ -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 | \

View File

@ -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;

View File

@ -668,6 +668,7 @@ virNetDevIPRouteParseXML;
# conf/node_device_conf.h
virNodeDevCapMdevTypeFree;
virNodeDevCapsDefFree;
virNodeDevCapTypeFromString;
virNodeDevCapTypeToString;

View File

@ -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:

View 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>

View File

@ -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;
}