Skip to content

Commit

Permalink
nodedev: Introduce the mdev capability to a PCI parent device
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
eskultety committed May 18, 2017
1 parent 4385df9 commit 500cbc0
Show file tree
Hide file tree
Showing 8 changed files with 313 additions and 2 deletions.
26 changes: 26 additions & 0 deletions docs/schemas/nodedev.rng
Original file line number Diff line number Diff line change
Expand Up @@ -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'>
Expand Down
101 changes: 101 additions & 0 deletions src/conf/node_device_conf.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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,
Expand All @@ -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);

Expand Down Expand Up @@ -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);
Expand Down
15 changes: 15 additions & 0 deletions src/conf/node_device_conf.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand All @@ -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;
Expand Down Expand Up @@ -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 | \
Expand Down
20 changes: 18 additions & 2 deletions src/conf/virnodedeviceobj.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,27 @@ 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)) ||
(STREQ(cap, vports_cap) &&
(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;
Expand Down Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions src/libvirt_private.syms
Original file line number Diff line number Diff line change
Expand Up @@ -668,6 +668,7 @@ virNetDevIPRouteParseXML;


# conf/node_device_conf.h
virNodeDevCapMdevTypeFree;
virNodeDevCapsDefFree;
virNodeDevCapTypeFromString;
virNodeDevCapTypeToString;
Expand Down
119 changes: 119 additions & 0 deletions src/node_device/node_device_udev.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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:
Expand Down
Loading

0 comments on commit 500cbc0

Please sign in to comment.