[libvirt] [PATCH v2 00/10] Add mdev reporting capability to the nodedev driver

since v1: - dropped the <description> element from the parent device nested capability - added missing RNG schema and tests - updated the documentation to describe the MDEV elements in both the parent and the child So, regarding the discussion about the presence of <description> element in the device XML, I dropped it for v2. There were points that we could continue debating about, but one of the major points against it is that libvirt should understand the information it's gathering and then exposing it in a structured way, which in this case wasn't the case, which wouldn't make the management layer's job any easier in any way, since they'd have to parse it either way (from libvirt or on their own). So, until this is resolved among vendors so that we can apply some kind of device classification (no userspace DB), management will have to simply read the description directly from sysfs. As part of a private discussion, another interesting point has been raised - virsh nodedev-list does support '--cap' option which filters only devices matching the pattern. However, with SRIOV, NPIV, and MDEV we have nested capabilities which are kept private and cannot be filtered by. That might be a nice feature for users: 1) OK, let's see what MDEVs are there on the host virsh nodedev-list --cap="mdev-<child|whatever>" 2) I'd like to create some on my own, so which physical devices do support MDEV? virsh nodedev-list --cap='mdev-<parent|whatever>' 3) create the device on that physical device The same goes for SRIOV, where we have virt_functions in parent and phys_function in child and NPIV (I don't remember what it was here), I didn't check in depth, but I think it should be possible for us to do and it's a valid use case where users don't have to parse dumpxml's output for information about the parent's nested capabilities. Erik Erik Skultety (10): nodedev: Make use of the compile-time missing enum in switch error conf: nodedev: Split virNodeDeviceDefFormat into more functions nodedev: udevProcessPCI: Drop syspath variable docs: Utilize our XSLT list generating template more nodedev: conf: Split PCI sub-capability parsing to a separate method nodedev: Introduce the mdev capability to the nodedev driver structure nodedev: Introduce the mdev capability to a PCI parent device nodedev: Introduce mdev capability for child devices docs: Provide a nodedev driver stub documentation docs: Document the mediated devices within the nodedev driver docs/drivers.html.in | 6 +- docs/drvnodedev.html.in | 344 +++++++++ docs/remote.html.in | 106 +-- docs/schemas/nodedev.rng | 41 ++ docs/storage.html.in | 62 +- include/libvirt/libvirt-nodedev.h | 1 + src/conf/node_device_conf.c | 812 +++++++++++++-------- src/conf/node_device_conf.h | 21 +- src/conf/virnodedeviceobj.c | 3 +- src/libvirt-nodedev.c | 1 + src/libvirt_private.syms | 1 + src/node_device/node_device_driver.c | 1 + src/node_device/node_device_udev.c | 242 +++++- .../mdev_3627463d_b7f0_4fea_b468_f1da537d301b.xml | 8 + tests/nodedevschemadata/pci_0000_02_10_7_mdev.xml | 27 + tests/nodedevxml2xmltest.c | 1 + tools/virsh-nodedev.c | 2 + 17 files changed, 1223 insertions(+), 456 deletions(-) create mode 100644 docs/drvnodedev.html.in create mode 100644 tests/nodedevschemadata/mdev_3627463d_b7f0_4fea_b468_f1da537d301b.xml create mode 100644 tests/nodedevschemadata/pci_0000_02_10_7_mdev.xml -- 2.12.2

So udevGetDeviceDetails was one those functions using an enum in a switch, but since it had a 'default' case, compiler didn't warn about an unhandled enum. Moreover, the error about an unsupported device type reported in the default case is unnecessary, since by the time we get there, udevGetDeviceType (which was called before) already made sure that any unrecognized device types had been handled properly. Signed-off-by: Erik Skultety <eskultet@redhat.com> --- src/node_device/node_device_udev.c | 45 +++++++++++++------------------------- 1 file changed, 15 insertions(+), 30 deletions(-) diff --git a/src/node_device/node_device_udev.c b/src/node_device/node_device_udev.c index bcae444d8..591da8db2 100644 --- a/src/node_device/node_device_udev.c +++ b/src/node_device/node_device_udev.c @@ -1043,50 +1043,35 @@ udevGetDeviceType(struct udev_device *device, static int udevGetDeviceDetails(struct udev_device *device, virNodeDeviceDefPtr def) { - int ret = 0; - switch (def->caps->data.type) { - case VIR_NODE_DEV_CAP_SYSTEM: - /* There's no libudev equivalent of system, so ignore it. */ - break; case VIR_NODE_DEV_CAP_PCI_DEV: - ret = udevProcessPCI(device, def); - break; + return udevProcessPCI(device, def); case VIR_NODE_DEV_CAP_USB_DEV: - ret = udevProcessUSBDevice(device, def); - break; + return udevProcessUSBDevice(device, def); case VIR_NODE_DEV_CAP_USB_INTERFACE: - ret = udevProcessUSBInterface(device, def); - break; + return udevProcessUSBInterface(device, def); case VIR_NODE_DEV_CAP_NET: - ret = udevProcessNetworkInterface(device, def); - break; + return udevProcessNetworkInterface(device, def); case VIR_NODE_DEV_CAP_SCSI_HOST: - ret = udevProcessSCSIHost(device, def); - break; + return udevProcessSCSIHost(device, def); case VIR_NODE_DEV_CAP_SCSI_TARGET: - ret = udevProcessSCSITarget(device, def); - break; + return udevProcessSCSITarget(device, def); case VIR_NODE_DEV_CAP_SCSI: - ret = udevProcessSCSIDevice(device, def); - break; + return udevProcessSCSIDevice(device, def); case VIR_NODE_DEV_CAP_STORAGE: - ret = udevProcessStorage(device, def); - break; + return udevProcessStorage(device, def); case VIR_NODE_DEV_CAP_SCSI_GENERIC: - ret = udevProcessSCSIGeneric(device, def); - break; + return udevProcessSCSIGeneric(device, def); case VIR_NODE_DEV_CAP_DRM: - ret = udevProcessDRMDevice(device, def); - break; - default: - virReportError(VIR_ERR_INTERNAL_ERROR, - _("Unknown device type %d"), def->caps->data.type); - ret = -1; + return udevProcessDRMDevice(device, def); + case VIR_NODE_DEV_CAP_SYSTEM: + case VIR_NODE_DEV_CAP_FC_HOST: + case VIR_NODE_DEV_CAP_VPORTS: + case VIR_NODE_DEV_CAP_LAST: break; } - return ret; + return 0; } -- 2.12.2

On Thu, Apr 20, 2017 at 03:05:51PM +0200, Erik Skultety wrote:
So udevGetDeviceDetails was one those functions using an enum in a
s/one those/one of those/
switch, but since it had a 'default' case, compiler didn't warn about an unhandled enum. Moreover, the error about an unsupported device type reported in the default case is unnecessary, since by the time we get there, udevGetDeviceType (which was called before) already made sure that any unrecognized device types had been handled properly.
Signed-off-by: Erik Skultety <eskultet@redhat.com> --- src/node_device/node_device_udev.c | 45 +++++++++++++------------------------- 1 file changed, 15 insertions(+), 30 deletions(-)
ACK Pavel

Make the code look cleaner by moving the capability specific bits into separate functions. Signed-off-by: Erik Skultety <eskultet@redhat.com> --- src/conf/node_device_conf.c | 578 ++++++++++++++++++++++++-------------------- 1 file changed, 322 insertions(+), 256 deletions(-) diff --git a/src/conf/node_device_conf.c b/src/conf/node_device_conf.c index d2ad6352b..85cfd8396 100644 --- a/src/conf/node_device_conf.c +++ b/src/conf/node_device_conf.c @@ -155,6 +155,320 @@ virPCIEDeviceInfoFormat(virBufferPtr buf, } +static void +virNodeDeviceCapSystemDefFormat(virBufferPtr buf, + const virNodeDevCapData *data) +{ + char uuidstr[VIR_UUID_STRING_BUFLEN]; + + if (data->system.product_name) + virBufferEscapeString(buf, "<product>%s</product>\n", + data->system.product_name); + virBufferAddLit(buf, "<hardware>\n"); + virBufferAdjustIndent(buf, 2); + if (data->system.hardware.vendor_name) + virBufferEscapeString(buf, "<vendor>%s</vendor>\n", + data->system.hardware.vendor_name); + if (data->system.hardware.version) + virBufferEscapeString(buf, "<version>%s</version>\n", + data->system.hardware.version); + if (data->system.hardware.serial) + virBufferEscapeString(buf, "<serial>%s</serial>\n", + data->system.hardware.serial); + virUUIDFormat(data->system.hardware.uuid, uuidstr); + virBufferAsprintf(buf, "<uuid>%s</uuid>\n", uuidstr); + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</hardware>\n"); + + virBufferAddLit(buf, "<firmware>\n"); + virBufferAdjustIndent(buf, 2); + if (data->system.firmware.vendor_name) + virBufferEscapeString(buf, "<vendor>%s</vendor>\n", + data->system.firmware.vendor_name); + if (data->system.firmware.version) + virBufferEscapeString(buf, "<version>%s</version>\n", + data->system.firmware.version); + if (data->system.firmware.release_date) + virBufferEscapeString(buf, "<release_date>%s</release_date>\n", + data->system.firmware.release_date); + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</firmware>\n"); +} + + +static void +virNodeDeviceCapPCIDefFormat(virBufferPtr buf, + const virNodeDevCapData *data) +{ + size_t i; + + virBufferAsprintf(buf, "<domain>%d</domain>\n", + data->pci_dev.domain); + virBufferAsprintf(buf, "<bus>%d</bus>\n", data->pci_dev.bus); + virBufferAsprintf(buf, "<slot>%d</slot>\n", + data->pci_dev.slot); + virBufferAsprintf(buf, "<function>%d</function>\n", + data->pci_dev.function); + virBufferAsprintf(buf, "<product id='0x%04x'", + data->pci_dev.product); + if (data->pci_dev.product_name) + virBufferEscapeString(buf, ">%s</product>\n", + data->pci_dev.product_name); + else + virBufferAddLit(buf, " />\n"); + virBufferAsprintf(buf, "<vendor id='0x%04x'", + data->pci_dev.vendor); + if (data->pci_dev.vendor_name) + virBufferEscapeString(buf, ">%s</vendor>\n", + data->pci_dev.vendor_name); + else + virBufferAddLit(buf, " />\n"); + if (data->pci_dev.flags & VIR_NODE_DEV_CAP_FLAG_PCI_PHYSICAL_FUNCTION) { + virBufferAddLit(buf, "<capability type='phys_function'>\n"); + virBufferAdjustIndent(buf, 2); + virBufferAsprintf(buf, + "<address domain='0x%.4x' bus='0x%.2x' " + "slot='0x%.2x' function='0x%.1x'/>\n", + data->pci_dev.physical_function->domain, + data->pci_dev.physical_function->bus, + data->pci_dev.physical_function->slot, + data->pci_dev.physical_function->function); + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</capability>\n"); + } + if (data->pci_dev.flags & VIR_NODE_DEV_CAP_FLAG_PCI_VIRTUAL_FUNCTION) { + virBufferAddLit(buf, "<capability type='virt_functions'"); + if (data->pci_dev.max_virtual_functions) + virBufferAsprintf(buf, " maxCount='%u'", + data->pci_dev.max_virtual_functions); + if (data->pci_dev.num_virtual_functions == 0) { + virBufferAddLit(buf, "/>\n"); + } else { + virBufferAddLit(buf, ">\n"); + virBufferAdjustIndent(buf, 2); + for (i = 0; i < data->pci_dev.num_virtual_functions; i++) { + virBufferAsprintf(buf, + "<address domain='0x%.4x' bus='0x%.2x' " + "slot='0x%.2x' function='0x%.1x'/>\n", + data->pci_dev.virtual_functions[i]->domain, + data->pci_dev.virtual_functions[i]->bus, + data->pci_dev.virtual_functions[i]->slot, + data->pci_dev.virtual_functions[i]->function); + } + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</capability>\n"); + } + } + if (data->pci_dev.hdrType) { + virBufferAsprintf(buf, "<capability type='%s'/>\n", + virPCIHeaderTypeToString(data->pci_dev.hdrType)); + } + if (data->pci_dev.nIommuGroupDevices) { + virBufferAsprintf(buf, "<iommuGroup number='%d'>\n", + data->pci_dev.iommuGroupNumber); + virBufferAdjustIndent(buf, 2); + for (i = 0; i < data->pci_dev.nIommuGroupDevices; i++) { + virBufferAsprintf(buf, + "<address domain='0x%.4x' bus='0x%.2x' " + "slot='0x%.2x' function='0x%.1x'/>\n", + data->pci_dev.iommuGroupDevices[i]->domain, + data->pci_dev.iommuGroupDevices[i]->bus, + data->pci_dev.iommuGroupDevices[i]->slot, + data->pci_dev.iommuGroupDevices[i]->function); + } + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</iommuGroup>\n"); + } + if (data->pci_dev.numa_node >= 0) + virBufferAsprintf(buf, "<numa node='%d'/>\n", + data->pci_dev.numa_node); + + if (data->pci_dev.flags & VIR_NODE_DEV_CAP_FLAG_PCIE) + virPCIEDeviceInfoFormat(buf, data->pci_dev.pci_express); +} + + +static void +virNodeDeviceCapUSBDevDefFormat(virBufferPtr buf, + const virNodeDevCapData *data) +{ + virBufferAsprintf(buf, "<bus>%d</bus>\n", data->usb_dev.bus); + virBufferAsprintf(buf, "<device>%d</device>\n", + data->usb_dev.device); + virBufferAsprintf(buf, "<product id='0x%04x'", + data->usb_dev.product); + if (data->usb_dev.product_name) + virBufferEscapeString(buf, ">%s</product>\n", + data->usb_dev.product_name); + else + virBufferAddLit(buf, " />\n"); + virBufferAsprintf(buf, "<vendor id='0x%04x'", + data->usb_dev.vendor); + if (data->usb_dev.vendor_name) + virBufferEscapeString(buf, ">%s</vendor>\n", + data->usb_dev.vendor_name); + else + virBufferAddLit(buf, " />\n"); +} + + +static void +virNodeDeviceCapUSBInterfaceDefFormat(virBufferPtr buf, + const virNodeDevCapData *data) +{ + virBufferAsprintf(buf, "<number>%d</number>\n", + data->usb_if.number); + virBufferAsprintf(buf, "<class>%d</class>\n", + data->usb_if._class); + virBufferAsprintf(buf, "<subclass>%d</subclass>\n", + data->usb_if.subclass); + virBufferAsprintf(buf, "<protocol>%d</protocol>\n", + data->usb_if.protocol); + if (data->usb_if.description) + virBufferEscapeString(buf, + "<description>%s</description>\n", + data->usb_if.description); +} + + +static void +virNodeDeviceCapNetDefFormat(virBufferPtr buf, + const virNodeDevCapData *data) +{ + size_t i; + + virBufferEscapeString(buf, "<interface>%s</interface>\n", + data->net.ifname); + if (data->net.address) + virBufferEscapeString(buf, "<address>%s</address>\n", + data->net.address); + virInterfaceLinkFormat(buf, &data->net.lnk); + if (data->net.features) { + for (i = 0; i < VIR_NET_DEV_FEAT_LAST; i++) { + if (virBitmapIsBitSet(data->net.features, i)) { + virBufferAsprintf(buf, "<feature name='%s'/>\n", + virNetDevFeatureTypeToString(i)); + } + } + } + if (data->net.subtype != VIR_NODE_DEV_CAP_NET_LAST) { + const char *subtyp = + virNodeDevNetCapTypeToString(data->net.subtype); + virBufferEscapeString(buf, "<capability type='%s'/>\n", + subtyp); + } +} + + +static void +virNodeDeviceCapSCSIHostDefFormat(virBufferPtr buf, + const virNodeDevCapData *data) +{ + virBufferAsprintf(buf, "<host>%d</host>\n", + data->scsi_host.host); + if (data->scsi_host.unique_id != -1) + virBufferAsprintf(buf, "<unique_id>%d</unique_id>\n", + data->scsi_host.unique_id); + if (data->scsi_host.flags & VIR_NODE_DEV_CAP_FLAG_HBA_FC_HOST) { + virBufferAddLit(buf, "<capability type='fc_host'>\n"); + virBufferAdjustIndent(buf, 2); + virBufferEscapeString(buf, "<wwnn>%s</wwnn>\n", + data->scsi_host.wwnn); + virBufferEscapeString(buf, "<wwpn>%s</wwpn>\n", + data->scsi_host.wwpn); + virBufferEscapeString(buf, "<fabric_wwn>%s</fabric_wwn>\n", + data->scsi_host.fabric_wwn); + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</capability>\n"); + } + if (data->scsi_host.flags & VIR_NODE_DEV_CAP_FLAG_HBA_VPORT_OPS) { + virBufferAddLit(buf, "<capability type='vport_ops'>\n"); + virBufferAdjustIndent(buf, 2); + virBufferAsprintf(buf, "<max_vports>%d</max_vports>\n", + data->scsi_host.max_vports); + virBufferAsprintf(buf, "<vports>%d</vports>\n", + data->scsi_host.vports); + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</capability>\n"); + } +} + + +static void +virNodeDeviceCapSCSIDefFormat(virBufferPtr buf, + const virNodeDevCapData *data) +{ + virBufferAsprintf(buf, "<host>%d</host>\n", data->scsi.host); + virBufferAsprintf(buf, "<bus>%d</bus>\n", data->scsi.bus); + virBufferAsprintf(buf, "<target>%d</target>\n", + data->scsi.target); + virBufferAsprintf(buf, "<lun>%d</lun>\n", data->scsi.lun); + if (data->scsi.type) + virBufferEscapeString(buf, "<type>%s</type>\n", + data->scsi.type); +} + + +static void +virNodeDeviceCapStorageDefFormat(virBufferPtr buf, + const virNodeDevCapData *data) +{ + virBufferEscapeString(buf, "<block>%s</block>\n", + data->storage.block); + if (data->storage.bus) + virBufferEscapeString(buf, "<bus>%s</bus>\n", + data->storage.bus); + if (data->storage.drive_type) + virBufferEscapeString(buf, "<drive_type>%s</drive_type>\n", + data->storage.drive_type); + if (data->storage.model) + virBufferEscapeString(buf, "<model>%s</model>\n", + data->storage.model); + if (data->storage.vendor) + virBufferEscapeString(buf, "<vendor>%s</vendor>\n", + data->storage.vendor); + if (data->storage.serial) + virBufferEscapeString(buf, "<serial>%s</serial>\n", + data->storage.serial); + if (data->storage.flags & VIR_NODE_DEV_CAP_STORAGE_REMOVABLE) { + int avl = data->storage.flags & + VIR_NODE_DEV_CAP_STORAGE_REMOVABLE_MEDIA_AVAILABLE; + virBufferAddLit(buf, "<capability type='removable'>\n"); + virBufferAdjustIndent(buf, 2); + virBufferAsprintf(buf, "<media_available>%d" + "</media_available>\n", avl ? 1 : 0); + virBufferAsprintf(buf, "<media_size>%llu</media_size>\n", + data->storage.removable_media_size); + if (data->storage.media_label) + virBufferEscapeString(buf, + "<media_label>%s</media_label>\n", + data->storage.media_label); + if (data->storage.logical_block_size > 0) + virBufferAsprintf(buf, "<logical_block_size>%llu" + "</logical_block_size>\n", + data->storage.logical_block_size); + if (data->storage.num_blocks > 0) + virBufferAsprintf(buf, + "<num_blocks>%llu</num_blocks>\n", + data->storage.num_blocks); + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</capability>\n"); + } else { + virBufferAsprintf(buf, "<size>%llu</size>\n", + data->storage.size); + if (data->storage.logical_block_size > 0) + virBufferAsprintf(buf, "<logical_block_size>%llu" + "</logical_block_size>\n", + data->storage.logical_block_size); + if (data->storage.num_blocks > 0) + virBufferAsprintf(buf, "<num_blocks>%llu</num_blocks>\n", + data->storage.num_blocks); + } + if (data->storage.flags & VIR_NODE_DEV_CAP_STORAGE_HOTPLUGGABLE) + virBufferAddLit(buf, "<capability type='hotpluggable'/>\n"); +} + + char * virNodeDeviceDefFormat(const virNodeDeviceDef *def) { @@ -185,7 +499,6 @@ virNodeDeviceDefFormat(const virNodeDeviceDef *def) } for (caps = def->caps; caps; caps = caps->next) { - char uuidstr[VIR_UUID_STRING_BUFLEN]; virNodeDevCapDataPtr data = &caps->data; virBufferAsprintf(&buf, "<capability type='%s'>\n", @@ -193,279 +506,32 @@ virNodeDeviceDefFormat(const virNodeDeviceDef *def) virBufferAdjustIndent(&buf, 2); switch (caps->data.type) { case VIR_NODE_DEV_CAP_SYSTEM: - if (data->system.product_name) - virBufferEscapeString(&buf, "<product>%s</product>\n", - data->system.product_name); - virBufferAddLit(&buf, "<hardware>\n"); - virBufferAdjustIndent(&buf, 2); - if (data->system.hardware.vendor_name) - virBufferEscapeString(&buf, "<vendor>%s</vendor>\n", - data->system.hardware.vendor_name); - if (data->system.hardware.version) - virBufferEscapeString(&buf, "<version>%s</version>\n", - data->system.hardware.version); - if (data->system.hardware.serial) - virBufferEscapeString(&buf, "<serial>%s</serial>\n", - data->system.hardware.serial); - virUUIDFormat(data->system.hardware.uuid, uuidstr); - virBufferAsprintf(&buf, "<uuid>%s</uuid>\n", uuidstr); - virBufferAdjustIndent(&buf, -2); - virBufferAddLit(&buf, "</hardware>\n"); - - virBufferAddLit(&buf, "<firmware>\n"); - virBufferAdjustIndent(&buf, 2); - if (data->system.firmware.vendor_name) - virBufferEscapeString(&buf, "<vendor>%s</vendor>\n", - data->system.firmware.vendor_name); - if (data->system.firmware.version) - virBufferEscapeString(&buf, "<version>%s</version>\n", - data->system.firmware.version); - if (data->system.firmware.release_date) - virBufferEscapeString(&buf, "<release_date>%s</release_date>\n", - data->system.firmware.release_date); - virBufferAdjustIndent(&buf, -2); - virBufferAddLit(&buf, "</firmware>\n"); + virNodeDeviceCapSystemDefFormat(&buf, data); break; case VIR_NODE_DEV_CAP_PCI_DEV: - virBufferAsprintf(&buf, "<domain>%d</domain>\n", - data->pci_dev.domain); - virBufferAsprintf(&buf, "<bus>%d</bus>\n", data->pci_dev.bus); - virBufferAsprintf(&buf, "<slot>%d</slot>\n", - data->pci_dev.slot); - virBufferAsprintf(&buf, "<function>%d</function>\n", - data->pci_dev.function); - virBufferAsprintf(&buf, "<product id='0x%04x'", - data->pci_dev.product); - if (data->pci_dev.product_name) - virBufferEscapeString(&buf, ">%s</product>\n", - data->pci_dev.product_name); - else - virBufferAddLit(&buf, " />\n"); - virBufferAsprintf(&buf, "<vendor id='0x%04x'", - data->pci_dev.vendor); - if (data->pci_dev.vendor_name) - virBufferEscapeString(&buf, ">%s</vendor>\n", - data->pci_dev.vendor_name); - else - virBufferAddLit(&buf, " />\n"); - if (data->pci_dev.flags & VIR_NODE_DEV_CAP_FLAG_PCI_PHYSICAL_FUNCTION) { - virBufferAddLit(&buf, "<capability type='phys_function'>\n"); - virBufferAdjustIndent(&buf, 2); - virBufferAsprintf(&buf, - "<address domain='0x%.4x' bus='0x%.2x' " - "slot='0x%.2x' function='0x%.1x'/>\n", - data->pci_dev.physical_function->domain, - data->pci_dev.physical_function->bus, - data->pci_dev.physical_function->slot, - data->pci_dev.physical_function->function); - virBufferAdjustIndent(&buf, -2); - virBufferAddLit(&buf, "</capability>\n"); - } - if (data->pci_dev.flags & VIR_NODE_DEV_CAP_FLAG_PCI_VIRTUAL_FUNCTION) { - virBufferAddLit(&buf, "<capability type='virt_functions'"); - if (data->pci_dev.max_virtual_functions) - virBufferAsprintf(&buf, " maxCount='%u'", - data->pci_dev.max_virtual_functions); - if (data->pci_dev.num_virtual_functions == 0) { - virBufferAddLit(&buf, "/>\n"); - } else { - virBufferAddLit(&buf, ">\n"); - virBufferAdjustIndent(&buf, 2); - for (i = 0; i < data->pci_dev.num_virtual_functions; i++) { - virBufferAsprintf(&buf, - "<address domain='0x%.4x' bus='0x%.2x' " - "slot='0x%.2x' function='0x%.1x'/>\n", - data->pci_dev.virtual_functions[i]->domain, - data->pci_dev.virtual_functions[i]->bus, - data->pci_dev.virtual_functions[i]->slot, - data->pci_dev.virtual_functions[i]->function); - } - virBufferAdjustIndent(&buf, -2); - virBufferAddLit(&buf, "</capability>\n"); - } - } - if (data->pci_dev.hdrType) { - virBufferAsprintf(&buf, "<capability type='%s'/>\n", - virPCIHeaderTypeToString(data->pci_dev.hdrType)); - } - if (data->pci_dev.nIommuGroupDevices) { - virBufferAsprintf(&buf, "<iommuGroup number='%d'>\n", - data->pci_dev.iommuGroupNumber); - virBufferAdjustIndent(&buf, 2); - for (i = 0; i < data->pci_dev.nIommuGroupDevices; i++) { - virBufferAsprintf(&buf, - "<address domain='0x%.4x' bus='0x%.2x' " - "slot='0x%.2x' function='0x%.1x'/>\n", - data->pci_dev.iommuGroupDevices[i]->domain, - data->pci_dev.iommuGroupDevices[i]->bus, - data->pci_dev.iommuGroupDevices[i]->slot, - data->pci_dev.iommuGroupDevices[i]->function); - } - virBufferAdjustIndent(&buf, -2); - virBufferAddLit(&buf, "</iommuGroup>\n"); - } - if (data->pci_dev.numa_node >= 0) - virBufferAsprintf(&buf, "<numa node='%d'/>\n", - data->pci_dev.numa_node); - - if (data->pci_dev.flags & VIR_NODE_DEV_CAP_FLAG_PCIE) - virPCIEDeviceInfoFormat(&buf, data->pci_dev.pci_express); + virNodeDeviceCapPCIDefFormat(&buf, data); break; case VIR_NODE_DEV_CAP_USB_DEV: - virBufferAsprintf(&buf, "<bus>%d</bus>\n", data->usb_dev.bus); - virBufferAsprintf(&buf, "<device>%d</device>\n", - data->usb_dev.device); - virBufferAsprintf(&buf, "<product id='0x%04x'", - data->usb_dev.product); - if (data->usb_dev.product_name) - virBufferEscapeString(&buf, ">%s</product>\n", - data->usb_dev.product_name); - else - virBufferAddLit(&buf, " />\n"); - virBufferAsprintf(&buf, "<vendor id='0x%04x'", - data->usb_dev.vendor); - if (data->usb_dev.vendor_name) - virBufferEscapeString(&buf, ">%s</vendor>\n", - data->usb_dev.vendor_name); - else - virBufferAddLit(&buf, " />\n"); + virNodeDeviceCapUSBDevDefFormat(&buf, data); break; case VIR_NODE_DEV_CAP_USB_INTERFACE: - virBufferAsprintf(&buf, "<number>%d</number>\n", - data->usb_if.number); - virBufferAsprintf(&buf, "<class>%d</class>\n", - data->usb_if._class); - virBufferAsprintf(&buf, "<subclass>%d</subclass>\n", - data->usb_if.subclass); - virBufferAsprintf(&buf, "<protocol>%d</protocol>\n", - data->usb_if.protocol); - if (data->usb_if.description) - virBufferEscapeString(&buf, - "<description>%s</description>\n", - data->usb_if.description); + virNodeDeviceCapUSBInterfaceDefFormat(&buf, data); break; case VIR_NODE_DEV_CAP_NET: - virBufferEscapeString(&buf, "<interface>%s</interface>\n", - data->net.ifname); - if (data->net.address) - virBufferEscapeString(&buf, "<address>%s</address>\n", - data->net.address); - virInterfaceLinkFormat(&buf, &data->net.lnk); - if (data->net.features) { - for (i = 0; i < VIR_NET_DEV_FEAT_LAST; i++) { - if (virBitmapIsBitSet(data->net.features, i)) { - virBufferAsprintf(&buf, "<feature name='%s'/>\n", - virNetDevFeatureTypeToString(i)); - } - } - } - if (data->net.subtype != VIR_NODE_DEV_CAP_NET_LAST) { - const char *subtyp = - virNodeDevNetCapTypeToString(data->net.subtype); - virBufferEscapeString(&buf, "<capability type='%s'/>\n", - subtyp); - } + virNodeDeviceCapNetDefFormat(&buf, data); break; case VIR_NODE_DEV_CAP_SCSI_HOST: - virBufferAsprintf(&buf, "<host>%d</host>\n", - data->scsi_host.host); - if (data->scsi_host.unique_id != -1) - virBufferAsprintf(&buf, "<unique_id>%d</unique_id>\n", - data->scsi_host.unique_id); - if (data->scsi_host.flags & VIR_NODE_DEV_CAP_FLAG_HBA_FC_HOST) { - virBufferAddLit(&buf, "<capability type='fc_host'>\n"); - virBufferAdjustIndent(&buf, 2); - virBufferEscapeString(&buf, "<wwnn>%s</wwnn>\n", - data->scsi_host.wwnn); - virBufferEscapeString(&buf, "<wwpn>%s</wwpn>\n", - data->scsi_host.wwpn); - virBufferEscapeString(&buf, "<fabric_wwn>%s</fabric_wwn>\n", - data->scsi_host.fabric_wwn); - virBufferAdjustIndent(&buf, -2); - virBufferAddLit(&buf, "</capability>\n"); - } - if (data->scsi_host.flags & VIR_NODE_DEV_CAP_FLAG_HBA_VPORT_OPS) { - virBufferAddLit(&buf, "<capability type='vport_ops'>\n"); - virBufferAdjustIndent(&buf, 2); - virBufferAsprintf(&buf, "<max_vports>%d</max_vports>\n", - data->scsi_host.max_vports); - virBufferAsprintf(&buf, "<vports>%d</vports>\n", - data->scsi_host.vports); - virBufferAdjustIndent(&buf, -2); - virBufferAddLit(&buf, "</capability>\n"); - } - + virNodeDeviceCapSCSIHostDefFormat(&buf, data); break; - case VIR_NODE_DEV_CAP_SCSI_TARGET: virBufferEscapeString(&buf, "<target>%s</target>\n", data->scsi_target.name); break; - case VIR_NODE_DEV_CAP_SCSI: - virBufferAsprintf(&buf, "<host>%d</host>\n", data->scsi.host); - virBufferAsprintf(&buf, "<bus>%d</bus>\n", data->scsi.bus); - virBufferAsprintf(&buf, "<target>%d</target>\n", - data->scsi.target); - virBufferAsprintf(&buf, "<lun>%d</lun>\n", data->scsi.lun); - if (data->scsi.type) - virBufferEscapeString(&buf, "<type>%s</type>\n", - data->scsi.type); + virNodeDeviceCapSCSIDefFormat(&buf, data); break; case VIR_NODE_DEV_CAP_STORAGE: - virBufferEscapeString(&buf, "<block>%s</block>\n", - data->storage.block); - if (data->storage.bus) - virBufferEscapeString(&buf, "<bus>%s</bus>\n", - data->storage.bus); - if (data->storage.drive_type) - virBufferEscapeString(&buf, "<drive_type>%s</drive_type>\n", - data->storage.drive_type); - if (data->storage.model) - virBufferEscapeString(&buf, "<model>%s</model>\n", - data->storage.model); - if (data->storage.vendor) - virBufferEscapeString(&buf, "<vendor>%s</vendor>\n", - data->storage.vendor); - if (data->storage.serial) - virBufferEscapeString(&buf, "<serial>%s</serial>\n", - data->storage.serial); - if (data->storage.flags & VIR_NODE_DEV_CAP_STORAGE_REMOVABLE) { - int avl = data->storage.flags & - VIR_NODE_DEV_CAP_STORAGE_REMOVABLE_MEDIA_AVAILABLE; - virBufferAddLit(&buf, "<capability type='removable'>\n"); - virBufferAdjustIndent(&buf, 2); - virBufferAsprintf(&buf, "<media_available>%d" - "</media_available>\n", avl ? 1 : 0); - virBufferAsprintf(&buf, "<media_size>%llu</media_size>\n", - data->storage.removable_media_size); - if (data->storage.media_label) - virBufferEscapeString(&buf, - "<media_label>%s</media_label>\n", - data->storage.media_label); - if (data->storage.logical_block_size > 0) - virBufferAsprintf(&buf, "<logical_block_size>%llu" - "</logical_block_size>\n", - data->storage.logical_block_size); - if (data->storage.num_blocks > 0) - virBufferAsprintf(&buf, - "<num_blocks>%llu</num_blocks>\n", - data->storage.num_blocks); - virBufferAdjustIndent(&buf, -2); - virBufferAddLit(&buf, "</capability>\n"); - } else { - virBufferAsprintf(&buf, "<size>%llu</size>\n", - data->storage.size); - if (data->storage.logical_block_size > 0) - virBufferAsprintf(&buf, "<logical_block_size>%llu" - "</logical_block_size>\n", - data->storage.logical_block_size); - if (data->storage.num_blocks > 0) - virBufferAsprintf(&buf, "<num_blocks>%llu</num_blocks>\n", - data->storage.num_blocks); - } - if (data->storage.flags & VIR_NODE_DEV_CAP_STORAGE_HOTPLUGGABLE) - virBufferAddLit(&buf, "<capability type='hotpluggable'/>\n"); + virNodeDeviceCapStorageDefFormat(&buf, data); break; case VIR_NODE_DEV_CAP_SCSI_GENERIC: virBufferEscapeString(&buf, "<char>%s</char>\n", -- 2.12.2

On Thu, Apr 20, 2017 at 03:05:52PM +0200, Erik Skultety wrote:
Make the code look cleaner by moving the capability specific bits into separate functions.
Signed-off-by: Erik Skultety <eskultet@redhat.com> --- src/conf/node_device_conf.c | 578 ++++++++++++++++++++++++-------------------- 1 file changed, 322 insertions(+), 256 deletions(-)
ACK Pavel

Since we have that information provided by @def which is not a private object, there is really no need for the variable. Signed-off-by: Erik Skultety <eskultet@redhat.com> --- src/node_device/node_device_udev.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/node_device/node_device_udev.c b/src/node_device/node_device_udev.c index 591da8db2..6e706a10b 100644 --- a/src/node_device/node_device_udev.c +++ b/src/node_device/node_device_udev.c @@ -316,7 +316,6 @@ static int udevTranslatePCIIds(unsigned int vendor, static int udevProcessPCI(struct udev_device *device, virNodeDeviceDefPtr def) { - const char *syspath = NULL; virNodeDevCapPCIDevPtr pci_dev = &def->caps->data.pci_dev; virPCIEDeviceInfoPtr pci_express = NULL; virPCIDevicePtr pciDev = NULL; @@ -324,19 +323,17 @@ static int udevProcessPCI(struct udev_device *device, int ret = -1; char *p; - syspath = udev_device_get_syspath(device); - if (udevGetUintProperty(device, "PCI_CLASS", &pci_dev->class, 16) < 0) goto cleanup; - if ((p = strrchr(syspath, '/')) == NULL || + if ((p = strrchr(def->sysfs_path, '/')) == NULL || virStrToLong_ui(p + 1, &p, 16, &pci_dev->domain) < 0 || p == NULL || virStrToLong_ui(p + 1, &p, 16, &pci_dev->bus) < 0 || p == NULL || virStrToLong_ui(p + 1, &p, 16, &pci_dev->slot) < 0 || p == NULL || virStrToLong_ui(p + 1, &p, 16, &pci_dev->function) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("failed to parse the PCI address from sysfs path: '%s'"), - syspath); + def->sysfs_path); goto cleanup; } @@ -363,8 +360,7 @@ static int udevProcessPCI(struct udev_device *device, &pci_dev->numa_node, 10) < 0) goto cleanup; - if (nodeDeviceSysfsGetPCIRelatedDevCaps(syspath, - &def->caps->data.pci_dev) < 0) + if (nodeDeviceSysfsGetPCIRelatedDevCaps(def->sysfs_path, pci_dev) < 0) goto cleanup; if (!(pciDev = virPCIDeviceNew(pci_dev->domain, -- 2.12.2

On Thu, Apr 20, 2017 at 03:05:53PM +0200, Erik Skultety wrote:
Since we have that information provided by @def which is not a private object, there is really no need for the variable.
Signed-off-by: Erik Skultety <eskultet@redhat.com> --- src/node_device/node_device_udev.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-)
ACK Pavel

Since we do have this template at hand, why not using it wherever possible (list of supported pool types and remote access section). Also, perform some stylistic micro adjustments. Signed-off-by: Erik Skultety <eskultet@redhat.com> --- docs/remote.html.in | 106 +++++++++++++++------------------------------------ docs/storage.html.in | 62 ++++++------------------------ 2 files changed, 41 insertions(+), 127 deletions(-) diff --git a/docs/remote.html.in b/docs/remote.html.in index 443683d51..117ee3477 100644 --- a/docs/remote.html.in +++ b/docs/remote.html.in @@ -7,57 +7,11 @@ Libvirt allows you to access hypervisors running on remote machines through authenticated and encrypted connections. </p> - <ul> - <li> - <a href="#Remote_basic_usage">Basic usage</a> - </li> - <li> - <a href="#Remote_transports">Transports</a> - </li> - <li> - <a href="#Remote_URI_reference">Remote URIs</a> - <ul> - <li> - <a href="#Remote_URI_parameters">Extra parameters</a> - </li> - </ul> - </li> - <li> - <a href="#Remote_certificates">Generating TLS certificates</a> - <ul> - <li> - <a href="#Remote_PKI">Public Key Infrastructure set up</a> - </li> - <li> - <a href="#Remote_TLS_background">Background to TLS certificates</a> - </li> - <li> - <a href="#Remote_TLS_CA">Setting up a Certificate Authority (CA)</a> - </li> - <li> - <a href="#Remote_TLS_server_certificates">Issuing server certificates</a> - </li> - <li> - <a href="#Remote_TLS_client_certificates">Issuing client certificates</a> - </li> - <li> - <a href="#Remote_TLS_troubleshooting">Troubleshooting TLS certificate problems</a> - </li> - </ul> - </li> - <li> - <a href="#Remote_libvirtd_configuration">libvirtd configuration file</a> - </li> - <li> - <a href="#Remote_IPv6">IPv6 support</a> - </li> - <li> - <a href="#Remote_limitations">Limitations</a> - </li> - </ul> - <h3> + <ul id="toc"></ul> + + <h2> <a name="Remote_basic_usage">Basic usage</a> - </h3> + </h2> <p> On the remote machine, <code>libvirtd</code> should be running in general. See <a href="#Remote_libvirtd_configuration">the section @@ -95,9 +49,9 @@ relating to failures in the remote transport itself. </li> <li> Remote calls are handled synchronously, so they will be much slower than, say, direct hypervisor calls. </li> </ul> - <h3> + <h2> <a name="Remote_transports">Transports</a> - </h3> + </h2> <p> Remote libvirt supports a range of transports: </p> @@ -156,9 +110,9 @@ netcat is required on the remote side.</dd> <p> The default transport, if no other is specified, is <code>tls</code>. </p> - <h3> + <h2> <a name="Remote_URI_reference">Remote URIs</a> - </h3> + </h2> <p> See also: <a href="uri.html">documentation on ordinary ("local") URIs</a>. </p> @@ -203,9 +157,9 @@ and use a different known_hosts file.</li> Connect to a remote host using a ssh connection with the libssh driver and use a different known_hosts file.</li> </ul> - <h4> + <h3> <a name="Remote_URI_parameters">Extra parameters</a> - </h4> + </h3> <p> Extra parameters can be added to remote URIs as part of the query string (the part following <q><code>?</code></q>). @@ -409,12 +363,12 @@ Note that parameter values must be <td> Example: <code>sshauth=privkey,agent</code> </td> </tr> </table> - <h3> + <h2> <a name="Remote_certificates">Generating TLS certificates</a> - </h3> - <h4> + </h2> + <h3> <a name="Remote_PKI">Public Key Infrastructure set up</a> - </h4> + </h3> <p> If you are unsure how to create TLS certificates, skip to the next section. @@ -517,9 +471,9 @@ next section. </li> <li> For the root user, the global default locations will always be used.</li> </ul> - <h4> + <h3> <a name="Remote_TLS_background">Background to TLS certificates</a> - </h4> + </h3> <p> Libvirt supports TLS certificates for verifying the identity of the server and clients. There are two distinct checks involved: @@ -552,9 +506,9 @@ they have a valid certificate issued by the CA for their own IP address. You may want to change this to make it less (or more) permissive, depending on your needs. </p> - <h4> + <h3> <a name="Remote_TLS_CA">Setting up a Certificate Authority (CA)</a> - </h4> + </h3> <p> You will need the <a href="http://www.gnu.org/software/gnutls/manual/html_node/Invoking-certtool.html">GnuTLS certtool program documented here</a>. In Fedora, it is in the @@ -623,9 +577,9 @@ This is all that is required to set up your CA. Keep the CA's private key carefully as you will need it when you come to issue certificates for your clients and servers. </p> - <h4> + <h3> <a name="Remote_TLS_server_certificates">Issuing server certificates</a> - </h4> + </h3> <p> For each server (libvirtd) you need to issue a certificate with the X.509 CommonName (CN) field set to the hostname @@ -706,9 +660,9 @@ which can be installed on the server as <code>/etc/pki/libvirt/servercert.pem</code>. </li> </ul> - <h4> + <h3> <a name="Remote_TLS_client_certificates">Issuing client certificates</a> - </h4> + </h3> <p> For each client (ie. any program linked with libvirt, such as <a href="http://virt-manager.et.redhat.com/">virt-manager</a>) @@ -759,9 +713,9 @@ cp clientcert.pem /etc/pki/libvirt/clientcert.pem </pre> </li> </ol> - <h4> + <h3> <a name="Remote_TLS_troubleshooting">Troubleshooting TLS certificate problems</a> - </h4> + </h3> <dl> <dt> failed to verify client's certificate </dt> <dd> @@ -777,9 +731,9 @@ tell you enough to diagnose the problem. to analyze the setup on the client or server machines, preferably as root. It will try to point out the possible problems and provide solutions to fix the set up up to a point where you have secure remote access.</p> - <h3> + <h2> <a name="Remote_libvirtd_configuration">libvirtd configuration file</a> - </h3> + </h2> <p> Libvirtd (the remote daemon) is configured from a file called <code>/etc/libvirt/libvirtd.conf</code>, or specified on @@ -945,9 +899,9 @@ Blank lines and comments beginning with <code>#</code> are ignored. </td> </tr> </table> - <h3> + <h2> <a name="Remote_IPv6">IPv6 support</a> - </h3> + </h2> <p> The libvirtd service and libvirt remote client driver both use the <code>getaddrinfo()</code> functions for name resolution and are @@ -958,9 +912,9 @@ address resolved for a service is reachable over IPv6, then an IPv6 connection will be made, otherwise IPv4 will be used. In summary it should just 'do the right thing(tm)'. </p> - <h3> + <h2> <a name="Remote_limitations">Limitations</a> - </h3> + </h2> <ul> <li> Fine-grained authentication: libvirt in general, but in particular the remote case should support more diff --git a/docs/storage.html.in b/docs/storage.html.in index 5e18f02c5..89ebb7097 100644 --- a/docs/storage.html.in +++ b/docs/storage.html.in @@ -83,47 +83,7 @@ <p> Libvirt supports the following storage pool types: </p> - <ul> - <li> - <a href="#StorageBackendDir">Directory backend</a> - </li> - <li> - <a href="#StorageBackendFS">Local filesystem backend</a> - </li> - <li> - <a href="#StorageBackendNetFS">Network filesystem backend</a> - </li> - <li> - <a href="#StorageBackendLogical">Logical backend</a> - </li> - <li> - <a href="#StorageBackendDisk">Disk backend</a> - </li> - <li> - <a href="#StorageBackendISCSI">iSCSI backend</a> - </li> - <li> - <a href="#StorageBackendSCSI">SCSI backend</a> - </li> - <li> - <a href="#StorageBackendMultipath">Multipath backend</a> - </li> - <li> - <a href="#StorageBackendRBD">RBD (RADOS Block Device) backend</a> - </li> - <li> - <a href="#StorageBackendSheepdog">Sheepdog backend</a> - </li> - <li> - <a href="#StorageBackendGluster">Gluster backend</a> - </li> - <li> - <a href="#StorageBackendZFS">ZFS backend</a> - </li> - <li> - <a href="#StorageBackendVstorage">Virtuozzo storage backend</a> - </li> - </ul> + <ul id="toc"></ul> <h2><a name="StorageBackendDir">Directory pool</a></h2> <p> @@ -306,7 +266,7 @@ </p> - <h2><a name="StorageBackendLogical">Logical volume pools</a></h2> + <h2><a name="StorageBackendLogical">Logical volume pool</a></h2> <p> This provides a pool based on an LVM volume group. For a pre-defined LVM volume group, simply providing the group @@ -343,7 +303,7 @@ </p> - <h2><a name="StorageBackendDisk">Disk volume pools</a></h2> + <h2><a name="StorageBackendDisk">Disk pool</a></h2> <p> This provides a pool based on a physical disk. Volumes are created by adding partitions to the disk. Disk pools have constraints @@ -434,7 +394,7 @@ </ul> - <h2><a name="StorageBackendISCSI">iSCSI volume pools</a></h2> + <h2><a name="StorageBackendISCSI">iSCSI pool</a></h2> <p> This provides a pool based on an iSCSI target. Volumes must be pre-allocated on the iSCSI server, and cannot be created via @@ -473,7 +433,7 @@ The iSCSI volume pool does not use the volume format type element. </p> - <h2><a name="StorageBackendSCSI">SCSI volume pools</a></h2> + <h2><a name="StorageBackendSCSI">SCSI pool</a></h2> <p> This provides a pool based on a SCSI HBA. Volumes are preexisting SCSI LUNs, and cannot be created via the libvirt APIs. Since /dev/XXX names @@ -505,7 +465,7 @@ The SCSI volume pool does not use the volume format type element. </p> - <h2><a name="StorageBackendMultipath">Multipath pools</a></h2> + <h2><a name="StorageBackendMultipath">Multipath pool</a></h2> <p> This provides a pool that contains all the multipath devices on the host. Therefore, only one Multipath pool may be configured per host. @@ -538,7 +498,7 @@ The Multipath volume pool does not use the volume format type element. </p> - <h2><a name="StorageBackendRBD">RBD pools</a></h2> + <h2><a name="StorageBackendRBD">RBD pool</a></h2> <p> This storage driver provides a pool which contains all RBD images in a RADOS pool. RBD (RADOS Block Device) is part @@ -611,7 +571,7 @@ The RBD pool does not use the volume format type element. </p> - <h2><a name="StorageBackendSheepdog">Sheepdog pools</a></h2> + <h2><a name="StorageBackendSheepdog">Sheepdog pool</a></h2> <p> This provides a pool based on a Sheepdog Cluster. Sheepdog is a distributed storage system for QEMU/KVM. @@ -670,7 +630,7 @@ The Sheepdog pool does not use the volume format type element. </p> - <h2><a name="StorageBackendGluster">Gluster pools</a></h2> + <h2><a name="StorageBackendGluster">Gluster pool</a></h2> <p> This provides a pool based on native Gluster access. Gluster is a distributed file system that can be exposed to the user via @@ -756,7 +716,7 @@ pool type. </p> - <h2><a name="StorageBackendZFS">ZFS pools</a></h2> + <h2><a name="StorageBackendZFS">ZFS pool</a></h2> <p> This provides a pool based on the ZFS filesystem. Initially it was developed for FreeBSD, and <span class="since">since 1.3.2</span> experimental support @@ -794,7 +754,7 @@ <p> The ZFS volume pool does not use the volume format type element. </p> - <h2><a name="StorageBackendVstorage">Vstorage pools</a></h2> + <h2><a name="StorageBackendVstorage">Vstorage pool</a></h2> <p> This provides a pool based on Virtuozzo storage. Virtuozzo Storage is a highly available distributed software-defined storage with built-in -- 2.12.2

On Thu, Apr 20, 2017 at 03:05:54PM +0200, Erik Skultety wrote:
Since we do have this template at hand, why not using it wherever possible (list of supported pool types and remote access section). Also, perform some stylistic micro adjustments.
Signed-off-by: Erik Skultety <eskultet@redhat.com> --- docs/remote.html.in | 106 +++++++++++++++------------------------------------ docs/storage.html.in | 62 ++++++------------------------ 2 files changed, 41 insertions(+), 127 deletions(-)
ACK Pavel

On Fri, Apr 21, 2017 at 02:51:50PM +0200, Pavel Hrdina wrote:
On Thu, Apr 20, 2017 at 03:05:54PM +0200, Erik Skultety wrote:
Since we do have this template at hand, why not using it wherever possible (list of supported pool types and remote access section). Also, perform some stylistic micro adjustments.
Signed-off-by: Erik Skultety <eskultet@redhat.com> --- docs/remote.html.in | 106 +++++++++++++++------------------------------------ docs/storage.html.in | 62 ++++++------------------------ 2 files changed, 41 insertions(+), 127 deletions(-)
ACK
Pavel
I went ahead and pushed patches 1-4, as those are just cleanups. Thanks, Erik

Since there's at least SRIOV and MDEV sub-capabilities to be parsed, let's make the code more readable by splitting it to several logical blocks. Signed-off-by: Erik Skultety <eskultet@redhat.com> --- src/conf/node_device_conf.c | 84 ++++++++++++++++++++++++++++----------------- 1 file changed, 53 insertions(+), 31 deletions(-) diff --git a/src/conf/node_device_conf.c b/src/conf/node_device_conf.c index 85cfd8396..de346597a 100644 --- a/src/conf/node_device_conf.c +++ b/src/conf/node_device_conf.c @@ -1286,76 +1286,100 @@ virPCIEDeviceInfoParseXML(xmlXPathContextPtr ctxt, static int -virNodeDevPCICapabilityParseXML(xmlXPathContextPtr ctxt, - xmlNodePtr node, - virNodeDevCapPCIDevPtr pci_dev) +virNodeDevPCICapSRIOVParseXML(xmlXPathContextPtr ctxt, + xmlNodePtr node, + virNodeDevCapPCIDevPtr pci_dev, + unsigned int cap) { + int ret = -1; char *maxFuncsStr = virXMLPropString(node, "maxCount"); - char *type = virXMLPropString(node, "type"); xmlNodePtr *addresses = NULL; - xmlNodePtr orignode = ctxt->node; - int ret = -1; - size_t i = 0; - ctxt->node = node; - - if (!type) { - virReportError(VIR_ERR_XML_ERROR, "%s", _("Missing capability type")); - goto out; - } - - if (STREQ(type, "phys_function")) { + if (cap == VIR_NODE_DEV_CAP_FLAG_PCI_PHYSICAL_FUNCTION) { xmlNodePtr address = virXPathNode("./address[1]", ctxt); if (VIR_ALLOC(pci_dev->physical_function) < 0) - goto out; + goto cleanup; if (!address) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Missing address in 'phys_function' capability")); - goto out; + goto cleanup; } if (virPCIDeviceAddressParseXML(address, pci_dev->physical_function) < 0) - goto out; - - pci_dev->flags |= VIR_NODE_DEV_CAP_FLAG_PCI_PHYSICAL_FUNCTION; - } else if (STREQ(type, "virt_functions")) { + goto cleanup; + } else { + size_t i; int naddresses; if ((naddresses = virXPathNodeSet("./address", ctxt, &addresses)) < 0) - goto out; + goto cleanup; if (maxFuncsStr && virStrToLong_uip(maxFuncsStr, NULL, 10, &pci_dev->max_virtual_functions) < 0) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Malformed 'maxCount' parameter")); - goto out; + goto cleanup; } if (VIR_ALLOC_N(pci_dev->virtual_functions, naddresses) < 0) - goto out; + goto cleanup; for (i = 0; i < naddresses; i++) { virPCIDeviceAddressPtr addr = NULL; if (VIR_ALLOC(addr) < 0) - goto out; + goto cleanup; if (virPCIDeviceAddressParseXML(addresses[i], addr) < 0) { VIR_FREE(addr); - goto out; + goto cleanup; } if (VIR_APPEND_ELEMENT(pci_dev->virtual_functions, pci_dev->num_virtual_functions, addr) < 0) - goto out; + goto cleanup; } + } - pci_dev->flags |= VIR_NODE_DEV_CAP_FLAG_PCI_VIRTUAL_FUNCTION; + pci_dev->flags |= cap; + ret = 0; + cleanup: + VIR_FREE(addresses); + VIR_FREE(maxFuncsStr); + return ret; +} + + +static int +virNodeDevPCICapabilityParseXML(xmlXPathContextPtr ctxt, + xmlNodePtr node, + virNodeDevCapPCIDevPtr pci_dev) +{ + char *type = virXMLPropString(node, "type"); + xmlNodePtr orignode = ctxt->node; + unsigned int sriov_cap = 0; + int ret = -1; + + ctxt->node = node; + + if (!type) { + virReportError(VIR_ERR_XML_ERROR, "%s", _("Missing capability type")); + goto cleanup; + } + + if (STREQ(type, "phys_function")) + sriov_cap = VIR_NODE_DEV_CAP_FLAG_PCI_PHYSICAL_FUNCTION; + else if (STREQ(type, "virt_functions")) + sriov_cap = VIR_NODE_DEV_CAP_FLAG_PCI_VIRTUAL_FUNCTION; + + if (sriov_cap && + virNodeDevPCICapSRIOVParseXML(ctxt, node, pci_dev, sriov_cap) < 0) { + goto cleanup; } else { int hdrType = virPCIHeaderTypeFromString(type); @@ -1364,9 +1388,7 @@ virNodeDevPCICapabilityParseXML(xmlXPathContextPtr ctxt, } ret = 0; - out: - VIR_FREE(addresses); - VIR_FREE(maxFuncsStr); + cleanup: VIR_FREE(type); ctxt->node = orignode; return ret; -- 2.12.2

On Thu, Apr 20, 2017 at 03:05:55PM +0200, Erik Skultety wrote:
Since there's at least SRIOV and MDEV sub-capabilities to be parsed, let's make the code more readable by splitting it to several logical blocks.
Signed-off-by: Erik Skultety <eskultet@redhat.com> --- src/conf/node_device_conf.c | 84 ++++++++++++++++++++++++++++----------------- 1 file changed, 53 insertions(+), 31 deletions(-)
diff --git a/src/conf/node_device_conf.c b/src/conf/node_device_conf.c index 85cfd8396..de346597a 100644 --- a/src/conf/node_device_conf.c +++ b/src/conf/node_device_conf.c @@ -1286,76 +1286,100 @@ virPCIEDeviceInfoParseXML(xmlXPathContextPtr ctxt,
static int -virNodeDevPCICapabilityParseXML(xmlXPathContextPtr ctxt, - xmlNodePtr node, - virNodeDevCapPCIDevPtr pci_dev) +virNodeDevPCICapSRIOVParseXML(xmlXPathContextPtr ctxt, + xmlNodePtr node, + virNodeDevCapPCIDevPtr pci_dev, + unsigned int cap) {
You can go even further and split this function into two separate functions for "physical function" and "virtual function".
+ int ret = -1; char *maxFuncsStr = virXMLPropString(node, "maxCount"); - char *type = virXMLPropString(node, "type"); xmlNodePtr *addresses = NULL; - xmlNodePtr orignode = ctxt->node; - int ret = -1; - size_t i = 0;
- ctxt->node = node; - - if (!type) { - virReportError(VIR_ERR_XML_ERROR, "%s", _("Missing capability type")); - goto out; - } - - if (STREQ(type, "phys_function")) { + if (cap == VIR_NODE_DEV_CAP_FLAG_PCI_PHYSICAL_FUNCTION) { xmlNodePtr address = virXPathNode("./address[1]", ctxt);
if (VIR_ALLOC(pci_dev->physical_function) < 0) - goto out; + goto cleanup;
if (!address) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Missing address in 'phys_function' capability")); - goto out; + goto cleanup; }
if (virPCIDeviceAddressParseXML(address, pci_dev->physical_function) < 0) - goto out; - - pci_dev->flags |= VIR_NODE_DEV_CAP_FLAG_PCI_PHYSICAL_FUNCTION; - } else if (STREQ(type, "virt_functions")) { + goto cleanup; + } else { + size_t i; int naddresses;
if ((naddresses = virXPathNodeSet("./address", ctxt, &addresses)) < 0) - goto out; + goto cleanup;
if (maxFuncsStr && virStrToLong_uip(maxFuncsStr, NULL, 10, &pci_dev->max_virtual_functions) < 0) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Malformed 'maxCount' parameter")); - goto out; + goto cleanup; }
if (VIR_ALLOC_N(pci_dev->virtual_functions, naddresses) < 0) - goto out; + goto cleanup;
for (i = 0; i < naddresses; i++) { virPCIDeviceAddressPtr addr = NULL;
if (VIR_ALLOC(addr) < 0) - goto out; + goto cleanup;
if (virPCIDeviceAddressParseXML(addresses[i], addr) < 0) { VIR_FREE(addr); - goto out; + goto cleanup; }
if (VIR_APPEND_ELEMENT(pci_dev->virtual_functions, pci_dev->num_virtual_functions, addr) < 0) - goto out; + goto cleanup; } + }
- pci_dev->flags |= VIR_NODE_DEV_CAP_FLAG_PCI_VIRTUAL_FUNCTION; + pci_dev->flags |= cap; + ret = 0; + cleanup: + VIR_FREE(addresses); + VIR_FREE(maxFuncsStr); + return ret; +} + + +static int +virNodeDevPCICapabilityParseXML(xmlXPathContextPtr ctxt, + xmlNodePtr node, + virNodeDevCapPCIDevPtr pci_dev) +{ + char *type = virXMLPropString(node, "type"); + xmlNodePtr orignode = ctxt->node; + unsigned int sriov_cap = 0; + int ret = -1; + + ctxt->node = node; + + if (!type) { + virReportError(VIR_ERR_XML_ERROR, "%s", _("Missing capability type")); + goto cleanup; + } + + if (STREQ(type, "phys_function")) + sriov_cap = VIR_NODE_DEV_CAP_FLAG_PCI_PHYSICAL_FUNCTION; + else if (STREQ(type, "virt_functions")) + sriov_cap = VIR_NODE_DEV_CAP_FLAG_PCI_VIRTUAL_FUNCTION; + + if (sriov_cap && + virNodeDevPCICapSRIOVParseXML(ctxt, node, pci_dev, sriov_cap) < 0) { + goto cleanup;
If you drop the sriov_cap the code can be cleaner by simply passing the VIR_NODE_DEV_CAP_FLAG_PCI_* flag directly as parameter and if you decide to split the virNodeDevPCICapSRIOVParseXML() you will not need to pass it at all.) Pavel
} else { int hdrType = virPCIHeaderTypeFromString(type);
@@ -1364,9 +1388,7 @@ virNodeDevPCICapabilityParseXML(xmlXPathContextPtr ctxt, }
ret = 0; - out: - VIR_FREE(addresses); - VIR_FREE(maxFuncsStr); + cleanup: VIR_FREE(type); ctxt->node = orignode; return ret; -- 2.12.2
-- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list

Besides updating the capability enum, this patch introduces 'virNodeDevCapMdev' structure which will store everything libvirt can gather from sysfs about a mediated device. Since we need to report the info for both the mediated child device and its parent mdev capabilities (merely the mdev types' attributes), this structure serves in both of these cases with the difference being that the amount of data it holds depends on the specific scenario (child vs parent). Signed-off-by: Erik Skultety <eskultet@redhat.com> --- include/libvirt/libvirt-nodedev.h | 1 + src/conf/node_device_conf.c | 6 +++++- src/conf/node_device_conf.h | 4 +++- src/conf/virnodedeviceobj.c | 3 ++- src/libvirt-nodedev.c | 1 + src/node_device/node_device_driver.c | 1 + src/node_device/node_device_udev.c | 8 +++++++- tools/virsh-nodedev.c | 2 ++ 8 files changed, 22 insertions(+), 4 deletions(-) diff --git a/include/libvirt/libvirt-nodedev.h b/include/libvirt/libvirt-nodedev.h index 85003903d..59edf4db0 100644 --- a/include/libvirt/libvirt-nodedev.h +++ b/include/libvirt/libvirt-nodedev.h @@ -79,6 +79,7 @@ typedef enum { VIR_CONNECT_LIST_NODE_DEVICES_CAP_VPORTS = 1 << 10, /* Capable of vport */ VIR_CONNECT_LIST_NODE_DEVICES_CAP_SCSI_GENERIC = 1 << 11, /* Capable of scsi_generic */ VIR_CONNECT_LIST_NODE_DEVICES_CAP_DRM = 1 << 12, /* DRM device */ + VIR_CONNECT_LIST_NODE_DEVICES_CAP_MDEV = 1 << 13, /* Mediated device */ } virConnectListAllNodeDeviceFlags; int virConnectListAllNodeDevices (virConnectPtr conn, diff --git a/src/conf/node_device_conf.c b/src/conf/node_device_conf.c index de346597a..fdddc97eb 100644 --- a/src/conf/node_device_conf.c +++ b/src/conf/node_device_conf.c @@ -60,7 +60,8 @@ VIR_ENUM_IMPL(virNodeDevCap, VIR_NODE_DEV_CAP_LAST, "fc_host", "vports", "scsi_generic", - "drm") + "drm", + "mdev") VIR_ENUM_IMPL(virNodeDevNetCap, VIR_NODE_DEV_CAP_NET_LAST, "80203", @@ -542,6 +543,7 @@ virNodeDeviceDefFormat(const virNodeDeviceDef *def) break; case VIR_NODE_DEV_CAP_FC_HOST: case VIR_NODE_DEV_CAP_VPORTS: + case VIR_NODE_DEV_CAP_MDEV: case VIR_NODE_DEV_CAP_LAST: break; } @@ -1613,6 +1615,7 @@ virNodeDevCapsDefParseXML(xmlXPathContextPtr ctxt, case VIR_NODE_DEV_CAP_FC_HOST: case VIR_NODE_DEV_CAP_VPORTS: case VIR_NODE_DEV_CAP_SCSI_GENERIC: + case VIR_NODE_DEV_CAP_MDEV: case VIR_NODE_DEV_CAP_LAST: virReportError(VIR_ERR_INTERNAL_ERROR, _("unknown capability type '%d' for '%s'"), @@ -1928,6 +1931,7 @@ virNodeDevCapsDefFree(virNodeDevCapsDefPtr caps) case VIR_NODE_DEV_CAP_SCSI_GENERIC: VIR_FREE(data->sg.path); break; + case VIR_NODE_DEV_CAP_MDEV: case VIR_NODE_DEV_CAP_DRM: case VIR_NODE_DEV_CAP_FC_HOST: case VIR_NODE_DEV_CAP_VPORTS: diff --git a/src/conf/node_device_conf.h b/src/conf/node_device_conf.h index a5d5cdd2a..375f97256 100644 --- a/src/conf/node_device_conf.h +++ b/src/conf/node_device_conf.h @@ -64,6 +64,7 @@ typedef enum { VIR_NODE_DEV_CAP_VPORTS, /* HBA which is capable of vports */ VIR_NODE_DEV_CAP_SCSI_GENERIC, /* SCSI generic device */ VIR_NODE_DEV_CAP_DRM, /* DRM device */ + VIR_NODE_DEV_CAP_MDEV, /* Mediated device */ VIR_NODE_DEV_CAP_LAST } virNodeDevCapType; @@ -351,7 +352,8 @@ virNodeDevCapsDefFree(virNodeDevCapsDefPtr caps); VIR_CONNECT_LIST_NODE_DEVICES_CAP_FC_HOST | \ VIR_CONNECT_LIST_NODE_DEVICES_CAP_VPORTS | \ VIR_CONNECT_LIST_NODE_DEVICES_CAP_SCSI_GENERIC | \ - VIR_CONNECT_LIST_NODE_DEVICES_CAP_DRM) + VIR_CONNECT_LIST_NODE_DEVICES_CAP_DRM | \ + VIR_CONNECT_LIST_NODE_DEVICES_CAP_MDEV) char * virNodeDeviceGetParentName(virConnectPtr conn, diff --git a/src/conf/virnodedeviceobj.c b/src/conf/virnodedeviceobj.c index 4f47b4e41..442914b27 100644 --- a/src/conf/virnodedeviceobj.c +++ b/src/conf/virnodedeviceobj.c @@ -550,7 +550,8 @@ virNodeDeviceMatch(virNodeDeviceObjPtr devobj, MATCH(FC_HOST) || MATCH(VPORTS) || MATCH(SCSI_GENERIC) || - MATCH(DRM))) + MATCH(DRM) || + MATCH(MDEV))) return false; } diff --git a/src/libvirt-nodedev.c b/src/libvirt-nodedev.c index 83376b0d9..f9d8edf7e 100644 --- a/src/libvirt-nodedev.c +++ b/src/libvirt-nodedev.c @@ -98,6 +98,7 @@ virNodeNumOfDevices(virConnectPtr conn, const char *cap, unsigned int flags) * VIR_CONNECT_LIST_NODE_DEVICES_CAP_VPORTS * VIR_CONNECT_LIST_NODE_DEVICES_CAP_SCSI_GENERIC * VIR_CONNECT_LIST_NODE_DEVICES_CAP_DRM + * VIR_CONNECT_LIST_NODE_DEVICES_CAP_MDEV * * Returns the number of node devices found or -1 and sets @devices to NULL in * case of error. On success, the array stored into @devices is guaranteed to diff --git a/src/node_device/node_device_driver.c b/src/node_device/node_device_driver.c index c3997c922..fa1993e43 100644 --- a/src/node_device/node_device_driver.c +++ b/src/node_device/node_device_driver.c @@ -82,6 +82,7 @@ static int update_caps(virNodeDeviceObjPtr dev) case VIR_NODE_DEV_CAP_FC_HOST: case VIR_NODE_DEV_CAP_VPORTS: case VIR_NODE_DEV_CAP_SCSI_GENERIC: + case VIR_NODE_DEV_CAP_MDEV: case VIR_NODE_DEV_CAP_LAST: break; } diff --git a/src/node_device/node_device_udev.c b/src/node_device/node_device_udev.c index 6e706a10b..95c1aee29 100644 --- a/src/node_device/node_device_udev.c +++ b/src/node_device/node_device_udev.c @@ -43,6 +43,7 @@ #include "virpci.h" #include "virstring.h" #include "virnetdev.h" +#include "virmdev.h" #define VIR_FROM_THIS VIR_FROM_NODEDEV @@ -1016,12 +1017,16 @@ udevGetDeviceType(struct udev_device *device, if (udevHasDeviceProperty(device, "INTERFACE")) *type = VIR_NODE_DEV_CAP_NET; - /* SCSI generic device doesn't set DEVTYPE property */ + /* Neither SCSI generic devices nor mediated devices set DEVTYPE + * property, therefore we need to rely on the SUBSYSTEM property */ if (udevGetStringProperty(device, "SUBSYSTEM", &subsystem) < 0) return -1; if (STREQ_NULLABLE(subsystem, "scsi_generic")) *type = VIR_NODE_DEV_CAP_SCSI_GENERIC; + else if (STREQ_NULLABLE(subsystem, "mdev")) + *type = VIR_NODE_DEV_CAP_MDEV; + VIR_FREE(subsystem); } @@ -1060,6 +1065,7 @@ static int udevGetDeviceDetails(struct udev_device *device, return udevProcessSCSIGeneric(device, def); case VIR_NODE_DEV_CAP_DRM: return udevProcessDRMDevice(device, def); + case VIR_NODE_DEV_CAP_MDEV: case VIR_NODE_DEV_CAP_SYSTEM: case VIR_NODE_DEV_CAP_FC_HOST: case VIR_NODE_DEV_CAP_VPORTS: diff --git a/tools/virsh-nodedev.c b/tools/virsh-nodedev.c index c69144021..19dcfafa6 100644 --- a/tools/virsh-nodedev.c +++ b/tools/virsh-nodedev.c @@ -454,6 +454,8 @@ cmdNodeListDevices(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) case VIR_NODE_DEV_CAP_DRM: flags |= VIR_CONNECT_LIST_NODE_DEVICES_CAP_DRM; break; + case VIR_NODE_DEV_CAP_MDEV: + flags |= VIR_CONNECT_LIST_NODE_DEVICES_CAP_MDEV; case VIR_NODE_DEV_CAP_LAST: break; } -- 2.12.2

On Thu, Apr 20, 2017 at 03:05:56PM +0200, Erik Skultety wrote:
Besides updating the capability enum, this patch introduces 'virNodeDevCapMdev' structure which will store everything libvirt can gather from sysfs about a mediated device. Since we need to report the info for both the mediated child device and its parent mdev capabilities (merely the mdev types' attributes), this structure serves in both of these cases with the difference being that the amount of data it holds depends on the specific scenario (child vs parent).
The commit message is no longer valid since you've moved the struct into a different patch. Pavel

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' with the resulting XML of the following format: <device> ... <capability type='pci'> ... <capability type='mdev'> <type id='vendor-supplied-id'> <description>optional, raw, unstructured resource allocation data </description> <deviceAPI>vfio-pci</deviceAPI> <availableInstances>NUM</availableInstances> </type> ... <type> ... </type> </capability> </capability> ... </device> Signed-off-by: Erik Skultety <eskultet@redhat.com> --- docs/schemas/nodedev.rng | 24 ++++ src/conf/node_device_conf.c | 103 +++++++++++++++++ src/conf/node_device_conf.h | 17 +++ src/libvirt_private.syms | 1 + src/node_device/node_device_udev.c | 133 ++++++++++++++++++++++ tests/nodedevschemadata/pci_0000_02_10_7_mdev.xml | 27 +++++ 6 files changed, 305 insertions(+) create mode 100644 tests/nodedevschemadata/pci_0000_02_10_7_mdev.xml diff --git a/docs/schemas/nodedev.rng b/docs/schemas/nodedev.rng index 0f90a73c8..4b5dca777 100644 --- a/docs/schemas/nodedev.rng +++ b/docs/schemas/nodedev.rng @@ -205,6 +205,30 @@ </optional> <optional> + <element name='capability'> + <attribute name='type'> + <value>mdev</value> + </attribute> + <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> + </element> + </optional> + + <optional> <element name='iommuGroup'> <attribute name='number'> <ref name='unsignedInt'/> diff --git a/src/conf/node_device_conf.c b/src/conf/node_device_conf.c index fdddc97eb..fe4f1bc60 100644 --- a/src/conf/node_device_conf.c +++ b/src/conf/node_device_conf.c @@ -87,6 +87,26 @@ virNodeDevCapsDefParseString(const char *xpath, } +static void +virNodeDevCapMdevClear(virNodeDevCapMdevPtr mdev) +{ + VIR_FREE(mdev->type); + VIR_FREE(mdev->name); + VIR_FREE(mdev->device_api); +} + + +void +virNodeDevCapMdevFree(virNodeDevCapMdevPtr mdev) +{ + if (!mdev) + return; + + virNodeDevCapMdevClear(mdev); + VIR_FREE(mdev); +} + + void virNodeDeviceDefFree(virNodeDeviceDefPtr def) { @@ -264,6 +284,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'>\n"); + virBufferAdjustIndent(buf, 2); + for (i = 0; i < data->pci_dev.nmdevs; i++) { + virNodeDevCapMdevPtr mdev = data->pci_dev.mdevs[i]; + virBufferEscapeString(buf, "<type id='%s'>\n", mdev->type); + virBufferAdjustIndent(buf, 2); + if (mdev->name) + virBufferAsprintf(buf, "<name>%s</name>\n", + mdev->name); + virBufferAsprintf(buf, "<deviceAPI>%s</deviceAPI>\n", + mdev->device_api); + virBufferAsprintf(buf, + "<availableInstances>%u</availableInstances>\n", + mdev->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); @@ -1358,6 +1399,62 @@ virNodeDevPCICapSRIOVParseXML(xmlXPathContextPtr ctxt, static int +virNodeDevPCICapMediatedDevParseXML(xmlXPathContextPtr ctxt, + virNodeDevCapPCIDevPtr pci_dev) +{ + int ret = -1; + xmlNodePtr orignode = NULL; + xmlNodePtr *nodes = NULL; + int nmdevs = virXPathNodeSet("./type", ctxt, &nodes); + virNodeDevCapMdevPtr mdev = NULL; + size_t i; + + orignode = ctxt->node; + for (i = 0; i < nmdevs; i++) { + ctxt->node = nodes[i]; + + if (VIR_ALLOC(mdev) < 0) + goto cleanup; + + if (!(mdev->type = virXPathString("string(./@id[1])", ctxt))) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("missing 'id' attribute for mediated device's " + "<type> element")); + goto cleanup; + } + + if (!(mdev->device_api = virXPathString("string(./deviceAPI[1])", ctxt))) { + virReportError(VIR_ERR_XML_ERROR, + _("missing device API for mediated device type '%s'"), + mdev->type); + goto cleanup; + } + + if (virXPathUInt("number(./availableInstances)", ctxt, + &mdev->available_instances) < 0) { + virReportError(VIR_ERR_XML_ERROR, + _("missing number of available instances for " + "mediated device type '%s'"), + mdev->type); + goto cleanup; + } + + mdev->name = virXPathString("string(./name)", ctxt); + + if (VIR_APPEND_ELEMENT(pci_dev->mdevs, pci_dev->nmdevs, mdev) < 0) + goto cleanup; + } + + pci_dev->flags |= VIR_NODE_DEV_CAP_FLAG_PCI_MDEV; + ret = 0; + cleanup: + virNodeDevCapMdevFree(mdev); + ctxt->node = orignode; + return ret; +} + + +static int virNodeDevPCICapabilityParseXML(xmlXPathContextPtr ctxt, xmlNodePtr node, virNodeDevCapPCIDevPtr pci_dev) @@ -1382,6 +1479,9 @@ virNodeDevPCICapabilityParseXML(xmlXPathContextPtr ctxt, if (sriov_cap && virNodeDevPCICapSRIOVParseXML(ctxt, node, pci_dev, sriov_cap) < 0) { goto cleanup; + } if (STREQ(type, "mdev") && + virNodeDevPCICapMediatedDevParseXML(ctxt, pci_dev)) { + goto cleanup; } else { int hdrType = virPCIHeaderTypeFromString(type); @@ -1894,6 +1994,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.nmdevs; i++) + virNodeDevCapMdevFree(data->pci_dev.mdevs[i]); + VIR_FREE(data->pci_dev.mdevs); break; case VIR_NODE_DEV_CAP_USB_DEV: VIR_FREE(data->usb_dev.product_name); diff --git a/src/conf/node_device_conf.h b/src/conf/node_device_conf.h index 375f97256..883fa017e 100644 --- a/src/conf/node_device_conf.h +++ b/src/conf/node_device_conf.h @@ -94,6 +94,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 { @@ -132,6 +133,16 @@ struct _virNodeDevCapSystem { virNodeDevCapSystemFirmware firmware; }; +typedef struct _virNodeDevCapMdev virNodeDevCapMdev; +typedef virNodeDevCapMdev *virNodeDevCapMdevPtr; +struct _virNodeDevCapMdev { + char *type; + char *name; + char *device_api; + unsigned int available_instances; + unsigned int iommuGroupNumber; +}; + typedef struct _virNodeDevCapPCIDev virNodeDevCapPCIDev; typedef virNodeDevCapPCIDev *virNodeDevCapPCIDevPtr; struct _virNodeDevCapPCIDev { @@ -155,6 +166,8 @@ struct _virNodeDevCapPCIDev { int numa_node; virPCIEDeviceInfoPtr pci_express; int hdrType; /* enum virPCIHeaderType or -1 */ + virNodeDevCapMdevPtr *mdevs; + size_t nmdevs; }; typedef struct _virNodeDevCapUSBDev virNodeDevCapUSBDev; @@ -263,6 +276,7 @@ struct _virNodeDevCapData { virNodeDevCapStorage storage; virNodeDevCapSCSIGeneric sg; virNodeDevCapDRM drm; + virNodeDevCapMdev mdev; }; }; @@ -339,6 +353,9 @@ virNodeDeviceDefFree(virNodeDeviceDefPtr def); void virNodeDevCapsDefFree(virNodeDevCapsDefPtr caps); +void +virNodeDevCapMdevFree(virNodeDevCapMdevPtr mdev); + # define VIR_CONNECT_LIST_NODE_DEVICES_FILTERS_CAP \ (VIR_CONNECT_LIST_NODE_DEVICES_CAP_SYSTEM | \ VIR_CONNECT_LIST_NODE_DEVICES_CAP_PCI_DEV | \ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 181e17875..d1e872e60 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -665,6 +665,7 @@ virNetDevIPRouteParseXML; # conf/node_device_conf.h +virNodeDevCapMdevFree; virNodeDevCapsDefFree; virNodeDevCapTypeFromString; virNodeDevCapTypeToString; diff --git a/src/node_device/node_device_udev.c b/src/node_device/node_device_udev.c index 95c1aee29..79f1537d9 100644 --- a/src/node_device/node_device_udev.c +++ b/src/node_device/node_device_udev.c @@ -314,6 +314,133 @@ static int udevTranslatePCIIds(unsigned int vendor, } +static int +udevGetMdevCaps(struct udev_device *device, + const char *sysfspath, + virNodeDevCapMdevPtr mdev) +{ + int ret = -1; + char *attrpath = NULL; /* relative path to the actual sysfs attribute */ + const char *devpath = NULL; /* base sysfs path as reported by udev */ + const char *relpath = NULL; /* diff between @sysfspath and @devpath */ + char *tmp = NULL; + +#define MDEV_GET_SYSFS_ATTR(attr_name, dir, 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) \ + + /* 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, let's + * strip the common part of the path and let udev chew the relative bit. + */ + devpath = udev_device_get_syspath(device); + relpath = sysfspath + strlen(devpath); + + /* When calling from the mdev child device, @sysfspath is a symbolic link + * to the actual mdev type (rather than a physical path), so we need to + * resolve it in order to get the type's name. + */ + if (virFileResolveLink(sysfspath, &tmp) < 0) + goto cleanup; + + if (VIR_STRDUP(mdev->type, last_component(tmp)) < 0) + goto cleanup; + + MDEV_GET_SYSFS_ATTR(name, relpath, + udevGetStringSysfsAttr, &mdev->name); + MDEV_GET_SYSFS_ATTR(device_api, relpath, + udevGetStringSysfsAttr, &mdev->device_api); + MDEV_GET_SYSFS_ATTR(available_instances, relpath, + udevGetUintSysfsAttr, &mdev->available_instances, 10); + +#undef MDEV_GET_SYSFS_ATTR + + ret = 0; + cleanup: + VIR_FREE(attrpath); + VIR_FREE(tmp); + return ret; +} + + +static int +udevPCIGetMdevCaps(struct udev_device *device, + virNodeDevCapPCIDevPtr pcidata) +{ + int ret = -1; + int direrr = -1; + DIR *dir = NULL; + struct dirent *entry; + char *path = NULL; + char *tmppath = NULL; + virNodeDevCapMdevPtr mdev = NULL; + virNodeDevCapMdevPtr *mdevs = NULL; + size_t nmdevs = 0; + size_t i; + + if (virAsprintf(&path, "%s/mdev_supported_types", + udev_device_get_syspath(device)) < 0) + return -1; + + if ((direrr = virDirOpenIfExists(&dir, path)) < 0) + goto cleanup; + + if (direrr == 0) { + ret = 0; + goto cleanup; + } + + if (VIR_ALLOC(mdevs) < 0) + goto cleanup; + + /* since udev doesn't provide means to list other than top-level + * attributes, we need to scan the subdirectories ourselves + */ + while ((direrr = virDirRead(dir, &entry, path)) > 0) { + if (VIR_ALLOC(mdev) < 0) + goto cleanup; + + if (virAsprintf(&tmppath, "%s/%s", path, entry->d_name) < 0) + goto cleanup; + + if (udevGetMdevCaps(device, tmppath, mdev) < 0) + goto cleanup; + + if (VIR_APPEND_ELEMENT(mdevs, nmdevs, mdev) < 0) + goto cleanup; + + VIR_FREE(tmppath); + } + + if (direrr < 0) + goto cleanup; + + VIR_STEAL_PTR(pcidata->mdevs, mdevs); + pcidata->nmdevs = nmdevs; + nmdevs = 0; + ret = 0; + cleanup: + virNodeDevCapMdevFree(mdev); + for (i = 0; i < nmdevs; i++) + virNodeDevCapMdevFree(mdevs[i]); + VIR_FREE(mdevs); + VIR_FREE(path); + VIR_FREE(tmppath); + VIR_DIR_CLOSE(dir); + return ret; +} + + static int udevProcessPCI(struct udev_device *device, virNodeDeviceDefPtr def) { @@ -400,6 +527,12 @@ static int udevProcessPCI(struct udev_device *device, } } + /* check whether the device is mediated devices framework capable, if so, + * process it + */ + if (udevPCIGetMdevCaps(device, pci_dev) < 0) + goto cleanup; + ret = 0; cleanup: diff --git a/tests/nodedevschemadata/pci_0000_02_10_7_mdev.xml b/tests/nodedevschemadata/pci_0000_02_10_7_mdev.xml new file mode 100644 index 000000000..b745686d3 --- /dev/null +++ b/tests/nodedevschemadata/pci_0000_02_10_7_mdev.xml @@ -0,0 +1,27 @@ +<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'> + <type id='foo'> + <name>bar</name> + <deviceAPI>vfio-pci</deviceAPI> + <availableInstances>1</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> -- 2.12.2

[...]
+static int +udevPCIGetMdevCaps(struct udev_device *device, + virNodeDevCapPCIDevPtr pcidata) +{ + int ret = -1; + int direrr = -1; + DIR *dir = NULL; + struct dirent *entry; + char *path = NULL; + char *tmppath = NULL; + virNodeDevCapMdevPtr mdev = NULL; + virNodeDevCapMdevPtr *mdevs = NULL; + size_t nmdevs = 0; + size_t i; + + if (virAsprintf(&path, "%s/mdev_supported_types", + udev_device_get_syspath(device)) < 0) + return -1; + + if ((direrr = virDirOpenIfExists(&dir, path)) < 0) + goto cleanup; + + if (direrr == 0) { + ret = 0; + goto cleanup; + } + + if (VIR_ALLOC(mdevs) < 0) + goto cleanup; + + /* since udev doesn't provide means to list other than top-level + * attributes, we need to scan the subdirectories ourselves + */ + while ((direrr = virDirRead(dir, &entry, path)) > 0) { + if (VIR_ALLOC(mdev) < 0) + goto cleanup; + + if (virAsprintf(&tmppath, "%s/%s", path, entry->d_name) < 0) + goto cleanup; + + if (udevGetMdevCaps(device, tmppath, mdev) < 0) + goto cleanup; + + if (VIR_APPEND_ELEMENT(mdevs, nmdevs, mdev) < 0) + goto cleanup; + + VIR_FREE(tmppath); + } + + if (direrr < 0) + goto cleanup; + + VIR_STEAL_PTR(pcidata->mdevs, mdevs); + pcidata->nmdevs = nmdevs; + nmdevs = 0; + ret = 0; + cleanup:
Oops, I forgot something in here. Consider this bit squashed in (otherwise the parent device's output would lack some tiny bits) diff --git a/src/node_device/node_device_udev.c b/src/node_device/node_device_udev.c index a04009110..349d51f69 100644 --- a/src/node_device/node_device_udev.c +++ b/src/node_device/node_device_udev.c @@ -427,6 +427,7 @@ udevPCIGetMdevCaps(struct udev_device *device, VIR_STEAL_PTR(pcidata->mdevs, mdevs); pcidata->nmdevs = nmdevs; + pcidata->flags |= VIR_NODE_DEV_CAP_FLAG_PCI_MDEV; nmdevs = 0; ret = 0; cleanup: Erik

On Thu, Apr 20, 2017 at 03:05:57PM +0200, Erik Skultety wrote:
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' with the resulting XML of the following format:
<device> ... <capability type='pci'> ... <capability type='mdev'> <type id='vendor-supplied-id'> <description>optional, raw, unstructured resource allocation data </description> <deviceAPI>vfio-pci</deviceAPI> <availableInstances>NUM</availableInstances> </type> ... <type> ... </type> </capability> </capability> ... </device>
You've mentioned it in the cover letter and asked me privately to share my opinion about the mdev capability. I agree that we should split the capability into one for parent device and one for child device. Having one capability for two different devices where the capability means something else based on the device doesn't seem to be a good idea especially if we have an API that allows you list devices whit some specific capability. The internal representation of the capability is one structure where both parent and child shares only *type*. The suggested names mdev-parent and mdev-child seems as good names if the "parent" and "child" naming is generally used for mdev devices.
Signed-off-by: Erik Skultety <eskultet@redhat.com> --- docs/schemas/nodedev.rng | 24 ++++ src/conf/node_device_conf.c | 103 +++++++++++++++++ src/conf/node_device_conf.h | 17 +++ src/libvirt_private.syms | 1 + src/node_device/node_device_udev.c | 133 ++++++++++++++++++++++ tests/nodedevschemadata/pci_0000_02_10_7_mdev.xml | 27 +++++ 6 files changed, 305 insertions(+) create mode 100644 tests/nodedevschemadata/pci_0000_02_10_7_mdev.xml
diff --git a/docs/schemas/nodedev.rng b/docs/schemas/nodedev.rng index 0f90a73c8..4b5dca777 100644 --- a/docs/schemas/nodedev.rng +++ b/docs/schemas/nodedev.rng @@ -205,6 +205,30 @@ </optional>
<optional> + <element name='capability'> + <attribute name='type'> + <value>mdev</value> + </attribute> + <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> + </element> + </optional> + + <optional> <element name='iommuGroup'> <attribute name='number'> <ref name='unsignedInt'/> diff --git a/src/conf/node_device_conf.c b/src/conf/node_device_conf.c index fdddc97eb..fe4f1bc60 100644 --- a/src/conf/node_device_conf.c +++ b/src/conf/node_device_conf.c @@ -87,6 +87,26 @@ virNodeDevCapsDefParseString(const char *xpath, }
+static void +virNodeDevCapMdevClear(virNodeDevCapMdevPtr mdev) +{ + VIR_FREE(mdev->type); + VIR_FREE(mdev->name); + VIR_FREE(mdev->device_api); +}
This won't be required if we split the mdev capability.
+void +virNodeDevCapMdevFree(virNodeDevCapMdevPtr mdev) +{ + if (!mdev) + return; + + virNodeDevCapMdevClear(mdev); + VIR_FREE(mdev); +} + + void virNodeDeviceDefFree(virNodeDeviceDefPtr def) { @@ -264,6 +284,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'>\n"); + virBufferAdjustIndent(buf, 2); + for (i = 0; i < data->pci_dev.nmdevs; i++) { + virNodeDevCapMdevPtr mdev = data->pci_dev.mdevs[i]; + virBufferEscapeString(buf, "<type id='%s'>\n", mdev->type); + virBufferAdjustIndent(buf, 2); + if (mdev->name) + virBufferAsprintf(buf, "<name>%s</name>\n", + mdev->name);
This will fit on one line.
+ virBufferAsprintf(buf, "<deviceAPI>%s</deviceAPI>\n", + mdev->device_api); + virBufferAsprintf(buf, + "<availableInstances>%u</availableInstances>\n", + mdev->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); @@ -1358,6 +1399,62 @@ virNodeDevPCICapSRIOVParseXML(xmlXPathContextPtr ctxt,
static int +virNodeDevPCICapMediatedDevParseXML(xmlXPathContextPtr ctxt, + virNodeDevCapPCIDevPtr pci_dev) +{ + int ret = -1; + xmlNodePtr orignode = NULL; + xmlNodePtr *nodes = NULL; + int nmdevs = virXPathNodeSet("./type", ctxt, &nodes); + virNodeDevCapMdevPtr mdev = NULL; + size_t i; + + orignode = ctxt->node; + for (i = 0; i < nmdevs; i++) { + ctxt->node = nodes[i]; + + if (VIR_ALLOC(mdev) < 0) + goto cleanup; + + if (!(mdev->type = virXPathString("string(./@id[1])", ctxt))) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("missing 'id' attribute for mediated device's " + "<type> element")); + goto cleanup; + } + + if (!(mdev->device_api = virXPathString("string(./deviceAPI[1])", ctxt))) { + virReportError(VIR_ERR_XML_ERROR, + _("missing device API for mediated device type '%s'"), + mdev->type); + goto cleanup; + } + + if (virXPathUInt("number(./availableInstances)", ctxt, + &mdev->available_instances) < 0) { + virReportError(VIR_ERR_XML_ERROR, + _("missing number of available instances for " + "mediated device type '%s'"), + mdev->type); + goto cleanup; + } + + mdev->name = virXPathString("string(./name)", ctxt); + + if (VIR_APPEND_ELEMENT(pci_dev->mdevs, pci_dev->nmdevs, mdev) < 0) + goto cleanup; + } + + pci_dev->flags |= VIR_NODE_DEV_CAP_FLAG_PCI_MDEV; + ret = 0; + cleanup: + virNodeDevCapMdevFree(mdev); + ctxt->node = orignode; + return ret; +} + + +static int virNodeDevPCICapabilityParseXML(xmlXPathContextPtr ctxt, xmlNodePtr node, virNodeDevCapPCIDevPtr pci_dev) @@ -1382,6 +1479,9 @@ virNodeDevPCICapabilityParseXML(xmlXPathContextPtr ctxt, if (sriov_cap && virNodeDevPCICapSRIOVParseXML(ctxt, node, pci_dev, sriov_cap) < 0) { goto cleanup; + } if (STREQ(type, "mdev") && + virNodeDevPCICapMediatedDevParseXML(ctxt, pci_dev)) { + goto cleanup; } else { int hdrType = virPCIHeaderTypeFromString(type);
@@ -1894,6 +1994,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.nmdevs; i++) + virNodeDevCapMdevFree(data->pci_dev.mdevs[i]); + VIR_FREE(data->pci_dev.mdevs); break; case VIR_NODE_DEV_CAP_USB_DEV: VIR_FREE(data->usb_dev.product_name); diff --git a/src/conf/node_device_conf.h b/src/conf/node_device_conf.h index 375f97256..883fa017e 100644 --- a/src/conf/node_device_conf.h +++ b/src/conf/node_device_conf.h @@ -94,6 +94,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 { @@ -132,6 +133,16 @@ struct _virNodeDevCapSystem { virNodeDevCapSystemFirmware firmware; };
+typedef struct _virNodeDevCapMdev virNodeDevCapMdev; +typedef virNodeDevCapMdev *virNodeDevCapMdevPtr; +struct _virNodeDevCapMdev { + char *type; + char *name; + char *device_api; + unsigned int available_instances; + unsigned int iommuGroupNumber; +}; + typedef struct _virNodeDevCapPCIDev virNodeDevCapPCIDev; typedef virNodeDevCapPCIDev *virNodeDevCapPCIDevPtr; struct _virNodeDevCapPCIDev { @@ -155,6 +166,8 @@ struct _virNodeDevCapPCIDev { int numa_node; virPCIEDeviceInfoPtr pci_express; int hdrType; /* enum virPCIHeaderType or -1 */ + virNodeDevCapMdevPtr *mdevs; + size_t nmdevs; };
typedef struct _virNodeDevCapUSBDev virNodeDevCapUSBDev; @@ -263,6 +276,7 @@ struct _virNodeDevCapData { virNodeDevCapStorage storage; virNodeDevCapSCSIGeneric sg; virNodeDevCapDRM drm; + virNodeDevCapMdev mdev;
This should be part of the next patch.
}; };
@@ -339,6 +353,9 @@ virNodeDeviceDefFree(virNodeDeviceDefPtr def); void virNodeDevCapsDefFree(virNodeDevCapsDefPtr caps);
+void +virNodeDevCapMdevFree(virNodeDevCapMdevPtr mdev); + # define VIR_CONNECT_LIST_NODE_DEVICES_FILTERS_CAP \ (VIR_CONNECT_LIST_NODE_DEVICES_CAP_SYSTEM | \ VIR_CONNECT_LIST_NODE_DEVICES_CAP_PCI_DEV | \ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 181e17875..d1e872e60 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -665,6 +665,7 @@ virNetDevIPRouteParseXML;
# conf/node_device_conf.h +virNodeDevCapMdevFree; virNodeDevCapsDefFree; virNodeDevCapTypeFromString; virNodeDevCapTypeToString; diff --git a/src/node_device/node_device_udev.c b/src/node_device/node_device_udev.c index 95c1aee29..79f1537d9 100644 --- a/src/node_device/node_device_udev.c +++ b/src/node_device/node_device_udev.c @@ -314,6 +314,133 @@ static int udevTranslatePCIIds(unsigned int vendor, }
+static int +udevGetMdevCaps(struct udev_device *device, + const char *sysfspath, + virNodeDevCapMdevPtr mdev) +{ + int ret = -1; + char *attrpath = NULL; /* relative path to the actual sysfs attribute */ + const char *devpath = NULL; /* base sysfs path as reported by udev */ + const char *relpath = NULL; /* diff between @sysfspath and @devpath */ + char *tmp = NULL; + +#define MDEV_GET_SYSFS_ATTR(attr_name, dir, 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) \ + + /* 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, let's + * strip the common part of the path and let udev chew the relative bit. + */ + devpath = udev_device_get_syspath(device); + relpath = sysfspath + strlen(devpath); + + /* When calling from the mdev child device, @sysfspath is a symbolic link + * to the actual mdev type (rather than a physical path), so we need to + * resolve it in order to get the type's name. + */ + if (virFileResolveLink(sysfspath, &tmp) < 0) + goto cleanup; + + if (VIR_STRDUP(mdev->type, last_component(tmp)) < 0) + goto cleanup; + + MDEV_GET_SYSFS_ATTR(name, relpath, + udevGetStringSysfsAttr, &mdev->name); + MDEV_GET_SYSFS_ATTR(device_api, relpath, + udevGetStringSysfsAttr, &mdev->device_api); + MDEV_GET_SYSFS_ATTR(available_instances, relpath, + udevGetUintSysfsAttr, &mdev->available_instances, 10); + +#undef MDEV_GET_SYSFS_ATTR + + ret = 0; + cleanup: + VIR_FREE(attrpath); + VIR_FREE(tmp); + return ret; +} + + +static int +udevPCIGetMdevCaps(struct udev_device *device, + virNodeDevCapPCIDevPtr pcidata) +{ + int ret = -1; + int direrr = -1; + DIR *dir = NULL; + struct dirent *entry; + char *path = NULL; + char *tmppath = NULL; + virNodeDevCapMdevPtr mdev = NULL; + virNodeDevCapMdevPtr *mdevs = NULL; + size_t nmdevs = 0; + size_t i; + + if (virAsprintf(&path, "%s/mdev_supported_types", + udev_device_get_syspath(device)) < 0) + return -1; + + if ((direrr = virDirOpenIfExists(&dir, path)) < 0)
I would probably name it as dirret, it doesn't indicate only whether there was an error.
+ goto cleanup; + + if (direrr == 0) { + ret = 0; + goto cleanup; + } + + if (VIR_ALLOC(mdevs) < 0) + goto cleanup; + + /* since udev doesn't provide means to list other than top-level + * attributes, we need to scan the subdirectories ourselves + */ + while ((direrr = virDirRead(dir, &entry, path)) > 0) { + if (VIR_ALLOC(mdev) < 0) + goto cleanup; + + if (virAsprintf(&tmppath, "%s/%s", path, entry->d_name) < 0) + goto cleanup; + + if (udevGetMdevCaps(device, tmppath, mdev) < 0) + goto cleanup; + + if (VIR_APPEND_ELEMENT(mdevs, nmdevs, mdev) < 0) + goto cleanup; + + VIR_FREE(tmppath); + } + + if (direrr < 0) + goto cleanup; + + VIR_STEAL_PTR(pcidata->mdevs, mdevs); + pcidata->nmdevs = nmdevs; + nmdevs = 0; + ret = 0; + cleanup: + virNodeDevCapMdevFree(mdev); + for (i = 0; i < nmdevs; i++) + virNodeDevCapMdevFree(mdevs[i]); + VIR_FREE(mdevs); + VIR_FREE(path); + VIR_FREE(tmppath); + VIR_DIR_CLOSE(dir); + return ret; +} + + static int udevProcessPCI(struct udev_device *device, virNodeDeviceDefPtr def) { @@ -400,6 +527,12 @@ static int udevProcessPCI(struct udev_device *device, } }
+ /* check whether the device is mediated devices framework capable, if so, + * process it + */ + if (udevPCIGetMdevCaps(device, pci_dev) < 0) + goto cleanup; + ret = 0;
cleanup: diff --git a/tests/nodedevschemadata/pci_0000_02_10_7_mdev.xml b/tests/nodedevschemadata/pci_0000_02_10_7_mdev.xml new file mode 100644 index 000000000..b745686d3 --- /dev/null +++ b/tests/nodedevschemadata/pci_0000_02_10_7_mdev.xml @@ -0,0 +1,27 @@ +<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'> + <type id='foo'> + <name>bar</name> + <deviceAPI>vfio-pci</deviceAPI> + <availableInstances>1</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> -- 2.12.2
-- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list

Start discovering the mediated devices on the host system and format the attributes for the child device into the XML. Compared to the parent device which reports generic information about the abstract mediated devices types, a child device only reports the type name it has been instantiated from and the iommu group number, since that's device specific compared to the rest of the info that can be gathered about mdevs at the moment. This patch introduces both the formatting and parsing routines, updates nodedev.rng schema, adding a testcase for mdev child device as well. The resulting mdev child device XML: <device> <name>mdev_4b20d080_1b54_4048_85b3_a6a62d165c01</name> <path>/sys/devices/.../4b20d080-1b54-4048-85b3-a6a62d165c01</path> <parent>pci_0000_06_00_0</parent> <driver> <name>vfio_mdev</name> </driver> <capability type='mdev'> <type id='vendor-supplied-type-id'/> <iommuGroup number='NUM'/> <capability/> <device/> Signed-off-by: Erik Skultety <eskultet@redhat.com> --- docs/schemas/nodedev.rng | 17 ++++++++ src/conf/node_device_conf.c | 43 +++++++++++++++++++- src/node_device/node_device_udev.c | 46 ++++++++++++++++++++++ .../mdev_3627463d_b7f0_4fea_b468_f1da537d301b.xml | 8 ++++ tests/nodedevxml2xmltest.c | 1 + 5 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 tests/nodedevschemadata/mdev_3627463d_b7f0_4fea_b468_f1da537d301b.xml diff --git a/docs/schemas/nodedev.rng b/docs/schemas/nodedev.rng index 4b5dca777..8466d504d 100644 --- a/docs/schemas/nodedev.rng +++ b/docs/schemas/nodedev.rng @@ -83,6 +83,7 @@ <ref name="capscsi"/> <ref name="capstorage"/> <ref name="capdrm"/> + <ref name="capmdev"/> </choice> </element> </define> @@ -578,6 +579,22 @@ </element> </define> + <define name='capmdev'> + <attribute name='type'> + <value>mdev</value> + </attribute> + <element name='type'> + <attribute name='id'> + <data type='string'/> + </attribute> + </element> + <element name='iommuGroup'> + <attribute name='number'> + <ref name='unsignedInt'/> + </attribute> + </element> + </define> + <define name='address'> <element name='address'> <attribute name='domain'><ref name='hexuint'/></attribute> diff --git a/src/conf/node_device_conf.c b/src/conf/node_device_conf.c index fe4f1bc60..757e4bed7 100644 --- a/src/conf/node_device_conf.c +++ b/src/conf/node_device_conf.c @@ -582,9 +582,13 @@ virNodeDeviceDefFormat(const virNodeDeviceDef *def) case VIR_NODE_DEV_CAP_DRM: virBufferEscapeString(&buf, "<type>%s</type>\n", virNodeDevDRMTypeToString(data->drm.type)); break; + case VIR_NODE_DEV_CAP_MDEV: + virBufferEscapeString(&buf, "<type id='%s'/>\n", data->mdev.type); + virBufferAsprintf(&buf, "<iommuGroup number='%u'/>\n", + data->mdev.iommuGroupNumber); + break; case VIR_NODE_DEV_CAP_FC_HOST: case VIR_NODE_DEV_CAP_VPORTS: - case VIR_NODE_DEV_CAP_MDEV: case VIR_NODE_DEV_CAP_LAST: break; } @@ -1645,6 +1649,39 @@ virNodeDevCapSystemParseXML(xmlXPathContextPtr ctxt, } +static int +virNodeDevCapMdevParseXML(xmlXPathContextPtr ctxt, + virNodeDeviceDefPtr def, + xmlNodePtr node, + virNodeDevCapMdevPtr mdev) +{ + xmlNodePtr orignode; + int ret = -1; + + orignode = ctxt->node; + ctxt->node = node; + + if (!(mdev->type = virXPathString("string(./type[1]/@id)", ctxt))) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("missing type id attribute for '%s'"), def->name); + goto out; + } + + if (virNodeDevCapsDefParseULong("number(./iommuGroup[1]/@number)", ctxt, + &mdev->iommuGroupNumber, def, + _("missing iommuGroup number atribute for " + "'%s'"), + _("invalid iommuGroup number attribute for " + "'%s'")) < 0) + goto out; + + ret = 0; + out: + ctxt->node = orignode; + return ret; +} + + static virNodeDevCapsDefPtr virNodeDevCapsDefParseXML(xmlXPathContextPtr ctxt, virNodeDeviceDefPtr def, @@ -1716,6 +1753,8 @@ virNodeDevCapsDefParseXML(xmlXPathContextPtr ctxt, case VIR_NODE_DEV_CAP_VPORTS: case VIR_NODE_DEV_CAP_SCSI_GENERIC: case VIR_NODE_DEV_CAP_MDEV: + ret = virNodeDevCapMdevParseXML(ctxt, def, node, &caps->data.mdev); + break; case VIR_NODE_DEV_CAP_LAST: virReportError(VIR_ERR_INTERNAL_ERROR, _("unknown capability type '%d' for '%s'"), @@ -2035,6 +2074,8 @@ virNodeDevCapsDefFree(virNodeDevCapsDefPtr caps) VIR_FREE(data->sg.path); break; case VIR_NODE_DEV_CAP_MDEV: + virNodeDevCapMdevClear(&data->mdev); + break; case VIR_NODE_DEV_CAP_DRM: case VIR_NODE_DEV_CAP_FC_HOST: case VIR_NODE_DEV_CAP_VPORTS: diff --git a/src/node_device/node_device_udev.c b/src/node_device/node_device_udev.c index 79f1537d9..a04009110 100644 --- a/src/node_device/node_device_udev.c +++ b/src/node_device/node_device_udev.c @@ -1083,6 +1083,51 @@ udevProcessSCSIGeneric(struct udev_device *dev, } static int +udevProcessMediatedDevice(struct udev_device *dev, + virNodeDeviceDefPtr def) +{ + int ret = -1; + const char *uuidstr = NULL; + int iommugrp = -1; + int model = -1; + char *path = NULL; + virMediatedDevicePtr mdev = NULL; + virNodeDevCapMdevPtr data = &def->caps->data.mdev; + + if (virAsprintf(&path, "%s/mdev_type", udev_device_get_syspath(dev)) < 0) + goto cleanup; + + if (udevGetMdevCaps(dev, path, data) < 0) + goto cleanup; + + if ((model = virMediatedDeviceModelTypeFromString(data->device_api)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Device API '%s' not supported yet"), + data->device_api); + goto cleanup; + } + + uuidstr = udev_device_get_sysname(dev); + if (!(mdev = virMediatedDeviceNew(uuidstr, model))) + goto cleanup; + + if ((iommugrp = virMediatedDeviceGetIOMMUGroupNum(mdev)) < 0) + goto cleanup; + + if (udevGenerateDeviceName(dev, def, NULL) != 0) + goto cleanup; + + data->iommuGroupNumber = iommugrp; + + ret = 0; + cleanup: + VIR_FREE(path); + virMediatedDeviceFree(mdev); + return ret; + +} + +static int udevGetDeviceNodes(struct udev_device *device, virNodeDeviceDefPtr def) { @@ -1199,6 +1244,7 @@ static int udevGetDeviceDetails(struct udev_device *device, case VIR_NODE_DEV_CAP_DRM: return udevProcessDRMDevice(device, def); case VIR_NODE_DEV_CAP_MDEV: + return udevProcessMediatedDevice(device, def); case VIR_NODE_DEV_CAP_SYSTEM: case VIR_NODE_DEV_CAP_FC_HOST: case VIR_NODE_DEV_CAP_VPORTS: diff --git a/tests/nodedevschemadata/mdev_3627463d_b7f0_4fea_b468_f1da537d301b.xml b/tests/nodedevschemadata/mdev_3627463d_b7f0_4fea_b468_f1da537d301b.xml new file mode 100644 index 000000000..470e5917e --- /dev/null +++ b/tests/nodedevschemadata/mdev_3627463d_b7f0_4fea_b468_f1da537d301b.xml @@ -0,0 +1,8 @@ +<device> + <name>mdev_3627463d_b7f0_4fea_b468_f1da537d301b</name> + <parent>computer</parent> + <capability type='mdev'> + <type id='mtty-1'/> + <iommuGroup number='12'/> + </capability> +</device> diff --git a/tests/nodedevxml2xmltest.c b/tests/nodedevxml2xmltest.c index f023d8a13..88c75ea78 100644 --- a/tests/nodedevxml2xmltest.c +++ b/tests/nodedevxml2xmltest.c @@ -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("mdev_3627463d_b7f0_4fea_b468_f1da537d301b"); return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } -- 2.12.2

There's lot more to document about the nodedev driver, besides PCI and SR-IOV (even this might need to be extended), but let's start small-ish and at least have a page for it linked from the drivers.html. Signed-off-by: Erik Skultety <eskultet@redhat.com> --- docs/drivers.html.in | 6 +- docs/drvnodedev.html.in | 184 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 189 insertions(+), 1 deletion(-) create mode 100644 docs/drvnodedev.html.in diff --git a/docs/drivers.html.in b/docs/drivers.html.in index be7483b9b..61993861e 100644 --- a/docs/drivers.html.in +++ b/docs/drivers.html.in @@ -4,7 +4,11 @@ <body> <h1>Internal drivers</h1> - <ul id="toc"></ul> + <ul> + <li><a href="#hypervisor">Hypervisor drivers</a></li> + <li><a href="#storage">Storage drivers</a></li> + <li><a href="drvnodedev.html">Node device driver</a></li> + </ul> <p> The libvirt public API delegates its implementation to one or diff --git a/docs/drvnodedev.html.in b/docs/drvnodedev.html.in new file mode 100644 index 000000000..ed185c3df --- /dev/null +++ b/docs/drvnodedev.html.in @@ -0,0 +1,184 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> + <body> + <h1>Host device management</h1> + + <p> + Libvirt provides management of both physical and virtual host devices + (historically also referred to as node devices) like USB, PCI, SCSI, and + network devices. This also includes various virtualization capabilities + which the aforementioned devices provide for utilization, for example + SR-IOV, NPIV, MDEV, DRM, etc. <br/> + <br/> + The node device driver provides means to list and show details about host + devices (<code>virsh nodedev-list</code>, + <code>virsh nodedev-dumpxml</code>), which are generic and can be used + with all devices. It also provides means to create and destroy devices + (<code>virsh nodedev-create</code>, <code>virsh nodedev-destroy</code>) + which are meant to be used to create virtual devices, currently only + supported by NPIV + (<a href="http://wiki.libvirt.org/page/NPIV_in_libvirt">more info about NPIV)</a>). <br/> + <br/> + Devices on the host system are arranged in a tree-like hierarchy, with + the root node being called <code>computer</code>. The node device driver + supports two backends to manage the devices, HAL and udev, with the former + being deprecated in favour of the latter.<br/> + The generic format of a host device XML can be seen below. + To identify a device both within the host and the device tree hierarchy, + the following elements are used: + </p> + <dl> + <dt><code>name</code></dt> + <dd> + The device's name will be generated by libvirt using the subsystem, + like pci and the device's sysfs basename. + </dd> + <dt><code>path</code></dt> + <dd> + Fully qualified sysfs path to the device. + </dd> + <dt><code>parent</code></dt> + <dd> + This element identifies the parent node in the device hierarchy. The + value of the element will correspond with the device parent's + <code>name</code> element or <code>computer</code> if the device does + not have any parent. + </dd> + <dt><code>driver</code></dt> + <dd> + This elements reports the driver in use for this device. The presence + of this element in the output XML depends on whether the underlying + device manager (most likely udev) exposes information about the + driver. + </dd> + <dt><code>capability</code></dt> + <dd> + Describes the device in terms of feature support. The element has one + mandatory attribute <code>type</code> the value of which determines + the type of the device. Currently recognized values for the attribute + are: + <code>system</code>, + <code>pci</code>, + <code>usb</code>, + <code>usb_device</code>, + <code>net</code>, + <code>scsi</code>, + <code>scsi_host</code> (<span class="since">Since 0.4.7</span>), + <code>fc_host</code>, + <code>vports</code>, + <code>scsi_target</code> (<span class="since">Since 0.7.3</span>), + <code>storage</code> (<span class="since">Since 1.0.4</span>), + <code>scsi_generic</code> (<span class="since">Since 1.0.7</span>), + <code>drm</code> (<span class="since">Since 3.1.0</span>), and + <code>mdev</code> (<span class="since">Since 3.2.0</span>). + This element can be nested in which case it further specifies a + device's capability. Refer to specific device types to see more values + for the <code>type</code> attribute which are exclusive. + </dd> + </dl> + + <h2>Basic structure of a node device</h2> + <pre> +<device> + <name>pci_0000_00_17_0</name> + <path>/sys/devices/pci0000:00/0000:00:17.0</path> + <parent>computer</parent> + <driver> + <name>ahci</name> + </driver> + <capability type='pci'> +... + </capability> +</device></pre> + + <ul id="toc"/> + + <h2><a name="PCI">PCI host devices</a></h2> + <dl> + <dt><code>capability</code></dt> + <dd> + When used as top level element, the supported values for the + <code>type</code> attribute are <code>pci</code> and + <code>phys_function</code> (see <a href="#SRIOVCap">SR-IOV below</a>). + </dd> + </dl> + <pre> +<device> + <name>pci_0000_04_00_1</name> + <path>/sys/devices/pci0000:00/0000:00:06.0/0000:04:00.1</path> + <parent>pci_0000_00_06_0</parent> + <driver> + <name>igb</name> + </driver> + <capability type='pci'> + <domain>0</domain> + <bus>4</bus> + <slot>0</slot> + <function>1</function> + <product id='0x10c9'>82576 Gigabit Network Connection</product> + <vendor id='0x8086'>Intel Corporation</vendor> + <iommuGroup number='15'> + <address domain='0x0000' bus='0x04' slot='0x00' function='0x1'/> + </iommuGroup> + <numa node='0'/> + <pci-express> + <link validity='cap' port='1' speed='2.5' width='2'/> + <link validity='sta' speed='2.5' width='2'/> + </pci-express> + </capability> +</device></pre> + + <p> + The XML format for a PCI device stays the same for any further + capabilities it supports, a single nested <code><capability></code> + element will be included for each capability the device supports. + </p> + + <h3><a name="SRIOVCap">SR-IOV capability</a></h3> + <p> + Single root input/output virtualization (SR-IOV) allows sharing of the + PCIe resources by multiple virtual environments. That is achieved by + slicing up a single full-featured physical resource called physical + function (PF) into multiple devices called virtual functions (VFs) sharing + their configuration with the underlying PF. Despite the SR-IOV + specification, the amount of VFs that can be created on a PF varies among + manufacturers.<br/> + <br/> + Suppose the NIC <a href="#PCI">above</a> was also SR-IOV capable, it would + also include a nested + <code><capability></code> element enumerating all virtual + functions available on the physical device (physical port) like in the + example below. + </p> + + <pre> +<capability type='pci'> +... + <capability type='virt_functions' maxCount='7'> + <address domain='0x0000' bus='0x04' slot='0x10' function='0x1'/> + <address domain='0x0000' bus='0x04' slot='0x10' function='0x3'/> + <address domain='0x0000' bus='0x04' slot='0x10' function='0x5'/> + <address domain='0x0000' bus='0x04' slot='0x10' function='0x7'/> + <address domain='0x0000' bus='0x04' slot='0x11' function='0x1'/> + <address domain='0x0000' bus='0x04' slot='0x11' function='0x3'/> + <address domain='0x0000' bus='0x04' slot='0x11' function='0x5'/> + </capability> +... +</capability></pre> + <p> + A SR-IOV child device on the other hand, would then report its top level + capability type as a physical function instead: + </p> + + <pre> +<device> +... + <capability type='phys_function'> + <address domain='0x0000' bus='0x04' slot='0x00' function='0x0'/> + </capability> +... +<device></pre> + + </body> +</html> -- 2.12.2

On Thu, Apr 20, 2017 at 03:05:59PM +0200, Erik Skultety wrote:
There's lot more to document about the nodedev driver, besides PCI and SR-IOV (even this might need to be extended), but let's start small-ish and at least have a page for it linked from the drivers.html.
Signed-off-by: Erik Skultety <eskultet@redhat.com> --- docs/drivers.html.in | 6 +- docs/drvnodedev.html.in | 184 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 189 insertions(+), 1 deletion(-) create mode 100644 docs/drvnodedev.html.in
diff --git a/docs/drvnodedev.html.in b/docs/drvnodedev.html.in new file mode 100644 index 000000000..ed185c3df --- /dev/null +++ b/docs/drvnodedev.html.in @@ -0,0 +1,184 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> + <body> + <h1>Host device management</h1> + + <p> + Libvirt provides management of both physical and virtual host devices + (historically also referred to as node devices) like USB, PCI, SCSI, and + network devices. This also includes various virtualization capabilities + which the aforementioned devices provide for utilization, for example + SR-IOV, NPIV, MDEV, DRM, etc. <br/> + <br/> + The node device driver provides means to list and show details about host + devices (<code>virsh nodedev-list</code>, + <code>virsh nodedev-dumpxml</code>), which are generic and can be used + with all devices. It also provides means to create and destroy devices + (<code>virsh nodedev-create</code>, <code>virsh nodedev-destroy</code>) + which are meant to be used to create virtual devices, currently only + supported by NPIV + (<a href="http://wiki.libvirt.org/page/NPIV_in_libvirt">more info about NPIV)</a>). <br/> ^ Extra parenthesis ------------------------------------------------------------------'
Jan
+ <br/> + Devices on the host system are arranged in a tree-like hierarchy, with + the root node being called <code>computer</code>. The node device driver + supports two backends to manage the devices, HAL and udev, with the former + being deprecated in favour of the latter.<br/>

On Thu, Apr 20, 2017 at 03:05:59PM +0200, Erik Skultety wrote:
There's lot more to document about the nodedev driver, besides PCI and SR-IOV (even this might need to be extended), but let's start small-ish and at least have a page for it linked from the drivers.html.
Signed-off-by: Erik Skultety <eskultet@redhat.com> --- docs/drivers.html.in | 6 +- docs/drvnodedev.html.in | 184 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 189 insertions(+), 1 deletion(-) create mode 100644 docs/drvnodedev.html.in
diff --git a/docs/drivers.html.in b/docs/drivers.html.in index be7483b9b..61993861e 100644 --- a/docs/drivers.html.in +++ b/docs/drivers.html.in @@ -4,7 +4,11 @@ <body> <h1>Internal drivers</h1>
- <ul id="toc"></ul> + <ul> + <li><a href="#hypervisor">Hypervisor drivers</a></li> + <li><a href="#storage">Storage drivers</a></li> + <li><a href="drvnodedev.html">Node device driver</a></li> + </ul>
<p> The libvirt public API delegates its implementation to one or diff --git a/docs/drvnodedev.html.in b/docs/drvnodedev.html.in new file mode 100644 index 000000000..ed185c3df --- /dev/null +++ b/docs/drvnodedev.html.in @@ -0,0 +1,184 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> + <body> + <h1>Host device management</h1> + + <p> + Libvirt provides management of both physical and virtual host devices + (historically also referred to as node devices) like USB, PCI, SCSI, and + network devices. This also includes various virtualization capabilities + which the aforementioned devices provide for utilization, for example + SR-IOV, NPIV, MDEV, DRM, etc. <br/> + <br/>
You should use </p> and <p> instead of double <br/> if we are using paragraphs.
+ The node device driver provides means to list and show details about host + devices (<code>virsh nodedev-list</code>, + <code>virsh nodedev-dumpxml</code>), which are generic and can be used + with all devices. It also provides means to create and destroy devices + (<code>virsh nodedev-create</code>, <code>virsh nodedev-destroy</code>) + which are meant to be used to create virtual devices, currently only + supported by NPIV + (<a href="http://wiki.libvirt.org/page/NPIV_in_libvirt">more info about NPIV)</a>). <br/> + <br/>
Same here.
+ Devices on the host system are arranged in a tree-like hierarchy, with + the root node being called <code>computer</code>. The node device driver + supports two backends to manage the devices, HAL and udev, with the former + being deprecated in favour of the latter.<br/>
Either remove the single <br/> or replace it with a pair of </p> and <p> to end current paragraph and start a new one.
+ The generic format of a host device XML can be seen below. + To identify a device both within the host and the device tree hierarchy, + the following elements are used: + </p> + <dl> + <dt><code>name</code></dt> + <dd> + The device's name will be generated by libvirt using the subsystem, + like pci and the device's sysfs basename. + </dd> + <dt><code>path</code></dt> + <dd> + Fully qualified sysfs path to the device. + </dd> + <dt><code>parent</code></dt> + <dd> + This element identifies the parent node in the device hierarchy. The + value of the element will correspond with the device parent's + <code>name</code> element or <code>computer</code> if the device does + not have any parent. + </dd> + <dt><code>driver</code></dt> + <dd> + This elements reports the driver in use for this device. The presence + of this element in the output XML depends on whether the underlying + device manager (most likely udev) exposes information about the + driver. + </dd> + <dt><code>capability</code></dt> + <dd> + Describes the device in terms of feature support. The element has one + mandatory attribute <code>type</code> the value of which determines + the type of the device. Currently recognized values for the attribute + are: + <code>system</code>, + <code>pci</code>, + <code>usb</code>, + <code>usb_device</code>, + <code>net</code>, + <code>scsi</code>, + <code>scsi_host</code> (<span class="since">Since 0.4.7</span>), + <code>fc_host</code>, + <code>vports</code>, + <code>scsi_target</code> (<span class="since">Since 0.7.3</span>), + <code>storage</code> (<span class="since">Since 1.0.4</span>), + <code>scsi_generic</code> (<span class="since">Since 1.0.7</span>), + <code>drm</code> (<span class="since">Since 3.1.0</span>), and + <code>mdev</code> (<span class="since">Since 3.2.0</span>). + This element can be nested in which case it further specifies a + device's capability. Refer to specific device types to see more values + for the <code>type</code> attribute which are exclusive. + </dd> + </dl> + + <h2>Basic structure of a node device</h2> + <pre> +<device> + <name>pci_0000_00_17_0</name> + <path>/sys/devices/pci0000:00/0000:00:17.0</path> + <parent>computer</parent> + <driver> + <name>ahci</name> + </driver> + <capability type='pci'> +... + </capability> +</device></pre> + + <ul id="toc"/> + + <h2><a name="PCI">PCI host devices</a></h2> + <dl> + <dt><code>capability</code></dt> + <dd> + When used as top level element, the supported values for the + <code>type</code> attribute are <code>pci</code> and + <code>phys_function</code> (see <a href="#SRIOVCap">SR-IOV below</a>). + </dd> + </dl> + <pre> +<device> + <name>pci_0000_04_00_1</name> + <path>/sys/devices/pci0000:00/0000:00:06.0/0000:04:00.1</path> + <parent>pci_0000_00_06_0</parent> + <driver> + <name>igb</name> + </driver> + <capability type='pci'> + <domain>0</domain> + <bus>4</bus> + <slot>0</slot> + <function>1</function> + <product id='0x10c9'>82576 Gigabit Network Connection</product> + <vendor id='0x8086'>Intel Corporation</vendor> + <iommuGroup number='15'> + <address domain='0x0000' bus='0x04' slot='0x00' function='0x1'/> + </iommuGroup> + <numa node='0'/> + <pci-express> + <link validity='cap' port='1' speed='2.5' width='2'/> + <link validity='sta' speed='2.5' width='2'/> + </pci-express> + </capability> +</device></pre> + + <p> + The XML format for a PCI device stays the same for any further + capabilities it supports, a single nested <code><capability></code> + element will be included for each capability the device supports. + </p> + + <h3><a name="SRIOVCap">SR-IOV capability</a></h3> + <p> + Single root input/output virtualization (SR-IOV) allows sharing of the + PCIe resources by multiple virtual environments. That is achieved by + slicing up a single full-featured physical resource called physical + function (PF) into multiple devices called virtual functions (VFs) sharing + their configuration with the underlying PF. Despite the SR-IOV + specification, the amount of VFs that can be created on a PF varies among + manufacturers.<br/> + <br/>
Replace double <br/> with </p> and <p>.
+ Suppose the NIC <a href="#PCI">above</a> was also SR-IOV capable, it would + also include a nested + <code><capability></code> element enumerating all virtual + functions available on the physical device (physical port) like in the + example below. + </p> + + <pre> +<capability type='pci'> +... + <capability type='virt_functions' maxCount='7'> + <address domain='0x0000' bus='0x04' slot='0x10' function='0x1'/> + <address domain='0x0000' bus='0x04' slot='0x10' function='0x3'/> + <address domain='0x0000' bus='0x04' slot='0x10' function='0x5'/> + <address domain='0x0000' bus='0x04' slot='0x10' function='0x7'/> + <address domain='0x0000' bus='0x04' slot='0x11' function='0x1'/> + <address domain='0x0000' bus='0x04' slot='0x11' function='0x3'/> + <address domain='0x0000' bus='0x04' slot='0x11' function='0x5'/> + </capability> +... +</capability></pre> + <p> + A SR-IOV child device on the other hand, would then report its top level + capability type as a physical function instead: + </p> + + <pre> +<device> +... + <capability type='phys_function'> + <address domain='0x0000' bus='0x04' slot='0x00' function='0x0'/> + </capability> +... +<device></pre> + + </body> +</html> -- 2.12.2
I'm not a native speaker but the text makes sense and having something is definitely better than having no documentation at all. ACK Pavel

On Mon, Apr 24, 2017 at 04:04:01PM +0200, Pavel Hrdina wrote:
On Thu, Apr 20, 2017 at 03:05:59PM +0200, Erik Skultety wrote:
There's lot more to document about the nodedev driver, besides PCI and SR-IOV (even this might need to be extended), but let's start small-ish and at least have a page for it linked from the drivers.html.
Signed-off-by: Erik Skultety <eskultet@redhat.com> --- docs/drivers.html.in | 6 +- docs/drvnodedev.html.in | 184 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 189 insertions(+), 1 deletion(-) create mode 100644 docs/drvnodedev.html.in
diff --git a/docs/drivers.html.in b/docs/drivers.html.in index be7483b9b..61993861e 100644 --- a/docs/drivers.html.in +++ b/docs/drivers.html.in @@ -4,7 +4,11 @@ <body> <h1>Internal drivers</h1>
- <ul id="toc"></ul> + <ul> + <li><a href="#hypervisor">Hypervisor drivers</a></li> + <li><a href="#storage">Storage drivers</a></li> + <li><a href="drvnodedev.html">Node device driver</a></li> + </ul>
<p> The libvirt public API delegates its implementation to one or diff --git a/docs/drvnodedev.html.in b/docs/drvnodedev.html.in new file mode 100644 index 000000000..ed185c3df --- /dev/null +++ b/docs/drvnodedev.html.in @@ -0,0 +1,184 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> + <body> + <h1>Host device management</h1> + + <p> + Libvirt provides management of both physical and virtual host devices + (historically also referred to as node devices) like USB, PCI, SCSI, and + network devices. This also includes various virtualization capabilities + which the aforementioned devices provide for utilization, for example + SR-IOV, NPIV, MDEV, DRM, etc. <br/> + <br/>
You should use </p> and <p> instead of double <br/> if we are using paragraphs.
+ The node device driver provides means to list and show details about host + devices (<code>virsh nodedev-list</code>, + <code>virsh nodedev-dumpxml</code>), which are generic and can be used + with all devices. It also provides means to create and destroy devices + (<code>virsh nodedev-create</code>, <code>virsh nodedev-destroy</code>) + which are meant to be used to create virtual devices, currently only + supported by NPIV + (<a href="http://wiki.libvirt.org/page/NPIV_in_libvirt">more info about NPIV)</a>). <br/> + <br/>
Same here.
+ Devices on the host system are arranged in a tree-like hierarchy, with + the root node being called <code>computer</code>. The node device driver + supports two backends to manage the devices, HAL and udev, with the former + being deprecated in favour of the latter.<br/>
Either remove the single <br/> or replace it with a pair of </p> and <p> to end current paragraph and start a new one.
+ The generic format of a host device XML can be seen below. + To identify a device both within the host and the device tree hierarchy, + the following elements are used: + </p> + <dl> + <dt><code>name</code></dt> + <dd> + The device's name will be generated by libvirt using the subsystem, + like pci and the device's sysfs basename. + </dd> + <dt><code>path</code></dt> + <dd> + Fully qualified sysfs path to the device. + </dd> + <dt><code>parent</code></dt> + <dd> + This element identifies the parent node in the device hierarchy. The + value of the element will correspond with the device parent's + <code>name</code> element or <code>computer</code> if the device does + not have any parent. + </dd> + <dt><code>driver</code></dt> + <dd> + This elements reports the driver in use for this device. The presence + of this element in the output XML depends on whether the underlying + device manager (most likely udev) exposes information about the + driver. + </dd> + <dt><code>capability</code></dt> + <dd> + Describes the device in terms of feature support. The element has one + mandatory attribute <code>type</code> the value of which determines + the type of the device. Currently recognized values for the attribute + are: + <code>system</code>, + <code>pci</code>, + <code>usb</code>, + <code>usb_device</code>, + <code>net</code>, + <code>scsi</code>, + <code>scsi_host</code> (<span class="since">Since 0.4.7</span>), + <code>fc_host</code>, + <code>vports</code>, + <code>scsi_target</code> (<span class="since">Since 0.7.3</span>), + <code>storage</code> (<span class="since">Since 1.0.4</span>), + <code>scsi_generic</code> (<span class="since">Since 1.0.7</span>), + <code>drm</code> (<span class="since">Since 3.1.0</span>), and + <code>mdev</code> (<span class="since">Since 3.2.0</span>). + This element can be nested in which case it further specifies a + device's capability. Refer to specific device types to see more values + for the <code>type</code> attribute which are exclusive. + </dd> + </dl> + + <h2>Basic structure of a node device</h2> + <pre> +<device> + <name>pci_0000_00_17_0</name> + <path>/sys/devices/pci0000:00/0000:00:17.0</path> + <parent>computer</parent> + <driver> + <name>ahci</name> + </driver> + <capability type='pci'> +... + </capability> +</device></pre> + + <ul id="toc"/> + + <h2><a name="PCI">PCI host devices</a></h2> + <dl> + <dt><code>capability</code></dt> + <dd> + When used as top level element, the supported values for the + <code>type</code> attribute are <code>pci</code> and + <code>phys_function</code> (see <a href="#SRIOVCap">SR-IOV below</a>). + </dd> + </dl> + <pre> +<device> + <name>pci_0000_04_00_1</name> + <path>/sys/devices/pci0000:00/0000:00:06.0/0000:04:00.1</path> + <parent>pci_0000_00_06_0</parent> + <driver> + <name>igb</name> + </driver> + <capability type='pci'> + <domain>0</domain> + <bus>4</bus> + <slot>0</slot> + <function>1</function> + <product id='0x10c9'>82576 Gigabit Network Connection</product> + <vendor id='0x8086'>Intel Corporation</vendor> + <iommuGroup number='15'> + <address domain='0x0000' bus='0x04' slot='0x00' function='0x1'/> + </iommuGroup> + <numa node='0'/> + <pci-express> + <link validity='cap' port='1' speed='2.5' width='2'/> + <link validity='sta' speed='2.5' width='2'/> + </pci-express> + </capability> +</device></pre> + + <p> + The XML format for a PCI device stays the same for any further + capabilities it supports, a single nested <code><capability></code> + element will be included for each capability the device supports. + </p> + + <h3><a name="SRIOVCap">SR-IOV capability</a></h3> + <p> + Single root input/output virtualization (SR-IOV) allows sharing of the + PCIe resources by multiple virtual environments. That is achieved by + slicing up a single full-featured physical resource called physical + function (PF) into multiple devices called virtual functions (VFs) sharing + their configuration with the underlying PF. Despite the SR-IOV + specification, the amount of VFs that can be created on a PF varies among + manufacturers.<br/> + <br/>
Replace double <br/> with </p> and <p>.
+ Suppose the NIC <a href="#PCI">above</a> was also SR-IOV capable, it would + also include a nested + <code><capability></code> element enumerating all virtual + functions available on the physical device (physical port) like in the + example below. + </p> + + <pre> +<capability type='pci'> +... + <capability type='virt_functions' maxCount='7'> + <address domain='0x0000' bus='0x04' slot='0x10' function='0x1'/> + <address domain='0x0000' bus='0x04' slot='0x10' function='0x3'/> + <address domain='0x0000' bus='0x04' slot='0x10' function='0x5'/> + <address domain='0x0000' bus='0x04' slot='0x10' function='0x7'/> + <address domain='0x0000' bus='0x04' slot='0x11' function='0x1'/> + <address domain='0x0000' bus='0x04' slot='0x11' function='0x3'/> + <address domain='0x0000' bus='0x04' slot='0x11' function='0x5'/> + </capability> +... +</capability></pre> + <p> + A SR-IOV child device on the other hand, would then report its top level + capability type as a physical function instead: + </p> + + <pre> +<device> +... + <capability type='phys_function'> + <address domain='0x0000' bus='0x04' slot='0x00' function='0x0'/> + </capability> +... +<device></pre> + + </body> +</html> -- 2.12.2
I'm not a native speaker but the text makes sense and having something is definitely better than having no documentation at all.
ACK
Pavel
If you remove all MDEV references you can push it right away.

I'm not a native speaker but the text makes sense and having something is definitely better than having no documentation at all.
ACK
Pavel
If you remove all MDEV references you can push it right away.
I did so and pushed the documentation stub. Thanks, Erik

Signed-off-by: Erik Skultety <eskultet@redhat.com> --- docs/drvnodedev.html.in | 164 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 162 insertions(+), 2 deletions(-) diff --git a/docs/drvnodedev.html.in b/docs/drvnodedev.html.in index ed185c3df..6dece3806 100644 --- a/docs/drvnodedev.html.in +++ b/docs/drvnodedev.html.in @@ -71,7 +71,7 @@ <code>storage</code> (<span class="since">Since 1.0.4</span>), <code>scsi_generic</code> (<span class="since">Since 1.0.7</span>), <code>drm</code> (<span class="since">Since 3.1.0</span>), and - <code>mdev</code> (<span class="since">Since 3.2.0</span>). + <code>mdev</code> (<span class="since">Since 3.3.0</span>). This element can be nested in which case it further specifies a device's capability. Refer to specific device types to see more values for the <code>type</code> attribute which are exclusive. @@ -168,7 +168,7 @@ </capability></pre> <p> A SR-IOV child device on the other hand, would then report its top level - capability type as a physical function instead: + capability type as <code>phys_function</code> instead: </p> <pre> @@ -180,5 +180,165 @@ ... <device></pre> + <h3><a name="MDEVCap">MDEV capability</a></h3> + <p> + A PCI device capable of creating mediated devices will include a nested + capability mdev which enumerates all the supported mdev types on the + physical device, along with the type attributes available through sysfs: + </p> + + <dl> + <dt><code>type</code></dt> + <dd> + This element describes a mediated device type which acts as an + abstract template defining a resource allocation for instances of this + device type. The element has one attribute <code>id</code> which holds + an official vendor-supplied identifier for the type. + <span class="since">Since 3.3.0</span> + </dd> + + <dt><code>name</code></dt> + <dd> + The <code>name</code> element holds a vendor-supplied code name for + the given mediated device type. This is an optional element. + <span class="since">Since 3.3.0</span> + </dd> + + <dt><code>deviceAPI</code></dt> + <dd> + The value of this element describes how an instance of the given type + will be presented to the guest by the VFIO framework. + <span class="since">Since 3.3.0</span> + </dd> + + <dt><code>availableInstances</code></dt> + <dd> + This element reports the current state of resource allocation. In other + words, how many instances of the given type can still be successfully + created on the physical device. + <span class="since">Since 3.3.0</span> + </dd> + </dl> + + <p> + For a more info about mediated devices, refer to the + <a href="#MDEV">paragraph below</a>. + </p> + +<pre> +<device> +... + <driver> + <name>nvidia</name> + </driver> + <capability type='pci'> +... + <capability type='mdev'> + <type id='nvidia-11'> + <name>GRID M60-0B</name> + <deviceAPI>vfio-pci</deviceAPI> + <availableInstances>16</availableInstances> + </type> + <!-- Here would come the rest of the available mdev types --> + </capability> +... + </capability> +</device></pre> + + <h2><a name="MDEV">Mediated devices (MDEVs)</a></h2> + <p> + Mediated devices (<span class="since">Since 3.3.0</span>) are software + devices defining resource allocation on the backing physical device which + in turn allows the parent physical device's resources to be divided into + several mediated devices, thus sharing the physical device's performance + among multiple guests. Unlike SR-IOV however, where a PCIe device appears + as multiple separate PCIe devices on the host's PCI bus, mediated devices + only appear on the mdev virtual bus. Therefore, no detach/reattach + procedure from/to the host driver procedure is involved even though + mediated devices are used in a direct device assignment manner.<br/> + + The following sub-elements and attributes are exposed within the + <code>capability</code> element: + </p> + + <dl> + <dt><code>type</code></dt> + <dd> + This element describes a mediated device type which acts as an + abstract template defining a resource allocation for instances of this + device type. The element has one attribute <code>id</code> which holds + an official vendor-supplied identifier for the type. + <span class="since">Since 3.3.0</span> + </dd> + + <dt><code>iommuGroup</code></dt> + <dd> + This element supports a single attribute <code>number</code> which holds + the IOMMU group number the mediated device belongs to. + <span class="since">Since 3.3.0</span> + </dd> + </dl> + + <h3>Example of a mediated device</h3> + <pre> +<device> + <name>mdev_4b20d080_1b54_4048_85b3_a6a62d165c01</name> + <path>/sys/devices/pci0000:00/0000:00:02.0/4b20d080-1b54-4048-85b3-a6a62d165c01</path> + <parent>pci_0000_06_00_0</parent> + <driver> + <name>vfio_mdev</name> + </driver> + <capability type='mdev'> + <type id='nvidia-11'/> + <iommuGroup number='12'/> + <capability/> +<device/></pre> + + <p> + The support of mediated device's framework in libvirt's node device driver + covers the following features: + </p> + + <ul> + <li> + list available mediated devices on the host + (<span class="since">Since 3.3.0</span>) + </li> + <li> + display device details + (<span class="since">Since 3.3.0</span>) + </li> + </ul> + + <p> + Because mediated devices are instantiated from vendor specific templates, + simply called 'types', information describing these types is contained + within the parent device's capabilities + (see the example in <a href="#PCI">PCI host devices</a>).<br/><br/> + + To see the supported mediated device types on a specific physical device + use the following: + </p> + + <pre> +$ ls /sys/class/mdev_bus/<device>/mdev_supported_types</pre> + + <p> + To manually instantiate a mediated device, use one of the following as a + reference: + </p> + + <pre> +$ uuidgen > /sys/class/mdev_bus/<device>/mdev_supported_types/<type>/create +... +$ echo <UUID> > /sys/class/mdev_bus/<device>/mdev_supported_types/<type>/create</pre> + + <p> + Manual removal of a mediated device is then performed as follows: + </p> + + <pre> +$ echo 1 > /sys/bus/mdev/devices/<uuid>/remove</pre> + </body> </html> -- 2.12.2
participants (3)
-
Erik Skultety
-
Ján Tomko
-
Pavel Hrdina