[libvirt] [PATCH v12 00/11] Write separate module for hostdev passthrough

These patches implements a separate module for hostdev passthrough so that it could be shared by different drivers and can maintain a global state of a host device. patch 1/11: pci/usb/scsi used_by should include driver and domain info to check conflict among different drivers. patch 2/11~5/11: extract qemu specific code outside, to prepare for hostdev common library patch 6/11: move hostdev functions from qemu_hostdev.c to common library and maintain insistent device state. patch 7/11: add a unit test for hostdev common library. patch 8/11: switch qemu driver to use the common library instead of its own hostdev passthrough APIs. patch 9/11: switch lxc driver to use the common library instead of its own hostdev passthrough APIs. patch 10/11: add a hostdev pci backend type for xen usage. patch 11/11: add pci passthrough to libxl driver. --- Changes to v11: * rebase to latest shareable scsi hostdev changes * split v11 1/6 (add hostdev common library): add some small patches to extract qemu specific codes outside first before adding hostdev common library patch, for easier review. Chunyan Liu (11): change used_by: specify both driver and domain qemu_hostdev: move cfg->relaxedACS as a flag qemu_hostdev: move COLD_BOOT as a flag qemu_hostdev: parse BACKEND_DEFAULT outside qemu_hostdev: add/remove share device outside add hostdev passthrough common library add unit test for hostdev common library change qemu driver to use hostdev common library change lxc driver to use hostdev common library add hostdev pci backend type for xen add pci passthrough for libxl driver .gitignore | 1 + docs/schemas/domaincommon.rng | 1 + po/POTFILES.in | 3 +- src/Makefile.am | 3 +- src/conf/domain_conf.c | 3 +- src/conf/domain_conf.h | 1 + src/libvirt_private.syms | 21 + src/libxl/libxl_conf.c | 63 + src/libxl/libxl_conf.h | 4 + src/libxl/libxl_domain.c | 9 + src/libxl/libxl_driver.c | 448 +++++- src/lxc/lxc_conf.h | 4 - src/lxc/lxc_driver.c | 47 +- src/lxc/lxc_hostdev.c | 413 ----- src/lxc/lxc_hostdev.h | 43 - src/lxc/lxc_process.c | 24 +- src/qemu/qemu_command.c | 4 +- src/qemu/qemu_conf.h | 11 +- src/qemu/qemu_domain.c | 22 + src/qemu/qemu_driver.c | 83 +- src/qemu/qemu_hostdev.c | 89 +- src/qemu/qemu_hostdev.h | 5 +- src/qemu/qemu_hotplug.c | 144 +- src/qemu/qemu_process.c | 61 +- src/util/virhostdev.c | 1787 ++++++++++++++++++++ src/util/virhostdev.h | 134 ++ src/util/virpci.c | 30 +- src/util/virpci.h | 9 +- src/util/virscsi.c | 30 +- src/util/virscsi.h | 7 +- src/util/virusb.c | 29 +- src/util/virusb.h | 8 +- tests/Makefile.am | 5 + .../qemuxml2argv-hostdev-pci-address.xml | 1 + .../qemuxml2argvdata/qemuxml2argv-net-hostdev.xml | 1 + tests/qemuxml2argvdata/qemuxml2argv-pci-rom.xml | 2 + tests/virhostdevtest.c | 473 ++++++ tests/virscsitest.c | 6 +- 38 files changed, 3292 insertions(+), 737 deletions(-) delete mode 100644 src/lxc/lxc_hostdev.c delete mode 100644 src/lxc/lxc_hostdev.h create mode 100644 src/util/virhostdev.c create mode 100644 src/util/virhostdev.h create mode 100644 tests/virhostdevtest.c

Add driver info to used_by, to avoid conflict among different drivers if there are more than one drivers existing and using the hostdev. Signed-off-by: Chunyan Liu <cyliu@suse.com> --- src/lxc/lxc_hostdev.c | 11 +++++++---- src/qemu/qemu_conf.h | 2 ++ src/qemu/qemu_driver.c | 8 ++++---- src/qemu/qemu_hostdev.c | 41 +++++++++++++++++++++++++---------------- src/util/virpci.c | 30 +++++++++++++++++++++++------- src/util/virpci.h | 9 ++++++--- src/util/virscsi.c | 30 ++++++++++++++++++++++-------- src/util/virscsi.h | 7 +++++-- src/util/virusb.c | 29 ++++++++++++++++++++++------- src/util/virusb.h | 8 ++++++-- tests/virscsitest.c | 6 +++--- 11 files changed, 125 insertions(+), 56 deletions(-) diff --git a/src/lxc/lxc_hostdev.c b/src/lxc/lxc_hostdev.c index 3b371fc..77ce965 100644 --- a/src/lxc/lxc_hostdev.c +++ b/src/lxc/lxc_hostdev.c @@ -60,7 +60,7 @@ virLXCUpdateActiveUsbHostdevs(virLXCDriverPtr driver, continue; } - virUSBDeviceSetUsedBy(usb, def->name); + virUSBDeviceSetUsedBy(usb, "QEMU", def->name); virObjectLock(driver->activeUsbHostdevs); if (virUSBDeviceListAdd(driver->activeUsbHostdevs, usb) < 0) { @@ -90,7 +90,9 @@ virLXCPrepareHostdevUSBDevices(virLXCDriverPtr driver, for (i = 0; i < count; i++) { virUSBDevicePtr usb = virUSBDeviceListGet(list, i); if ((tmp = virUSBDeviceListFind(driver->activeUsbHostdevs, usb))) { - const char *other_name = virUSBDeviceGetUsedBy(tmp); + const char *other_name = NULL; + const char *other_drvname = NULL; + virUSBDeviceGetUsedBy(tmp, &other_drvname, &other_name); if (other_name) virReportError(VIR_ERR_OPERATION_INVALID, @@ -103,7 +105,7 @@ virLXCPrepareHostdevUSBDevices(virLXCDriverPtr driver, goto error; } - virUSBDeviceSetUsedBy(usb, name); + virUSBDeviceSetUsedBy(usb, "QEMU", name); VIR_DEBUG("Adding %03d.%03d dom=%s to activeUsbHostdevs", virUSBDeviceGetBus(usb), virUSBDeviceGetDevno(usb), name); /* @@ -352,6 +354,7 @@ virLXCDomainReAttachHostUsbDevices(virLXCDriverPtr driver, virDomainHostdevDefPtr hostdev = hostdevs[i]; virUSBDevicePtr usb, tmp; const char *used_by = NULL; + const char *used_by_drvname = NULL; if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) continue; @@ -389,7 +392,7 @@ virLXCDomainReAttachHostUsbDevices(virLXCDriverPtr driver, continue; } - used_by = virUSBDeviceGetUsedBy(tmp); + virUSBDeviceGetUsedBy(tmp, &used_by_drvname, &used_by); if (STREQ_NULLABLE(used_by, name)) { VIR_DEBUG("Removing %03d.%03d dom=%s from activeUsbHostdevs", hostdev->source.subsys.u.usb.bus, diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index 1f44a76..158cc1a 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -53,6 +53,8 @@ # error "Port me" # endif +# define QEMU_DRIVER_NAME "QEMU" + typedef struct _virQEMUDriver virQEMUDriver; typedef virQEMUDriver *virQEMUDriverPtr; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 59e018d..1fe8992 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -97,8 +97,6 @@ #define VIR_FROM_THIS VIR_FROM_QEMU -#define QEMU_DRIVER_NAME "QEMU" - #define QEMU_NB_MEM_PARAM 3 #define QEMU_NB_BLOCK_IO_TUNE_PARAM 6 @@ -11325,7 +11323,9 @@ qemuNodeDeviceReAttach(virNodeDevicePtr dev) virObjectLock(driver->inactivePciHostdevs); other = virPCIDeviceListFind(driver->activePciHostdevs, pci); if (other) { - const char *other_name = virPCIDeviceGetUsedBy(other); + const char *other_name = NULL; + const char *other_drvname = NULL; + virPCIDeviceGetUsedBy(other, &other_drvname, &other_name); if (other_name) virReportError(VIR_ERR_OPERATION_INVALID, @@ -16684,7 +16684,7 @@ static virDriver qemuDriver = { static virStateDriver qemuStateDriver = { - .name = "QEMU", + .name = QEMU_DRIVER_NAME, .stateInitialize = qemuStateInitialize, .stateAutoStart = qemuStateAutoStart, .stateCleanup = qemuStateCleanup, diff --git a/src/qemu/qemu_hostdev.c b/src/qemu/qemu_hostdev.c index 1b16386..01c24f9 100644 --- a/src/qemu/qemu_hostdev.c +++ b/src/qemu/qemu_hostdev.c @@ -177,7 +177,7 @@ qemuUpdateActivePciHostdevs(virQEMUDriverPtr driver, goto cleanup; } - virPCIDeviceSetUsedBy(dev, def->name); + virPCIDeviceSetUsedBy(dev, QEMU_DRIVER_NAME, def->name); /* Setup the original states for the PCI device */ virPCIDeviceSetUnbindFromStub(dev, hostdev->origstates.states.pci.unbind_from_stub); @@ -230,7 +230,7 @@ qemuUpdateActiveUsbHostdevs(virQEMUDriverPtr driver, continue; } - virUSBDeviceSetUsedBy(usb, def->name); + virUSBDeviceSetUsedBy(usb, QEMU_DRIVER_NAME, def->name); if (virUSBDeviceListAdd(driver->activeUsbHostdevs, usb) < 0) { virUSBDeviceFree(usb); @@ -274,13 +274,13 @@ qemuUpdateActiveScsiHostdevs(virQEMUDriverPtr driver, goto cleanup; if ((tmp = virSCSIDeviceListFind(driver->activeScsiHostdevs, scsi))) { - if (virSCSIDeviceSetUsedBy(tmp, def->name) < 0) { + if (virSCSIDeviceSetUsedBy(tmp, QEMU_DRIVER_NAME, def->name) < 0) { virSCSIDeviceFree(scsi); goto cleanup; } virSCSIDeviceFree(scsi); } else { - if (virSCSIDeviceSetUsedBy(scsi, def->name) < 0 || + if (virSCSIDeviceSetUsedBy(scsi, QEMU_DRIVER_NAME, def->name) < 0 || virSCSIDeviceListAdd(driver->activeScsiHostdevs, scsi) < 0) { virSCSIDeviceFree(scsi); goto cleanup; @@ -693,7 +693,9 @@ qemuPrepareHostdevPCIDevices(virQEMUDriverPtr driver, * the dev is in list driver->activePciHostdevs. */ if ((other = virPCIDeviceListFind(driver->activePciHostdevs, dev))) { - const char *other_name = virPCIDeviceGetUsedBy(other); + const char *other_name = NULL; + const char *other_drvname = NULL; + virPCIDeviceGetUsedBy(other, &other_drvname, &other_name); if (other_name) virReportError(VIR_ERR_OPERATION_INVALID, @@ -766,7 +768,7 @@ qemuPrepareHostdevPCIDevices(virQEMUDriverPtr driver, activeDev = virPCIDeviceListFind(driver->activePciHostdevs, dev); if (activeDev) - virPCIDeviceSetUsedBy(activeDev, name); + virPCIDeviceSetUsedBy(activeDev, QEMU_DRIVER_NAME, name); } /* Loop 8: Now set the original states for hostdev def */ @@ -857,7 +859,9 @@ qemuPrepareHostdevUSBDevices(virQEMUDriverPtr driver, for (i = 0; i < count; i++) { virUSBDevicePtr usb = virUSBDeviceListGet(list, i); if ((tmp = virUSBDeviceListFind(driver->activeUsbHostdevs, usb))) { - const char *other_name = virUSBDeviceGetUsedBy(tmp); + const char *other_name = NULL; + const char *other_drvname = NULL; + virUSBDeviceGetUsedBy(tmp, &other_drvname, &other_name); if (other_name) virReportError(VIR_ERR_OPERATION_INVALID, @@ -870,7 +874,7 @@ qemuPrepareHostdevUSBDevices(virQEMUDriverPtr driver, goto error; } - virUSBDeviceSetUsedBy(usb, name); + virUSBDeviceSetUsedBy(usb, QEMU_DRIVER_NAME, name); VIR_DEBUG("Adding %03d.%03d dom=%s to activeUsbHostdevs", virUSBDeviceGetBus(usb), virUSBDeviceGetDevno(usb), name); /* @@ -1140,10 +1144,10 @@ qemuPrepareHostdevSCSIDevices(virQEMUDriverPtr driver, goto error; } - if (virSCSIDeviceSetUsedBy(tmp, name) < 0) + if (virSCSIDeviceSetUsedBy(tmp, QEMU_DRIVER_NAME, name) < 0) goto error; } else { - if (virSCSIDeviceSetUsedBy(scsi, name) < 0) + if (virSCSIDeviceSetUsedBy(scsi, QEMU_DRIVER_NAME, name) < 0) goto error; VIR_DEBUG("Adding %s to activeScsiHostdevs", virSCSIDeviceGetName(scsi)); @@ -1275,10 +1279,14 @@ qemuDomainReAttachHostdevDevices(virQEMUDriverPtr driver, * been used by this domain. */ activeDev = virPCIDeviceListFind(driver->activePciHostdevs, dev); - if (activeDev && - STRNEQ_NULLABLE(name, virPCIDeviceGetUsedBy(activeDev))) { - virPCIDeviceListDel(pcidevs, dev); - continue; + if (activeDev) { + const char *tmp_name = NULL; + const char *tmp_drvname = NULL; + virPCIDeviceGetUsedBy(activeDev, &tmp_drvname, &tmp_name); + if (STRNEQ_NULLABLE(name, tmp_name)) { + virPCIDeviceListDel(pcidevs, dev); + continue; + } } virPCIDeviceListDel(driver->activePciHostdevs, dev); @@ -1333,6 +1341,7 @@ qemuDomainReAttachHostUsbDevices(virQEMUDriverPtr driver, virDomainHostdevDefPtr hostdev = hostdevs[i]; virUSBDevicePtr usb, tmp; const char *used_by = NULL; + const char *used_by_drvname = NULL; if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) continue; @@ -1370,7 +1379,7 @@ qemuDomainReAttachHostUsbDevices(virQEMUDriverPtr driver, continue; } - used_by = virUSBDeviceGetUsedBy(tmp); + virUSBDeviceGetUsedBy(tmp, &used_by_drvname, &used_by); if (STREQ_NULLABLE(used_by, name)) { VIR_DEBUG("Removing %03d.%03d dom=%s from activeUsbHostdevs", hostdev->source.subsys.u.usb.bus, @@ -1445,7 +1454,7 @@ qemuDomainReAttachHostScsiDevices(virQEMUDriverPtr driver, hostdev->source.subsys.u.scsi.unit, name); - virSCSIDeviceListDel(driver->activeScsiHostdevs, tmp, name); + virSCSIDeviceListDel(driver->activeScsiHostdevs, tmp, QEMU_DRIVER_NAME, name); virSCSIDeviceFree(scsi); } virObjectUnlock(driver->activeScsiHostdevs); diff --git a/src/util/virpci.c b/src/util/virpci.c index 00d1064..4ea2bcf 100644 --- a/src/util/virpci.c +++ b/src/util/virpci.c @@ -59,7 +59,10 @@ struct _virPCIDevice { char name[PCI_ADDR_LEN]; /* domain:bus:slot.function */ char id[PCI_ID_LEN]; /* product vendor */ char *path; - const char *used_by; /* The domain which uses the device */ + + /* The driver:domain which uses the device */ + char *used_by_drvname; + char *used_by_domname; unsigned int pcie_cap_pos; unsigned int pci_pm_cap_pos; @@ -1635,6 +1638,8 @@ virPCIDeviceFree(virPCIDevicePtr dev) VIR_DEBUG("%s %s: freeing", dev->id, dev->name); VIR_FREE(dev->path); VIR_FREE(dev->stubDriver); + VIR_FREE(dev->used_by_drvname); + VIR_FREE(dev->used_by_domname); VIR_FREE(dev); } @@ -1704,16 +1709,27 @@ virPCIDeviceSetReprobe(virPCIDevicePtr dev, bool reprobe) dev->reprobe = reprobe; } -void -virPCIDeviceSetUsedBy(virPCIDevicePtr dev, const char *name) +int +virPCIDeviceSetUsedBy(virPCIDevicePtr dev, + const char *drv_name, + const char *dom_name) { - dev->used_by = name; + if (VIR_STRDUP(dev->used_by_drvname, drv_name) < 0) + return -1; + + if (VIR_STRDUP(dev->used_by_domname, dom_name) < 0) + return -1; + + return 0; } -const char * -virPCIDeviceGetUsedBy(virPCIDevicePtr dev) +void +virPCIDeviceGetUsedBy(virPCIDevicePtr dev, + const char **drv_name, + const char **dom_name) { - return dev->used_by; + *drv_name = dev->used_by_drvname; + *dom_name = dev->used_by_domname; } void virPCIDeviceReattachInit(virPCIDevicePtr pci) diff --git a/src/util/virpci.h b/src/util/virpci.h index ac6dae1..20ffe54 100644 --- a/src/util/virpci.h +++ b/src/util/virpci.h @@ -66,9 +66,12 @@ int virPCIDeviceSetStubDriver(virPCIDevicePtr dev, const char *driver) ATTRIBUTE_NONNULL(2); const char *virPCIDeviceGetStubDriver(virPCIDevicePtr dev); -void virPCIDeviceSetUsedBy(virPCIDevice *dev, - const char *used_by); -const char *virPCIDeviceGetUsedBy(virPCIDevice *dev); +int virPCIDeviceSetUsedBy(virPCIDevice *dev, + const char *drv_name, + const char *dom_name); +void virPCIDeviceGetUsedBy(virPCIDevice *dev, + const char **drv_name, + const char **dom_name); unsigned int virPCIDeviceGetUnbindFromStub(virPCIDevicePtr dev); void virPCIDeviceSetUnbindFromStub(virPCIDevice *dev, bool unbind); diff --git a/src/util/virscsi.c b/src/util/virscsi.c index acc3815..5e28328 100644 --- a/src/util/virscsi.c +++ b/src/util/virscsi.c @@ -47,6 +47,11 @@ /* For virReportOOMError() and virReportSystemError() */ #define VIR_FROM_THIS VIR_FROM_NONE +struct _virUsedByInfo { + char *drvname; /* which driver */ + char *domname; /* which domain */ +}; +typedef struct _virUsedByInfo *virUsedByInfoPtr; struct _virSCSIDevice { unsigned int adapter; @@ -57,7 +62,7 @@ struct _virSCSIDevice { char *name; /* adapter:bus:target:unit */ char *id; /* model:vendor */ char *sg_path; /* e.g. /dev/sg2 */ - char **used_by; /* name of the domains using this dev */ + virUsedByInfoPtr *used_by; /* driver:domain(s) using this dev */ size_t n_used_by; /* how many domains are using this dev */ bool readonly; @@ -274,19 +279,26 @@ virSCSIDeviceFree(virSCSIDevicePtr dev) VIR_FREE(dev->id); VIR_FREE(dev->name); VIR_FREE(dev->sg_path); - for (i = 0; i < dev->n_used_by; i++) + for (i = 0; i < dev->n_used_by; i++) { + VIR_FREE(dev->used_by[i]->drvname); + VIR_FREE(dev->used_by[i]->domname); VIR_FREE(dev->used_by[i]); + } VIR_FREE(dev->used_by); VIR_FREE(dev); } int virSCSIDeviceSetUsedBy(virSCSIDevicePtr dev, - const char *name) + const char *drvname, + const char *domname) { - char *copy = NULL; - - if (VIR_STRDUP(copy, name) < 0) + virUsedByInfoPtr copy; + if (VIR_ALLOC(copy) < 0) + return -1; + if (VIR_STRDUP(copy->drvname, drvname) < 0) + return -1; + if (VIR_STRDUP(copy->domname, domname) < 0) return -1; return VIR_APPEND_ELEMENT(dev->used_by, dev->n_used_by, copy); @@ -427,13 +439,15 @@ virSCSIDeviceListSteal(virSCSIDeviceListPtr list, void virSCSIDeviceListDel(virSCSIDeviceListPtr list, virSCSIDevicePtr dev, - const char *name) + const char *drvname, + const char *domname) { virSCSIDevicePtr tmp = NULL; size_t i; for (i = 0; i < dev->n_used_by; i++) { - if (STREQ_NULLABLE(dev->used_by[i], name)) { + if (STREQ_NULLABLE(dev->used_by[i]->drvname, drvname) && + STREQ_NULLABLE(dev->used_by[i]->domname, domname)) { if (dev->n_used_by > 1) { VIR_DELETE_ELEMENT(dev->used_by, i, dev->n_used_by); } else { diff --git a/src/util/virscsi.h b/src/util/virscsi.h index 1b685eb..c67837f 100644 --- a/src/util/virscsi.h +++ b/src/util/virscsi.h @@ -53,7 +53,9 @@ virSCSIDevicePtr virSCSIDeviceNew(const char *sysfs_prefix, bool shareable); void virSCSIDeviceFree(virSCSIDevicePtr dev); -int virSCSIDeviceSetUsedBy(virSCSIDevicePtr dev, const char *name); +int virSCSIDeviceSetUsedBy(virSCSIDevicePtr dev, + const char *drvname, + const char *domname); bool virSCSIDeviceIsAvailable(virSCSIDevicePtr dev); const char *virSCSIDeviceGetName(virSCSIDevicePtr dev); unsigned int virSCSIDeviceGetAdapter(virSCSIDevicePtr dev); @@ -87,7 +89,8 @@ virSCSIDevicePtr virSCSIDeviceListSteal(virSCSIDeviceListPtr list, virSCSIDevicePtr dev); void virSCSIDeviceListDel(virSCSIDeviceListPtr list, virSCSIDevicePtr dev, - const char *name); + const char *drvname, + const char *domname); virSCSIDevicePtr virSCSIDeviceListFind(virSCSIDeviceListPtr list, virSCSIDevicePtr dev); diff --git a/src/util/virusb.c b/src/util/virusb.c index bb5980d..4e22973 100644 --- a/src/util/virusb.c +++ b/src/util/virusb.c @@ -55,7 +55,10 @@ struct _virUSBDevice { char name[USB_ADDR_LEN]; /* domain:bus:slot.function */ char id[USB_ID_LEN]; /* product vendor */ char *path; - const char *used_by; /* name of the domain using this dev */ + + /* driver:domain using this dev */ + char *used_by_drvname; + char *used_by_domname; }; struct _virUSBDeviceList { @@ -375,19 +378,31 @@ virUSBDeviceFree(virUSBDevicePtr dev) return; VIR_DEBUG("%s %s: freeing", dev->id, dev->name); VIR_FREE(dev->path); + VIR_FREE(dev->used_by_drvname); + VIR_FREE(dev->used_by_domname); VIR_FREE(dev); } - -void virUSBDeviceSetUsedBy(virUSBDevicePtr dev, - const char *name) +int +virUSBDeviceSetUsedBy(virUSBDevicePtr dev, + const char *drv_name, + const char *dom_name) { - dev->used_by = name; + if (VIR_STRDUP(dev->used_by_drvname, drv_name) < 0) + return -1; + if (VIR_STRDUP(dev->used_by_domname, dom_name) < 0) + return -1; + + return 0; } -const char * virUSBDeviceGetUsedBy(virUSBDevicePtr dev) +void +virUSBDeviceGetUsedBy(virUSBDevicePtr dev, + const char **drv_name, + const char **dom_name) { - return dev->used_by; + *drv_name = dev->used_by_drvname; + *dom_name = dev->used_by_domname; } const char *virUSBDeviceGetName(virUSBDevicePtr dev) diff --git a/src/util/virusb.h b/src/util/virusb.h index e0a7c4c..f98ea21 100644 --- a/src/util/virusb.h +++ b/src/util/virusb.h @@ -60,8 +60,12 @@ int virUSBDeviceFind(unsigned int vendor, virUSBDevicePtr *usb); void virUSBDeviceFree(virUSBDevicePtr dev); -void virUSBDeviceSetUsedBy(virUSBDevicePtr dev, const char *name); -const char *virUSBDeviceGetUsedBy(virUSBDevicePtr dev); +int virUSBDeviceSetUsedBy(virUSBDevicePtr dev, + const char *drv_name, + const char *dom_name); +void virUSBDeviceGetUsedBy(virUSBDevicePtr dev, + const char **drv_name, + const char **dom_name); const char *virUSBDeviceGetName(virUSBDevicePtr dev); unsigned int virUSBDeviceGetBus(virUSBDevicePtr dev); diff --git a/tests/virscsitest.c b/tests/virscsitest.c index d4b3e4a..586c41b 100644 --- a/tests/virscsitest.c +++ b/tests/virscsitest.c @@ -91,13 +91,13 @@ test2(const void *data ATTRIBUTE_UNUSED) if (!virSCSIDeviceIsAvailable(dev)) goto cleanup; - if (virSCSIDeviceSetUsedBy(dev, "fc18") < 0) + if (virSCSIDeviceSetUsedBy(dev, "QEMU", "fc18") < 0) goto cleanup; if (virSCSIDeviceIsAvailable(dev)) goto cleanup; - if (virSCSIDeviceSetUsedBy(dev, "fc20") < 0) + if (virSCSIDeviceSetUsedBy(dev, "QEMU", "fc20") < 0) goto cleanup; if (virSCSIDeviceIsAvailable(dev)) @@ -117,7 +117,7 @@ test2(const void *data ATTRIBUTE_UNUSED) if (!virSCSIDeviceListFind(list, dev)) goto cleanup; - virSCSIDeviceListDel(list, dev, "fc20"); + virSCSIDeviceListDel(list, dev, "QEMU", "fc20"); if (!virSCSIDeviceListFind(list, dev)) goto cleanup; -- 1.6.0.2

Hello ChunYan, Good to see your patchset into smaller pieces. This patch looks almost OK for me, but there are problems you'll need to look into. On Mon, 2014-02-17 at 14:32 +0800, Chunyan Liu wrote:
Add driver info to used_by, to avoid conflict among different drivers if there are more than one drivers existing and using the hostdev.
Signed-off-by: Chunyan Liu <cyliu@suse.com> --- src/lxc/lxc_hostdev.c | 11 +++++++---- src/qemu/qemu_conf.h | 2 ++ src/qemu/qemu_driver.c | 8 ++++---- src/qemu/qemu_hostdev.c | 41 +++++++++++++++++++++++++---------------- src/util/virpci.c | 30 +++++++++++++++++++++++------- src/util/virpci.h | 9 ++++++--- src/util/virscsi.c | 30 ++++++++++++++++++++++-------- src/util/virscsi.h | 7 +++++-- src/util/virusb.c | 29 ++++++++++++++++++++++------- src/util/virusb.h | 8 ++++++-- tests/virscsitest.c | 6 +++--- 11 files changed, 125 insertions(+), 56 deletions(-)
diff --git a/src/lxc/lxc_hostdev.c b/src/lxc/lxc_hostdev.c index 3b371fc..77ce965 100644 --- a/src/lxc/lxc_hostdev.c +++ b/src/lxc/lxc_hostdev.c @@ -60,7 +60,7 @@ virLXCUpdateActiveUsbHostdevs(virLXCDriverPtr driver, continue; }
- virUSBDeviceSetUsedBy(usb, def->name); + virUSBDeviceSetUsedBy(usb, "QEMU", def->name);
You meant LXC_DRIVER_NAME rather than "QEMU" here, right?
virObjectLock(driver->activeUsbHostdevs); if (virUSBDeviceListAdd(driver->activeUsbHostdevs, usb) < 0) { @@ -90,7 +90,9 @@ virLXCPrepareHostdevUSBDevices(virLXCDriverPtr driver, for (i = 0; i < count; i++) { virUSBDevicePtr usb = virUSBDeviceListGet(list, i); if ((tmp = virUSBDeviceListFind(driver->activeUsbHostdevs, usb))) { - const char *other_name = virUSBDeviceGetUsedBy(tmp); + const char *other_name = NULL; + const char *other_drvname = NULL; + virUSBDeviceGetUsedBy(tmp, &other_drvname, &other_name);
if (other_name) virReportError(VIR_ERR_OPERATION_INVALID, @@ -103,7 +105,7 @@ virLXCPrepareHostdevUSBDevices(virLXCDriverPtr driver, goto error; }
- virUSBDeviceSetUsedBy(usb, name); + virUSBDeviceSetUsedBy(usb, "QEMU", name);
Same here, I guess that should be LXC_DRIVER_NAME, no?
VIR_DEBUG("Adding %03d.%03d dom=%s to activeUsbHostdevs", virUSBDeviceGetBus(usb), virUSBDeviceGetDevno(usb), name); /* @@ -352,6 +354,7 @@ virLXCDomainReAttachHostUsbDevices(virLXCDriverPtr driver, virDomainHostdevDefPtr hostdev = hostdevs[i]; virUSBDevicePtr usb, tmp; const char *used_by = NULL; + const char *used_by_drvname = NULL;
if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) continue; @@ -389,7 +392,7 @@ virLXCDomainReAttachHostUsbDevices(virLXCDriverPtr driver, continue; }
- used_by = virUSBDeviceGetUsedBy(tmp); + virUSBDeviceGetUsedBy(tmp, &used_by_drvname, &used_by);
Where is used_by_drvname used? If that really isn't used, then replacing it by NULL would be fine.
if (STREQ_NULLABLE(used_by, name)) { VIR_DEBUG("Removing %03d.%03d dom=%s from activeUsbHostdevs", hostdev->source.subsys.u.usb.bus, diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index 1f44a76..158cc1a 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -53,6 +53,8 @@ # error "Port me" # endif
+# define QEMU_DRIVER_NAME "QEMU" + typedef struct _virQEMUDriver virQEMUDriver; typedef virQEMUDriver *virQEMUDriverPtr;
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 59e018d..1fe8992 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -97,8 +97,6 @@
#define VIR_FROM_THIS VIR_FROM_QEMU
-#define QEMU_DRIVER_NAME "QEMU" - #define QEMU_NB_MEM_PARAM 3
#define QEMU_NB_BLOCK_IO_TUNE_PARAM 6 @@ -11325,7 +11323,9 @@ qemuNodeDeviceReAttach(virNodeDevicePtr dev) virObjectLock(driver->inactivePciHostdevs); other = virPCIDeviceListFind(driver->activePciHostdevs, pci); if (other) { - const char *other_name = virPCIDeviceGetUsedBy(other); + const char *other_name = NULL; + const char *other_drvname = NULL; + virPCIDeviceGetUsedBy(other, &other_drvname, &other_name);
Is other_drvname used anywhere? Seems not from the patch, so using NULL would surely be preferable.
if (other_name) virReportError(VIR_ERR_OPERATION_INVALID, @@ -16684,7 +16684,7 @@ static virDriver qemuDriver = {
static virStateDriver qemuStateDriver = { - .name = "QEMU", + .name = QEMU_DRIVER_NAME, .stateInitialize = qemuStateInitialize, .stateAutoStart = qemuStateAutoStart, .stateCleanup = qemuStateCleanup, diff --git a/src/qemu/qemu_hostdev.c b/src/qemu/qemu_hostdev.c index 1b16386..01c24f9 100644 --- a/src/qemu/qemu_hostdev.c +++ b/src/qemu/qemu_hostdev.c @@ -177,7 +177,7 @@ qemuUpdateActivePciHostdevs(virQEMUDriverPtr driver, goto cleanup;
} - virPCIDeviceSetUsedBy(dev, def->name); + virPCIDeviceSetUsedBy(dev, QEMU_DRIVER_NAME, def->name);
/* Setup the original states for the PCI device */ virPCIDeviceSetUnbindFromStub(dev, hostdev->origstates.states.pci.unbind_from_stub); @@ -230,7 +230,7 @@ qemuUpdateActiveUsbHostdevs(virQEMUDriverPtr driver, continue; }
- virUSBDeviceSetUsedBy(usb, def->name); + virUSBDeviceSetUsedBy(usb, QEMU_DRIVER_NAME, def->name);
if (virUSBDeviceListAdd(driver->activeUsbHostdevs, usb) < 0) { virUSBDeviceFree(usb); @@ -274,13 +274,13 @@ qemuUpdateActiveScsiHostdevs(virQEMUDriverPtr driver, goto cleanup;
if ((tmp = virSCSIDeviceListFind(driver->activeScsiHostdevs, scsi))) { - if (virSCSIDeviceSetUsedBy(tmp, def->name) < 0) { + if (virSCSIDeviceSetUsedBy(tmp, QEMU_DRIVER_NAME, def->name) < 0) { virSCSIDeviceFree(scsi); goto cleanup; } virSCSIDeviceFree(scsi); } else { - if (virSCSIDeviceSetUsedBy(scsi, def->name) < 0 || + if (virSCSIDeviceSetUsedBy(scsi, QEMU_DRIVER_NAME, def->name) < 0 || virSCSIDeviceListAdd(driver->activeScsiHostdevs, scsi) < 0) { virSCSIDeviceFree(scsi); goto cleanup; @@ -693,7 +693,9 @@ qemuPrepareHostdevPCIDevices(virQEMUDriverPtr driver, * the dev is in list driver->activePciHostdevs. */ if ((other = virPCIDeviceListFind(driver->activePciHostdevs, dev))) { - const char *other_name = virPCIDeviceGetUsedBy(other); + const char *other_name = NULL; + const char *other_drvname = NULL; + virPCIDeviceGetUsedBy(other, &other_drvname, &other_name);
Again, other_drvname seems to be unused.
if (other_name) virReportError(VIR_ERR_OPERATION_INVALID, @@ -766,7 +768,7 @@ qemuPrepareHostdevPCIDevices(virQEMUDriverPtr driver, activeDev = virPCIDeviceListFind(driver->activePciHostdevs, dev);
if (activeDev) - virPCIDeviceSetUsedBy(activeDev, name); + virPCIDeviceSetUsedBy(activeDev, QEMU_DRIVER_NAME, name); }
/* Loop 8: Now set the original states for hostdev def */ @@ -857,7 +859,9 @@ qemuPrepareHostdevUSBDevices(virQEMUDriverPtr driver, for (i = 0; i < count; i++) { virUSBDevicePtr usb = virUSBDeviceListGet(list, i); if ((tmp = virUSBDeviceListFind(driver->activeUsbHostdevs, usb))) { - const char *other_name = virUSBDeviceGetUsedBy(tmp); + const char *other_name = NULL; + const char *other_drvname = NULL; + virUSBDeviceGetUsedBy(tmp, &other_drvname, &other_name);
Here too.
if (other_name) virReportError(VIR_ERR_OPERATION_INVALID, @@ -870,7 +874,7 @@ qemuPrepareHostdevUSBDevices(virQEMUDriverPtr driver, goto error; }
- virUSBDeviceSetUsedBy(usb, name); + virUSBDeviceSetUsedBy(usb, QEMU_DRIVER_NAME, name); VIR_DEBUG("Adding %03d.%03d dom=%s to activeUsbHostdevs", virUSBDeviceGetBus(usb), virUSBDeviceGetDevno(usb), name); /* @@ -1140,10 +1144,10 @@ qemuPrepareHostdevSCSIDevices(virQEMUDriverPtr driver, goto error; }
- if (virSCSIDeviceSetUsedBy(tmp, name) < 0) + if (virSCSIDeviceSetUsedBy(tmp, QEMU_DRIVER_NAME, name) < 0) goto error; } else { - if (virSCSIDeviceSetUsedBy(scsi, name) < 0) + if (virSCSIDeviceSetUsedBy(scsi, QEMU_DRIVER_NAME, name) < 0) goto error;
VIR_DEBUG("Adding %s to activeScsiHostdevs", virSCSIDeviceGetName(scsi)); @@ -1275,10 +1279,14 @@ qemuDomainReAttachHostdevDevices(virQEMUDriverPtr driver, * been used by this domain. */ activeDev = virPCIDeviceListFind(driver->activePciHostdevs, dev); - if (activeDev && - STRNEQ_NULLABLE(name, virPCIDeviceGetUsedBy(activeDev))) { - virPCIDeviceListDel(pcidevs, dev); - continue; + if (activeDev) { + const char *tmp_name = NULL; + const char *tmp_drvname = NULL; + virPCIDeviceGetUsedBy(activeDev, &tmp_drvname, &tmp_name);
Is tmp_drvname actually used?
+ if (STRNEQ_NULLABLE(name, tmp_name)) { + virPCIDeviceListDel(pcidevs, dev); + continue; + } }
virPCIDeviceListDel(driver->activePciHostdevs, dev); @@ -1333,6 +1341,7 @@ qemuDomainReAttachHostUsbDevices(virQEMUDriverPtr driver, virDomainHostdevDefPtr hostdev = hostdevs[i]; virUSBDevicePtr usb, tmp; const char *used_by = NULL; + const char *used_by_drvname = NULL;
if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) continue; @@ -1370,7 +1379,7 @@ qemuDomainReAttachHostUsbDevices(virQEMUDriverPtr driver, continue; }
- used_by = virUSBDeviceGetUsedBy(tmp); + virUSBDeviceGetUsedBy(tmp, &used_by_drvname, &used_by);
Same here, used_by_drvname isn't used.
if (STREQ_NULLABLE(used_by, name)) { VIR_DEBUG("Removing %03d.%03d dom=%s from activeUsbHostdevs", hostdev->source.subsys.u.usb.bus, @@ -1445,7 +1454,7 @@ qemuDomainReAttachHostScsiDevices(virQEMUDriverPtr driver, hostdev->source.subsys.u.scsi.unit, name);
- virSCSIDeviceListDel(driver->activeScsiHostdevs, tmp, name); + virSCSIDeviceListDel(driver->activeScsiHostdevs, tmp, QEMU_DRIVER_NAME, name); virSCSIDeviceFree(scsi); } virObjectUnlock(driver->activeScsiHostdevs); diff --git a/src/util/virpci.c b/src/util/virpci.c index 00d1064..4ea2bcf 100644 --- a/src/util/virpci.c +++ b/src/util/virpci.c @@ -59,7 +59,10 @@ struct _virPCIDevice { char name[PCI_ADDR_LEN]; /* domain:bus:slot.function */ char id[PCI_ID_LEN]; /* product vendor */ char *path; - const char *used_by; /* The domain which uses the device */ + + /* The driver:domain which uses the device */ + char *used_by_drvname; + char *used_by_domname;
Why changing from const char* to char*?
unsigned int pcie_cap_pos; unsigned int pci_pm_cap_pos; @@ -1635,6 +1638,8 @@ virPCIDeviceFree(virPCIDevicePtr dev) VIR_DEBUG("%s %s: freeing", dev->id, dev->name); VIR_FREE(dev->path); VIR_FREE(dev->stubDriver); + VIR_FREE(dev->used_by_drvname); + VIR_FREE(dev->used_by_domname); VIR_FREE(dev); }
@@ -1704,16 +1709,27 @@ virPCIDeviceSetReprobe(virPCIDevicePtr dev, bool reprobe) dev->reprobe = reprobe; }
-void -virPCIDeviceSetUsedBy(virPCIDevicePtr dev, const char *name) +int +virPCIDeviceSetUsedBy(virPCIDevicePtr dev, + const char *drv_name, + const char *dom_name) { - dev->used_by = name; + if (VIR_STRDUP(dev->used_by_drvname, drv_name) < 0) + return -1; + if (VIR_STRDUP(dev->used_by_domname, dom_name) < 0) + return -1; +
You will leak memory here if there was already a value set, You need to free dev->used_by_drvname and dev->used_by_domname before VIR_STRDUP'ing to them. However, it would be much more convenient to keep const char*.
+ return 0; }
-const char * -virPCIDeviceGetUsedBy(virPCIDevicePtr dev) +void +virPCIDeviceGetUsedBy(virPCIDevicePtr dev, + const char **drv_name, + const char **dom_name) { - return dev->used_by; + *drv_name = dev->used_by_drvname; + *dom_name = dev->used_by_domname; }
void virPCIDeviceReattachInit(virPCIDevicePtr pci) diff --git a/src/util/virpci.h b/src/util/virpci.h index ac6dae1..20ffe54 100644 --- a/src/util/virpci.h +++ b/src/util/virpci.h @@ -66,9 +66,12 @@ int virPCIDeviceSetStubDriver(virPCIDevicePtr dev, const char *driver) ATTRIBUTE_NONNULL(2); const char *virPCIDeviceGetStubDriver(virPCIDevicePtr dev); -void virPCIDeviceSetUsedBy(virPCIDevice *dev, - const char *used_by); -const char *virPCIDeviceGetUsedBy(virPCIDevice *dev); +int virPCIDeviceSetUsedBy(virPCIDevice *dev, + const char *drv_name, + const char *dom_name); +void virPCIDeviceGetUsedBy(virPCIDevice *dev, + const char **drv_name, + const char **dom_name); unsigned int virPCIDeviceGetUnbindFromStub(virPCIDevicePtr dev); void virPCIDeviceSetUnbindFromStub(virPCIDevice *dev, bool unbind); diff --git a/src/util/virscsi.c b/src/util/virscsi.c index acc3815..5e28328 100644 --- a/src/util/virscsi.c +++ b/src/util/virscsi.c @@ -47,6 +47,11 @@
/* For virReportOOMError() and virReportSystemError() */ #define VIR_FROM_THIS VIR_FROM_NONE +struct _virUsedByInfo { + char *drvname; /* which driver */ + char *domname; /* which domain */ +}; +typedef struct _virUsedByInfo *virUsedByInfoPtr;
struct _virSCSIDevice { unsigned int adapter; @@ -57,7 +62,7 @@ struct _virSCSIDevice { char *name; /* adapter:bus:target:unit */ char *id; /* model:vendor */ char *sg_path; /* e.g. /dev/sg2 */ - char **used_by; /* name of the domains using this dev */ + virUsedByInfoPtr *used_by; /* driver:domain(s) using this dev */ size_t n_used_by; /* how many domains are using this dev */
bool readonly; @@ -274,19 +279,26 @@ virSCSIDeviceFree(virSCSIDevicePtr dev) VIR_FREE(dev->id); VIR_FREE(dev->name); VIR_FREE(dev->sg_path); - for (i = 0; i < dev->n_used_by; i++) + for (i = 0; i < dev->n_used_by; i++) { + VIR_FREE(dev->used_by[i]->drvname); + VIR_FREE(dev->used_by[i]->domname); VIR_FREE(dev->used_by[i]); + } VIR_FREE(dev->used_by); VIR_FREE(dev); }
int virSCSIDeviceSetUsedBy(virSCSIDevicePtr dev, - const char *name) + const char *drvname, + const char *domname) { - char *copy = NULL; - - if (VIR_STRDUP(copy, name) < 0) + virUsedByInfoPtr copy; + if (VIR_ALLOC(copy) < 0) + return -1; + if (VIR_STRDUP(copy->drvname, drvname) < 0) + return -1; + if (VIR_STRDUP(copy->domname, domname) < 0) return -1;
return VIR_APPEND_ELEMENT(dev->used_by, dev->n_used_by, copy); @@ -427,13 +439,15 @@ virSCSIDeviceListSteal(virSCSIDeviceListPtr list, void virSCSIDeviceListDel(virSCSIDeviceListPtr list, virSCSIDevicePtr dev, - const char *name) + const char *drvname, + const char *domname) { virSCSIDevicePtr tmp = NULL; size_t i;
for (i = 0; i < dev->n_used_by; i++) { - if (STREQ_NULLABLE(dev->used_by[i], name)) { + if (STREQ_NULLABLE(dev->used_by[i]->drvname, drvname) && + STREQ_NULLABLE(dev->used_by[i]->domname, domname)) { if (dev->n_used_by > 1) { VIR_DELETE_ELEMENT(dev->used_by, i, dev->n_used_by);
I'ld say that doesn't free everything. You'll need to VIR_FREE the dev->drvname and dev->domname before.
} else { diff --git a/src/util/virscsi.h b/src/util/virscsi.h index 1b685eb..c67837f 100644 --- a/src/util/virscsi.h +++ b/src/util/virscsi.h @@ -53,7 +53,9 @@ virSCSIDevicePtr virSCSIDeviceNew(const char *sysfs_prefix, bool shareable);
void virSCSIDeviceFree(virSCSIDevicePtr dev); -int virSCSIDeviceSetUsedBy(virSCSIDevicePtr dev, const char *name); +int virSCSIDeviceSetUsedBy(virSCSIDevicePtr dev, + const char *drvname, + const char *domname); bool virSCSIDeviceIsAvailable(virSCSIDevicePtr dev); const char *virSCSIDeviceGetName(virSCSIDevicePtr dev); unsigned int virSCSIDeviceGetAdapter(virSCSIDevicePtr dev); @@ -87,7 +89,8 @@ virSCSIDevicePtr virSCSIDeviceListSteal(virSCSIDeviceListPtr list, virSCSIDevicePtr dev); void virSCSIDeviceListDel(virSCSIDeviceListPtr list, virSCSIDevicePtr dev, - const char *name); + const char *drvname, + const char *domname); virSCSIDevicePtr virSCSIDeviceListFind(virSCSIDeviceListPtr list, virSCSIDevicePtr dev);
diff --git a/src/util/virusb.c b/src/util/virusb.c index bb5980d..4e22973 100644 --- a/src/util/virusb.c +++ b/src/util/virusb.c @@ -55,7 +55,10 @@ struct _virUSBDevice { char name[USB_ADDR_LEN]; /* domain:bus:slot.function */ char id[USB_ID_LEN]; /* product vendor */ char *path; - const char *used_by; /* name of the domain using this dev */ + + /* driver:domain using this dev */ + char *used_by_drvname; + char *used_by_domname; };
Again, why changing from const char* to char*? It will mostly lead to memory handling troubles.
struct _virUSBDeviceList { @@ -375,19 +378,31 @@ virUSBDeviceFree(virUSBDevicePtr dev) return; VIR_DEBUG("%s %s: freeing", dev->id, dev->name); VIR_FREE(dev->path); + VIR_FREE(dev->used_by_drvname); + VIR_FREE(dev->used_by_domname); VIR_FREE(dev); }
- -void virUSBDeviceSetUsedBy(virUSBDevicePtr dev, - const char *name) +int +virUSBDeviceSetUsedBy(virUSBDevicePtr dev, + const char *drv_name, + const char *dom_name) { - dev->used_by = name; + if (VIR_STRDUP(dev->used_by_drvname, drv_name) < 0) + return -1; + if (VIR_STRDUP(dev->used_by_domname, dom_name) < 0) + return -1;
What if there was already values set? There would be a memory leak: either use const char* or VIR_FREE before the VIR_STRDUP.
+ + return 0; }
-const char * virUSBDeviceGetUsedBy(virUSBDevicePtr dev) +void +virUSBDeviceGetUsedBy(virUSBDevicePtr dev, + const char **drv_name, + const char **dom_name) { - return dev->used_by; + *drv_name = dev->used_by_drvname; + *dom_name = dev->used_by_domname; }
const char *virUSBDeviceGetName(virUSBDevicePtr dev) diff --git a/src/util/virusb.h b/src/util/virusb.h index e0a7c4c..f98ea21 100644 --- a/src/util/virusb.h +++ b/src/util/virusb.h @@ -60,8 +60,12 @@ int virUSBDeviceFind(unsigned int vendor, virUSBDevicePtr *usb);
void virUSBDeviceFree(virUSBDevicePtr dev); -void virUSBDeviceSetUsedBy(virUSBDevicePtr dev, const char *name); -const char *virUSBDeviceGetUsedBy(virUSBDevicePtr dev); +int virUSBDeviceSetUsedBy(virUSBDevicePtr dev, + const char *drv_name, + const char *dom_name); +void virUSBDeviceGetUsedBy(virUSBDevicePtr dev, + const char **drv_name, + const char **dom_name); const char *virUSBDeviceGetName(virUSBDevicePtr dev);
unsigned int virUSBDeviceGetBus(virUSBDevicePtr dev); diff --git a/tests/virscsitest.c b/tests/virscsitest.c index d4b3e4a..586c41b 100644 --- a/tests/virscsitest.c +++ b/tests/virscsitest.c @@ -91,13 +91,13 @@ test2(const void *data ATTRIBUTE_UNUSED) if (!virSCSIDeviceIsAvailable(dev)) goto cleanup;
- if (virSCSIDeviceSetUsedBy(dev, "fc18") < 0) + if (virSCSIDeviceSetUsedBy(dev, "QEMU", "fc18") < 0) goto cleanup;
if (virSCSIDeviceIsAvailable(dev)) goto cleanup;
- if (virSCSIDeviceSetUsedBy(dev, "fc20") < 0) + if (virSCSIDeviceSetUsedBy(dev, "QEMU", "fc20") < 0) goto cleanup;
if (virSCSIDeviceIsAvailable(dev)) @@ -117,7 +117,7 @@ test2(const void *data ATTRIBUTE_UNUSED) if (!virSCSIDeviceListFind(list, dev)) goto cleanup;
- virSCSIDeviceListDel(list, dev, "fc20"); + virSCSIDeviceListDel(list, dev, "QEMU", "fc20");
if (!virSCSIDeviceListFind(list, dev)) goto cleanup;
May be some tests with the "LXC" value couldn't harm. -- Cedric

On 02/17/2014 10:31 AM, Cedric Bosdonnat wrote:
--- a/src/util/virpci.c
+++ b/src/util/virpci.c @@ -59,7 +59,10 @@ struct _virPCIDevice { char name[PCI_ADDR_LEN]; /* domain:bus:slot.function */ char id[PCI_ID_LEN]; /* product vendor */ char *path; - const char *used_by; /* The domain which uses the device */ + + /* The driver:domain which uses the device */ + char *used_by_drvname; + char *used_by_domname; Why changing from const char* to char*?
Just guessing that it's due to paranoia about the original string disappearing. I can possibly understand that for used_by_domname, but used_br_drvname is always a constant from a #define, so it's probably not necessary.

2014-02-17 16:31 GMT+08:00 Cedric Bosdonnat <cbosdonnat@suse.com>:
Hello ChunYan,
Good to see your patchset into smaller pieces. This patch looks almost OK for me, but there are problems you'll need to look into.
Add driver info to used_by, to avoid conflict among different drivers if
On Mon, 2014-02-17 at 14:32 +0800, Chunyan Liu wrote: there
are more than one drivers existing and using the hostdev.
Signed-off-by: Chunyan Liu <cyliu@suse.com> --- src/lxc/lxc_hostdev.c | 11 +++++++---- src/qemu/qemu_conf.h | 2 ++ src/qemu/qemu_driver.c | 8 ++++---- src/qemu/qemu_hostdev.c | 41 +++++++++++++++++++++++++---------------- src/util/virpci.c | 30 +++++++++++++++++++++++------- src/util/virpci.h | 9 ++++++--- src/util/virscsi.c | 30 ++++++++++++++++++++++-------- src/util/virscsi.h | 7 +++++-- src/util/virusb.c | 29 ++++++++++++++++++++++------- src/util/virusb.h | 8 ++++++-- tests/virscsitest.c | 6 +++--- 11 files changed, 125 insertions(+), 56 deletions(-)
diff --git a/src/lxc/lxc_hostdev.c b/src/lxc/lxc_hostdev.c index 3b371fc..77ce965 100644 --- a/src/lxc/lxc_hostdev.c +++ b/src/lxc/lxc_hostdev.c @@ -60,7 +60,7 @@ virLXCUpdateActiveUsbHostdevs(virLXCDriverPtr driver, continue; }
- virUSBDeviceSetUsedBy(usb, def->name); + virUSBDeviceSetUsedBy(usb, "QEMU", def->name);
You meant LXC_DRIVER_NAME rather than "QEMU" here, right?
virObjectLock(driver->activeUsbHostdevs); if (virUSBDeviceListAdd(driver->activeUsbHostdevs, usb) < 0) { @@ -90,7 +90,9 @@ virLXCPrepareHostdevUSBDevices(virLXCDriverPtr driver, for (i = 0; i < count; i++) { virUSBDevicePtr usb = virUSBDeviceListGet(list, i); if ((tmp = virUSBDeviceListFind(driver->activeUsbHostdevs,
usb))) {
- const char *other_name = virUSBDeviceGetUsedBy(tmp); + const char *other_name = NULL; + const char *other_drvname = NULL; + virUSBDeviceGetUsedBy(tmp, &other_drvname, &other_name);
if (other_name) virReportError(VIR_ERR_OPERATION_INVALID, @@ -103,7 +105,7 @@ virLXCPrepareHostdevUSBDevices(virLXCDriverPtr driver, goto error; }
- virUSBDeviceSetUsedBy(usb, name); + virUSBDeviceSetUsedBy(usb, "QEMU", name);
Same here, I guess that should be LXC_DRIVER_NAME, no?
VIR_DEBUG("Adding %03d.%03d dom=%s to activeUsbHostdevs", virUSBDeviceGetBus(usb), virUSBDeviceGetDevno(usb),
name);
/* @@ -352,6 +354,7 @@ virLXCDomainReAttachHostUsbDevices(virLXCDriverPtr
driver,
virDomainHostdevDefPtr hostdev = hostdevs[i]; virUSBDevicePtr usb, tmp; const char *used_by = NULL; + const char *used_by_drvname = NULL;
if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) continue; @@ -389,7 +392,7 @@ virLXCDomainReAttachHostUsbDevices(virLXCDriverPtr
driver,
continue; }
- used_by = virUSBDeviceGetUsedBy(tmp); + virUSBDeviceGetUsedBy(tmp, &used_by_drvname, &used_by);
Where is used_by_drvname used? If that really isn't used, then replacing it by NULL would be fine.
if (STREQ_NULLABLE(used_by, name)) { VIR_DEBUG("Removing %03d.%03d dom=%s from
activeUsbHostdevs",
hostdev->source.subsys.u.usb.bus, diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index 1f44a76..158cc1a 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -53,6 +53,8 @@ # error "Port me" # endif
+# define QEMU_DRIVER_NAME "QEMU" + typedef struct _virQEMUDriver virQEMUDriver; typedef virQEMUDriver *virQEMUDriverPtr;
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 59e018d..1fe8992 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -97,8 +97,6 @@
#define VIR_FROM_THIS VIR_FROM_QEMU
-#define QEMU_DRIVER_NAME "QEMU" - #define QEMU_NB_MEM_PARAM 3
#define QEMU_NB_BLOCK_IO_TUNE_PARAM 6 @@ -11325,7 +11323,9 @@ qemuNodeDeviceReAttach(virNodeDevicePtr dev) virObjectLock(driver->inactivePciHostdevs); other = virPCIDeviceListFind(driver->activePciHostdevs, pci); if (other) { - const char *other_name = virPCIDeviceGetUsedBy(other); + const char *other_name = NULL; + const char *other_drvname = NULL; + virPCIDeviceGetUsedBy(other, &other_drvname, &other_name);
Is other_drvname used any here? Seems not from the patch, so using
NULLwould surely be preferable. Not used in this patch since tmp_drvname check is always 'success' within the qemu code. It will be used when switching to using common library. I'll change it.
if (other_name) virReportError(VIR_ERR_OPERATION_INVALID, @@ -16684,7 +16684,7 @@ static virDriver qemuDriver = {
static virStateDriver qemuStateDriver = { - .name = "QEMU", + .name = QEMU_DRIVER_NAME, .stateInitialize = qemuStateInitialize, .stateAutoStart = qemuStateAutoStart, .stateCleanup = qemuStateCleanup, diff --git a/src/qemu/qemu_hostdev.c b/src/qemu/qemu_hostdev.c index 1b16386..01c24f9 100644 --- a/src/qemu/qemu_hostdev.c +++ b/src/qemu/qemu_hostdev.c @@ -177,7 +177,7 @@ qemuUpdateActivePciHostdevs(virQEMUDriverPtr driver, goto cleanup;
} - virPCIDeviceSetUsedBy(dev, def->name); + virPCIDeviceSetUsedBy(dev, QEMU_DRIVER_NAME, def->name);
/* Setup the original states for the PCI device */ virPCIDeviceSetUnbindFromStub(dev,
hostdev->origstates.states.pci.unbind_from_stub);
@@ -230,7 +230,7 @@ qemuUpdateActiveUsbHostdevs(virQEMUDriverPtr driver, continue; }
- virUSBDeviceSetUsedBy(usb, def->name); + virUSBDeviceSetUsedBy(usb, QEMU_DRIVER_NAME, def->name);
if (virUSBDeviceListAdd(driver->activeUsbHostdevs, usb) < 0) { virUSBDeviceFree(usb); @@ -274,13 +274,13 @@ qemuUpdateActiveScsiHostdevs(virQEMUDriverPtr driver, goto cleanup;
if ((tmp = virSCSIDeviceListFind(driver->activeScsiHostdevs, scsi))) { - if (virSCSIDeviceSetUsedBy(tmp, def->name) < 0) { + if (virSCSIDeviceSetUsedBy(tmp, QEMU_DRIVER_NAME, def->name) < 0) { virSCSIDeviceFree(scsi); goto cleanup; } virSCSIDeviceFree(scsi); } else { - if (virSCSIDeviceSetUsedBy(scsi, def->name) < 0 || + if (virSCSIDeviceSetUsedBy(scsi, QEMU_DRIVER_NAME, def->name) < 0 || virSCSIDeviceListAdd(driver->activeScsiHostdevs, scsi) < 0) { virSCSIDeviceFree(scsi); goto cleanup; @@ -693,7 +693,9 @@ qemuPrepareHostdevPCIDevices(virQEMUDriverPtr driver, * the dev is in list driver->activePciHostdevs. */ if ((other = virPCIDeviceListFind(driver->activePciHostdevs, dev))) { - const char *other_name = virPCIDeviceGetUsedBy(other); + const char *other_name = NULL; + const char *other_drvname = NULL; + virPCIDeviceGetUsedBy(other, &other_drvname, &other_name);
Again, other_drvname seems to be unused.
if (other_name) virReportError(VIR_ERR_OPERATION_INVALID, @@ -766,7 +768,7 @@ qemuPrepareHostdevPCIDevices(virQEMUDriverPtr driver, activeDev = virPCIDeviceListFind(driver->activePciHostdevs,
dev);
if (activeDev) - virPCIDeviceSetUsedBy(activeDev, name); + virPCIDeviceSetUsedBy(activeDev, QEMU_DRIVER_NAME, name); }
/* Loop 8: Now set the original states for hostdev def */ @@ -857,7 +859,9 @@ qemuPrepareHostdevUSBDevices(virQEMUDriverPtr driver, for (i = 0; i < count; i++) { virUSBDevicePtr usb = virUSBDeviceListGet(list, i); if ((tmp = virUSBDeviceListFind(driver->activeUsbHostdevs,
usb))) {
- const char *other_name = virUSBDeviceGetUsedBy(tmp); + const char *other_name = NULL; + const char *other_drvname = NULL; + virUSBDeviceGetUsedBy(tmp, &other_drvname, &other_name);
Here too.
if (other_name) virReportError(VIR_ERR_OPERATION_INVALID, @@ -870,7 +874,7 @@ qemuPrepareHostdevUSBDevices(virQEMUDriverPtr driver, goto error; }
- virUSBDeviceSetUsedBy(usb, name); + virUSBDeviceSetUsedBy(usb, QEMU_DRIVER_NAME, name); VIR_DEBUG("Adding %03d.%03d dom=%s to activeUsbHostdevs", virUSBDeviceGetBus(usb), virUSBDeviceGetDevno(usb),
name);
/* @@ -1140,10 +1144,10 @@ qemuPrepareHostdevSCSIDevices(virQEMUDriverPtr
driver,
goto error; }
- if (virSCSIDeviceSetUsedBy(tmp, name) < 0) + if (virSCSIDeviceSetUsedBy(tmp, QEMU_DRIVER_NAME, name) < 0) goto error; } else { - if (virSCSIDeviceSetUsedBy(scsi, name) < 0) + if (virSCSIDeviceSetUsedBy(scsi, QEMU_DRIVER_NAME, name) <
0)
goto error;
VIR_DEBUG("Adding %s to activeScsiHostdevs",
virSCSIDeviceGetName(scsi));
@@ -1275,10 +1279,14 @@ qemuDomainReAttachHostdevDevices(virQEMUDriverPtr driver, * been used by this domain. */ activeDev = virPCIDeviceListFind(driver->activePciHostdevs, dev); - if (activeDev && - STRNEQ_NULLABLE(name, virPCIDeviceGetUsedBy(activeDev))) { - virPCIDeviceListDel(pcidevs, dev); - continue; + if (activeDev) { + const char *tmp_name = NULL; + const char *tmp_drvname = NULL; + virPCIDeviceGetUsedBy(activeDev, &tmp_drvname, &tmp_name);
Is tmp_drvname actually used?
+ if (STRNEQ_NULLABLE(name, tmp_name)) { + virPCIDeviceListDel(pcidevs, dev); + continue; + } }
virPCIDeviceListDel(driver->activePciHostdevs, dev); @@ -1333,6 +1341,7 @@ qemuDomainReAttachHostUsbDevices(virQEMUDriverPtr driver, virDomainHostdevDefPtr hostdev = hostdevs[i]; virUSBDevicePtr usb, tmp; const char *used_by = NULL; + const char *used_by_drvname = NULL;
if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) continue; @@ -1370,7 +1379,7 @@ qemuDomainReAttachHostUsbDevices(virQEMUDriverPtr driver, continue; }
- used_by = virUSBDeviceGetUsedBy(tmp); + virUSBDeviceGetUsedBy(tmp, &used_by_drvname, &used_by);
Same here, used_by_drvname isn't used.
if (STREQ_NULLABLE(used_by, name)) { VIR_DEBUG("Removing %03d.%03d dom=%s from
activeUsbHostdevs",
hostdev->source.subsys.u.usb.bus, @@ -1445,7 +1454,7 @@ qemuDomainReAttachHostScsiDevices(virQEMUDriverPtr
driver,
hostdev->source.subsys.u.scsi.unit, name);
- virSCSIDeviceListDel(driver->activeScsiHostdevs, tmp, name); + virSCSIDeviceListDel(driver->activeScsiHostdevs, tmp,
QEMU_DRIVER_NAME, name);
virSCSIDeviceFree(scsi); } virObjectUnlock(driver->activeScsiHostdevs); diff --git a/src/util/virpci.c b/src/util/virpci.c index 00d1064..4ea2bcf 100644 --- a/src/util/virpci.c +++ b/src/util/virpci.c @@ -59,7 +59,10 @@ struct _virPCIDevice { char name[PCI_ADDR_LEN]; /* domain:bus:slot.function */ char id[PCI_ID_LEN]; /* product vendor */ char *path; - const char *used_by; /* The domain which uses the
device */
+ + /* The driver:domain which uses the device */ + char *used_by_drvname; + char *used_by_domname;
Why changing from const char* to char*?
used_by_domname is better to be char *, we can never be sure when someone uses this this API with a string that they free sooner than we expect. Much safer to strdup the parameter. And used_by_drvname is just a parallel. Will check and correct the memory leak. .
unsigned int pcie_cap_pos; unsigned int pci_pm_cap_pos; @@ -1635,6 +1638,8 @@ virPCIDeviceFree(virPCIDevicePtr dev) VIR_DEBUG("%s %s: freeing", dev->id, dev->name); VIR_FREE(dev->path); VIR_FREE(dev->stubDriver); + VIR_FREE(dev->used_by_drvname); + VIR_FREE(dev->used_by_domname); VIR_FREE(dev); }
@@ -1704,16 +1709,27 @@ virPCIDeviceSetReprobe(virPCIDevicePtr dev, bool
reprobe)
dev->reprobe = reprobe; }
-void -virPCIDeviceSetUsedBy(virPCIDevicePtr dev, const char *name) +int +virPCIDeviceSetUsedBy(virPCIDevicePtr dev, + const char *drv_name, + const char *dom_name) { - dev->used_by = name; + if (VIR_STRDUP(dev->used_by_drvname, drv_name) < 0) + return -1; + if (VIR_STRDUP(dev->used_by_domname, dom_name) < 0) + return -1; +
You will leak memory here if there was already a value set, You need to free dev->used_by_drvname and dev->used_by_domname before VIR_STRDUP'ing to them.
However, it would be much more convenient to keep const char*.
+ return 0; }
-const char * -virPCIDeviceGetUsedBy(virPCIDevicePtr dev) +void +virPCIDeviceGetUsedBy(virPCIDevicePtr dev, + const char **drv_name, + const char **dom_name) { - return dev->used_by; + *drv_name = dev->used_by_drvname; + *dom_name = dev->used_by_domname; }
void virPCIDeviceReattachInit(virPCIDevicePtr pci) diff --git a/src/util/virpci.h b/src/util/virpci.h index ac6dae1..20ffe54 100644 --- a/src/util/virpci.h +++ b/src/util/virpci.h @@ -66,9 +66,12 @@ int virPCIDeviceSetStubDriver(virPCIDevicePtr dev, const char *driver) ATTRIBUTE_NONNULL(2); const char *virPCIDeviceGetStubDriver(virPCIDevicePtr dev); -void virPCIDeviceSetUsedBy(virPCIDevice *dev, - const char *used_by); -const char *virPCIDeviceGetUsedBy(virPCIDevice *dev); +int virPCIDeviceSetUsedBy(virPCIDevice *dev, + const char *drv_name, + const char *dom_name); +void virPCIDeviceGetUsedBy(virPCIDevice *dev, + const char **drv_name, + const char **dom_name); unsigned int virPCIDeviceGetUnbindFromStub(virPCIDevicePtr dev); void virPCIDeviceSetUnbindFromStub(virPCIDevice *dev, bool unbind); diff --git a/src/util/virscsi.c b/src/util/virscsi.c index acc3815..5e28328 100644 --- a/src/util/virscsi.c +++ b/src/util/virscsi.c @@ -47,6 +47,11 @@
/* For virReportOOMError() and virReportSystemError() */ #define VIR_FROM_THIS VIR_FROM_NONE +struct _virUsedByInfo { + char *drvname; /* which driver */ + char *domname; /* which domain */ +}; +typedef struct _virUsedByInfo *virUsedByInfoPtr;
struct _virSCSIDevice { unsigned int adapter; @@ -57,7 +62,7 @@ struct _virSCSIDevice { char *name; /* adapter:bus:target:unit */ char *id; /* model:vendor */ char *sg_path; /* e.g. /dev/sg2 */ - char **used_by; /* name of the domains using this dev */ + virUsedByInfoPtr *used_by; /* driver:domain(s) using this dev */ size_t n_used_by; /* how many domains are using this dev */
bool readonly; @@ -274,19 +279,26 @@ virSCSIDeviceFree(virSCSIDevicePtr dev) VIR_FREE(dev->id); VIR_FREE(dev->name); VIR_FREE(dev->sg_path); - for (i = 0; i < dev->n_used_by; i++) + for (i = 0; i < dev->n_used_by; i++) { + VIR_FREE(dev->used_by[i]->drvname); + VIR_FREE(dev->used_by[i]->domname); VIR_FREE(dev->used_by[i]); + } VIR_FREE(dev->used_by); VIR_FREE(dev); }
int virSCSIDeviceSetUsedBy(virSCSIDevicePtr dev, - const char *name) + const char *drvname, + const char *domname) { - char *copy = NULL; - - if (VIR_STRDUP(copy, name) < 0) + virUsedByInfoPtr copy; + if (VIR_ALLOC(copy) < 0) + return -1; + if (VIR_STRDUP(copy->drvname, drvname) < 0) + return -1; + if (VIR_STRDUP(copy->domname, domname) < 0) return -1;
return VIR_APPEND_ELEMENT(dev->used_by, dev->n_used_by, copy); @@ -427,13 +439,15 @@ virSCSIDeviceListSteal(virSCSIDeviceListPtr list, void virSCSIDeviceListDel(virSCSIDeviceListPtr list, virSCSIDevicePtr dev, - const char *name) + const char *drvname, + const char *domname) { virSCSIDevicePtr tmp = NULL; size_t i;
for (i = 0; i < dev->n_used_by; i++) { - if (STREQ_NULLABLE(dev->used_by[i], name)) { + if (STREQ_NULLABLE(dev->used_by[i]->drvname, drvname) && + STREQ_NULLABLE(dev->used_by[i]->domname, domname)) { if (dev->n_used_by > 1) { VIR_DELETE_ELEMENT(dev->used_by, i, dev->n_used_by);
I'ld say that doesn't free everything. You'll need to VIR_FREE the dev->drvname and dev->domname before.
} else { diff --git a/src/util/virscsi.h b/src/util/virscsi.h index 1b685eb..c67837f 100644 --- a/src/util/virscsi.h +++ b/src/util/virscsi.h @@ -53,7 +53,9 @@ virSCSIDevicePtr virSCSIDeviceNew(const char
*sysfs_prefix,
bool shareable);
void virSCSIDeviceFree(virSCSIDevicePtr dev); -int virSCSIDeviceSetUsedBy(virSCSIDevicePtr dev, const char *name); +int virSCSIDeviceSetUsedBy(virSCSIDevicePtr dev, + const char *drvname, + const char *domname); bool virSCSIDeviceIsAvailable(virSCSIDevicePtr dev); const char *virSCSIDeviceGetName(virSCSIDevicePtr dev); unsigned int virSCSIDeviceGetAdapter(virSCSIDevicePtr dev); @@ -87,7 +89,8 @@ virSCSIDevicePtr
virSCSIDeviceListSteal(virSCSIDeviceListPtr list,
virSCSIDevicePtr dev); void virSCSIDeviceListDel(virSCSIDeviceListPtr list, virSCSIDevicePtr dev, - const char *name); + const char *drvname, + const char *domname); virSCSIDevicePtr virSCSIDeviceListFind(virSCSIDeviceListPtr list, virSCSIDevicePtr dev);
diff --git a/src/util/virusb.c b/src/util/virusb.c index bb5980d..4e22973 100644 --- a/src/util/virusb.c +++ b/src/util/virusb.c @@ -55,7 +55,10 @@ struct _virUSBDevice { char name[USB_ADDR_LEN]; /* domain:bus:slot.function */ char id[USB_ID_LEN]; /* product vendor */ char *path; - const char *used_by; /* name of the domain using this
dev */
+ + /* driver:domain using this dev */ + char *used_by_drvname; + char *used_by_domname; };
Again, why changing from const char* to char*? It will mostly lead to memory handling troubles.
struct _virUSBDeviceList { @@ -375,19 +378,31 @@ virUSBDeviceFree(virUSBDevicePtr dev) return; VIR_DEBUG("%s %s: freeing", dev->id, dev->name); VIR_FREE(dev->path); + VIR_FREE(dev->used_by_drvname); + VIR_FREE(dev->used_by_domname); VIR_FREE(dev); }
- -void virUSBDeviceSetUsedBy(virUSBDevicePtr dev, - const char *name) +int +virUSBDeviceSetUsedBy(virUSBDevicePtr dev, + const char *drv_name, + const char *dom_name) { - dev->used_by = name; + if (VIR_STRDUP(dev->used_by_drvname, drv_name) < 0) + return -1; + if (VIR_STRDUP(dev->used_by_domname, dom_name) < 0) + return -1;
What if there was already values set? There would be a memory leak: either use const char* or VIR_FREE before the VIR_STRDUP.
+ + return 0; }
-const char * virUSBDeviceGetUsedBy(virUSBDevicePtr dev) +void +virUSBDeviceGetUsedBy(virUSBDevicePtr dev, + const char **drv_name, + const char **dom_name) { - return dev->used_by; + *drv_name = dev->used_by_drvname; + *dom_name = dev->used_by_domname; }
const char *virUSBDeviceGetName(virUSBDevicePtr dev) diff --git a/src/util/virusb.h b/src/util/virusb.h index e0a7c4c..f98ea21 100644 --- a/src/util/virusb.h +++ b/src/util/virusb.h @@ -60,8 +60,12 @@ int virUSBDeviceFind(unsigned int vendor, virUSBDevicePtr *usb);
void virUSBDeviceFree(virUSBDevicePtr dev); -void virUSBDeviceSetUsedBy(virUSBDevicePtr dev, const char *name); -const char *virUSBDeviceGetUsedBy(virUSBDevicePtr dev); +int virUSBDeviceSetUsedBy(virUSBDevicePtr dev, + const char *drv_name, + const char *dom_name); +void virUSBDeviceGetUsedBy(virUSBDevicePtr dev, + const char **drv_name, + const char **dom_name); const char *virUSBDeviceGetName(virUSBDevicePtr dev);
unsigned int virUSBDeviceGetBus(virUSBDevicePtr dev); diff --git a/tests/virscsitest.c b/tests/virscsitest.c index d4b3e4a..586c41b 100644 --- a/tests/virscsitest.c +++ b/tests/virscsitest.c @@ -91,13 +91,13 @@ test2(const void *data ATTRIBUTE_UNUSED) if (!virSCSIDeviceIsAvailable(dev)) goto cleanup;
- if (virSCSIDeviceSetUsedBy(dev, "fc18") < 0) + if (virSCSIDeviceSetUsedBy(dev, "QEMU", "fc18") < 0) goto cleanup;
if (virSCSIDeviceIsAvailable(dev)) goto cleanup;
- if (virSCSIDeviceSetUsedBy(dev, "fc20") < 0) + if (virSCSIDeviceSetUsedBy(dev, "QEMU", "fc20") < 0) goto cleanup;
if (virSCSIDeviceIsAvailable(dev)) @@ -117,7 +117,7 @@ test2(const void *data ATTRIBUTE_UNUSED) if (!virSCSIDeviceListFind(list, dev)) goto cleanup;
- virSCSIDeviceListDel(list, dev, "fc20"); + virSCSIDeviceListDel(list, dev, "QEMU", "fc20");
if (!virSCSIDeviceListFind(list, dev)) goto cleanup;
May be some tests with the "LXC" value couldn't harm.
-- Cedric

On Mon, Feb 17, 2014 at 09:31:36AM +0100, Cedric Bosdonnat wrote:
diff --git a/src/util/virpci.c b/src/util/virpci.c index 00d1064..4ea2bcf 100644 --- a/src/util/virpci.c +++ b/src/util/virpci.c @@ -59,7 +59,10 @@ struct _virPCIDevice { char name[PCI_ADDR_LEN]; /* domain:bus:slot.function */ char id[PCI_ID_LEN]; /* product vendor */ char *path; - const char *used_by; /* The domain which uses the device */ + + /* The driver:domain which uses the device */ + char *used_by_drvname; + char *used_by_domname;
Why changing from const char* to char*?
I requested this change previously. These values are passed into this class by its callers. It is bad design practice to assume you can keep a reference to pointers that are passed into you - they could be free'd by the caller at any time. strdup'ing them provides a safer API design. Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

For extracting hostdev codes from qemu_hostdev.c to common library, change qemu specific cfg->relaxedACS handling to be a flag, and pass it to hostdev functions. Signed-off-by: Chunyan Liu <cyliu@suse.com> --- src/qemu/qemu_hostdev.c | 11 +++++++---- src/qemu/qemu_hostdev.h | 10 ++++++++-- src/qemu/qemu_hotplug.c | 7 ++++++- src/qemu/qemu_process.c | 5 ++++- 4 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/qemu/qemu_hostdev.c b/src/qemu/qemu_hostdev.c index 01c24f9..0d313c0 100644 --- a/src/qemu/qemu_hostdev.c +++ b/src/qemu/qemu_hostdev.c @@ -650,7 +650,8 @@ qemuPrepareHostdevPCIDevices(virQEMUDriverPtr driver, const unsigned char *uuid, virDomainHostdevDefPtr *hostdevs, int nhostdevs, - virQEMUCapsPtr qemuCaps) + virQEMUCapsPtr qemuCaps, + unsigned int flags) { virPCIDeviceListPtr pcidevs = NULL; int last_processed_hostdev_vf = -1; @@ -682,8 +683,9 @@ qemuPrepareHostdevPCIDevices(virQEMUDriverPtr driver, for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) { virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i); virPCIDevicePtr other; + bool strict_acs_check = !!(flags & VIR_STRICT_ACS_CHECK); - if (!virPCIDeviceIsAssignable(dev, !cfg->relaxedACS)) { + if (!virPCIDeviceIsAssignable(dev, strict_acs_check)) { virReportError(VIR_ERR_OPERATION_INVALID, _("PCI device %s is not assignable"), virPCIDeviceGetName(dev)); @@ -1187,14 +1189,15 @@ int qemuPrepareHostDevices(virQEMUDriverPtr driver, virDomainDefPtr def, virQEMUCapsPtr qemuCaps, - bool coldBoot) + bool coldBoot, + unsigned int flags) { if (!def->nhostdevs) return 0; if (qemuPrepareHostdevPCIDevices(driver, def->name, def->uuid, def->hostdevs, def->nhostdevs, - qemuCaps) < 0) + qemuCaps, flags) < 0) return -1; if (qemuPrepareHostUSBDevices(driver, def, coldBoot) < 0) diff --git a/src/qemu/qemu_hostdev.h b/src/qemu/qemu_hostdev.h index ffb3167..ab7fb9f 100644 --- a/src/qemu/qemu_hostdev.h +++ b/src/qemu/qemu_hostdev.h @@ -27,6 +27,10 @@ # include "qemu_conf.h" # include "domain_conf.h" +typedef enum { + VIR_STRICT_ACS_CHECK = (1 << 0), /* strict acs check */ +} qemuHostdevFlag; + int qemuUpdateActivePciHostdevs(virQEMUDriverPtr driver, virDomainDefPtr def); int qemuUpdateActiveUsbHostdevs(virQEMUDriverPtr driver, @@ -40,7 +44,8 @@ int qemuPrepareHostdevPCIDevices(virQEMUDriverPtr driver, const unsigned char *uuid, virDomainHostdevDefPtr *hostdevs, int nhostdevs, - virQEMUCapsPtr qemuCaps); + virQEMUCapsPtr qemuCaps, + unsigned int flags); int qemuFindHostdevUSBDevice(virDomainHostdevDefPtr hostdev, bool mandatory, virUSBDevicePtr *usb); @@ -54,7 +59,8 @@ int qemuPrepareHostdevSCSIDevices(virQEMUDriverPtr driver, int qemuPrepareHostDevices(virQEMUDriverPtr driver, virDomainDefPtr def, virQEMUCapsPtr qemuCaps, - bool coldBoot); + bool coldBoot, + unsigned int flags); void qemuDomainReAttachHostScsiDevices(virQEMUDriverPtr driver, const char *name, virDomainHostdevDefPtr *hostdevs, diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 7066be6..c47c5e8 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -1154,12 +1154,16 @@ qemuDomainAttachHostPciDevice(virQEMUDriverPtr driver, bool teardownlabel = false; int backend; unsigned long long memKB; + virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); + unsigned int flags = 0; if (VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs + 1) < 0) return -1; + if (!cfg->relaxedACS) + flags |= VIR_STRICT_ACS_CHECK; if (qemuPrepareHostdevPCIDevices(driver, vm->def->name, vm->def->uuid, - &hostdev, 1, priv->qemuCaps) < 0) + &hostdev, 1, priv->qemuCaps, flags) < 0) return -1; /* this could have been changed by qemuPrepareHostdevPCIDevices */ @@ -1254,6 +1258,7 @@ qemuDomainAttachHostPciDevice(virQEMUDriverPtr driver, VIR_FREE(devstr); VIR_FREE(configfd_name); VIR_FORCE_CLOSE(configfd); + virObjectUnref(cfg); return 0; diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 33d2a77..c0f0719 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -3596,6 +3596,7 @@ int qemuProcessStart(virConnectPtr conn, unsigned int stop_flags; virQEMUDriverConfigPtr cfg; virCapsPtr caps = NULL; + unsigned int hostdev_flags = 0; VIR_DEBUG("vm=%p name=%s id=%d pid=%llu", vm, vm->def->name, vm->def->id, @@ -3685,8 +3686,10 @@ int qemuProcessStart(virConnectPtr conn, /* Must be run before security labelling */ VIR_DEBUG("Preparing host devices"); + if (!cfg->relaxedACS) + hostdev_flags |= VIR_STRICT_ACS_CHECK; if (qemuPrepareHostDevices(driver, vm->def, priv->qemuCaps, - !migrateFrom) < 0) + !migrateFrom, hostdev_flags) < 0) goto cleanup; VIR_DEBUG("Preparing chr devices"); -- 1.6.0.2

On Mon, 2014-02-17 at 14:32 +0800, Chunyan Liu wrote:
For extracting hostdev codes from qemu_hostdev.c to common library, change qemu specific cfg->relaxedACS handling to be a flag, and pass it to hostdev functions.
Signed-off-by: Chunyan Liu <cyliu@suse.com> --- src/qemu/qemu_hostdev.c | 11 +++++++---- src/qemu/qemu_hostdev.h | 10 ++++++++-- src/qemu/qemu_hotplug.c | 7 ++++++- src/qemu/qemu_process.c | 5 ++++- 4 files changed, 25 insertions(+), 8 deletions(-)
diff --git a/src/qemu/qemu_hostdev.c b/src/qemu/qemu_hostdev.c index 01c24f9..0d313c0 100644 --- a/src/qemu/qemu_hostdev.c +++ b/src/qemu/qemu_hostdev.c @@ -650,7 +650,8 @@ qemuPrepareHostdevPCIDevices(virQEMUDriverPtr driver, const unsigned char *uuid, virDomainHostdevDefPtr *hostdevs, int nhostdevs, - virQEMUCapsPtr qemuCaps) + virQEMUCapsPtr qemuCaps, + unsigned int flags) { virPCIDeviceListPtr pcidevs = NULL; int last_processed_hostdev_vf = -1; @@ -682,8 +683,9 @@ qemuPrepareHostdevPCIDevices(virQEMUDriverPtr driver, for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) { virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i); virPCIDevicePtr other; + bool strict_acs_check = !!(flags & VIR_STRICT_ACS_CHECK);
Wouldn't that be more readable to have VIR_RELAXED_ACS_CHECK instead? It wouldn't change the logic.
- if (!virPCIDeviceIsAssignable(dev, !cfg->relaxedACS)) { + if (!virPCIDeviceIsAssignable(dev, strict_acs_check)) { virReportError(VIR_ERR_OPERATION_INVALID, _("PCI device %s is not assignable"), virPCIDeviceGetName(dev)); @@ -1187,14 +1189,15 @@ int qemuPrepareHostDevices(virQEMUDriverPtr driver, virDomainDefPtr def, virQEMUCapsPtr qemuCaps, - bool coldBoot) + bool coldBoot, + unsigned int flags) { if (!def->nhostdevs) return 0;
if (qemuPrepareHostdevPCIDevices(driver, def->name, def->uuid, def->hostdevs, def->nhostdevs, - qemuCaps) < 0) + qemuCaps, flags) < 0) return -1;
if (qemuPrepareHostUSBDevices(driver, def, coldBoot) < 0) diff --git a/src/qemu/qemu_hostdev.h b/src/qemu/qemu_hostdev.h index ffb3167..ab7fb9f 100644 --- a/src/qemu/qemu_hostdev.h +++ b/src/qemu/qemu_hostdev.h @@ -27,6 +27,10 @@ # include "qemu_conf.h" # include "domain_conf.h"
+typedef enum { + VIR_STRICT_ACS_CHECK = (1 << 0), /* strict acs check */ +} qemuHostdevFlag; + int qemuUpdateActivePciHostdevs(virQEMUDriverPtr driver, virDomainDefPtr def); int qemuUpdateActiveUsbHostdevs(virQEMUDriverPtr driver, @@ -40,7 +44,8 @@ int qemuPrepareHostdevPCIDevices(virQEMUDriverPtr driver, const unsigned char *uuid, virDomainHostdevDefPtr *hostdevs, int nhostdevs, - virQEMUCapsPtr qemuCaps); + virQEMUCapsPtr qemuCaps, + unsigned int flags); int qemuFindHostdevUSBDevice(virDomainHostdevDefPtr hostdev, bool mandatory, virUSBDevicePtr *usb); @@ -54,7 +59,8 @@ int qemuPrepareHostdevSCSIDevices(virQEMUDriverPtr driver, int qemuPrepareHostDevices(virQEMUDriverPtr driver, virDomainDefPtr def, virQEMUCapsPtr qemuCaps, - bool coldBoot); + bool coldBoot, + unsigned int flags); void qemuDomainReAttachHostScsiDevices(virQEMUDriverPtr driver, const char *name, virDomainHostdevDefPtr *hostdevs, diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 7066be6..c47c5e8 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -1154,12 +1154,16 @@ qemuDomainAttachHostPciDevice(virQEMUDriverPtr driver, bool teardownlabel = false; int backend; unsigned long long memKB; + virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); + unsigned int flags = 0;
if (VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs + 1) < 0) return -1;
+ if (!cfg->relaxedACS) + flags |= VIR_STRICT_ACS_CHECK; if (qemuPrepareHostdevPCIDevices(driver, vm->def->name, vm->def->uuid, - &hostdev, 1, priv->qemuCaps) < 0) + &hostdev, 1, priv->qemuCaps, flags) < 0) return -1;
/* this could have been changed by qemuPrepareHostdevPCIDevices */ @@ -1254,6 +1258,7 @@ qemuDomainAttachHostPciDevice(virQEMUDriverPtr driver, VIR_FREE(devstr); VIR_FREE(configfd_name); VIR_FORCE_CLOSE(configfd); + virObjectUnref(cfg);
return 0;
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 33d2a77..c0f0719 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -3596,6 +3596,7 @@ int qemuProcessStart(virConnectPtr conn, unsigned int stop_flags; virQEMUDriverConfigPtr cfg; virCapsPtr caps = NULL; + unsigned int hostdev_flags = 0;
VIR_DEBUG("vm=%p name=%s id=%d pid=%llu", vm, vm->def->name, vm->def->id, @@ -3685,8 +3686,10 @@ int qemuProcessStart(virConnectPtr conn,
/* Must be run before security labelling */ VIR_DEBUG("Preparing host devices"); + if (!cfg->relaxedACS) + hostdev_flags |= VIR_STRICT_ACS_CHECK; if (qemuPrepareHostDevices(driver, vm->def, priv->qemuCaps, - !migrateFrom) < 0) + !migrateFrom, hostdev_flags) < 0) goto cleanup;
VIR_DEBUG("Preparing chr devices");
ACK. -- Cedric

On 02/17/2014 11:15 AM, Cedric Bosdonnat wrote:
On Mon, 2014-02-17 at 14:32 +0800, Chunyan Liu wrote:
For extracting hostdev codes from qemu_hostdev.c to common library, change qemu specific cfg->relaxedACS handling to be a flag, and pass it to hostdev functions.
Signed-off-by: Chunyan Liu <cyliu@suse.com> --- src/qemu/qemu_hostdev.c | 11 +++++++---- src/qemu/qemu_hostdev.h | 10 ++++++++-- src/qemu/qemu_hotplug.c | 7 ++++++- src/qemu/qemu_process.c | 5 ++++- 4 files changed, 25 insertions(+), 8 deletions(-)
diff --git a/src/qemu/qemu_hostdev.c b/src/qemu/qemu_hostdev.c index 01c24f9..0d313c0 100644 --- a/src/qemu/qemu_hostdev.c +++ b/src/qemu/qemu_hostdev.c @@ -650,7 +650,8 @@ qemuPrepareHostdevPCIDevices(virQEMUDriverPtr driver, const unsigned char *uuid, virDomainHostdevDefPtr *hostdevs, int nhostdevs, - virQEMUCapsPtr qemuCaps) + virQEMUCapsPtr qemuCaps, + unsigned int flags) { virPCIDeviceListPtr pcidevs = NULL; int last_processed_hostdev_vf = -1; @@ -682,8 +683,9 @@ qemuPrepareHostdevPCIDevices(virQEMUDriverPtr driver, for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) { virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i); virPCIDevicePtr other; + bool strict_acs_check = !!(flags & VIR_STRICT_ACS_CHECK); Wouldn't that be more readable to have VIR_RELAXED_ACS_CHECK instead? It wouldn't change the logic.
I agree that it would be better to make STRICT the default, and have a RELAXED flag - this way if someone forgets to add the flag in some circumstance, we won't be defaulting to lowering the level of security.
- if (!virPCIDeviceIsAssignable(dev, !cfg->relaxedACS)) { + if (!virPCIDeviceIsAssignable(dev, strict_acs_check)) { virReportError(VIR_ERR_OPERATION_INVALID, _("PCI device %s is not assignable"), virPCIDeviceGetName(dev)); @@ -1187,14 +1189,15 @@ int qemuPrepareHostDevices(virQEMUDriverPtr driver, virDomainDefPtr def, virQEMUCapsPtr qemuCaps, - bool coldBoot) + bool coldBoot, + unsigned int flags) { if (!def->nhostdevs) return 0;
if (qemuPrepareHostdevPCIDevices(driver, def->name, def->uuid, def->hostdevs, def->nhostdevs, - qemuCaps) < 0) + qemuCaps, flags) < 0) return -1;
if (qemuPrepareHostUSBDevices(driver, def, coldBoot) < 0) diff --git a/src/qemu/qemu_hostdev.h b/src/qemu/qemu_hostdev.h index ffb3167..ab7fb9f 100644 --- a/src/qemu/qemu_hostdev.h +++ b/src/qemu/qemu_hostdev.h @@ -27,6 +27,10 @@ # include "qemu_conf.h" # include "domain_conf.h"
+typedef enum { + VIR_STRICT_ACS_CHECK = (1 << 0), /* strict acs check */ +} qemuHostdevFlag; + int qemuUpdateActivePciHostdevs(virQEMUDriverPtr driver, virDomainDefPtr def); int qemuUpdateActiveUsbHostdevs(virQEMUDriverPtr driver, @@ -40,7 +44,8 @@ int qemuPrepareHostdevPCIDevices(virQEMUDriverPtr driver, const unsigned char *uuid, virDomainHostdevDefPtr *hostdevs, int nhostdevs, - virQEMUCapsPtr qemuCaps); + virQEMUCapsPtr qemuCaps, + unsigned int flags); int qemuFindHostdevUSBDevice(virDomainHostdevDefPtr hostdev, bool mandatory, virUSBDevicePtr *usb); @@ -54,7 +59,8 @@ int qemuPrepareHostdevSCSIDevices(virQEMUDriverPtr driver, int qemuPrepareHostDevices(virQEMUDriverPtr driver, virDomainDefPtr def, virQEMUCapsPtr qemuCaps, - bool coldBoot); + bool coldBoot, + unsigned int flags); void qemuDomainReAttachHostScsiDevices(virQEMUDriverPtr driver, const char *name, virDomainHostdevDefPtr *hostdevs, diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 7066be6..c47c5e8 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -1154,12 +1154,16 @@ qemuDomainAttachHostPciDevice(virQEMUDriverPtr driver, bool teardownlabel = false; int backend; unsigned long long memKB; + virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver);
Once you've gotten a valid return from virQEMUDriverGetConfig, you've increased the refcount on the driver object, and you *must* unref it when you're finished using it...
+ unsigned int flags = 0;
if (VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs + 1) < 0) return -1;
...but if you take this return, you've not unrefed (yeah yeah, I know that if you've had a memalloc failure you're hosed anyway...)
+ if (!cfg->relaxedACS) + flags |= VIR_STRICT_ACS_CHECK; if (qemuPrepareHostdevPCIDevices(driver, vm->def->name, vm->def->uuid, - &hostdev, 1, priv->qemuCaps) < 0) + &hostdev, 1, priv->qemuCaps, flags) < 0) return -1;
...and similarly here. Both of these returns need to goto cleanup;, and cleanup needs to be just before the call to virObjectUnref().
/* this could have been changed by qemuPrepareHostdevPCIDevices */ @@ -1254,6 +1258,7 @@ qemuDomainAttachHostPciDevice(virQEMUDriverPtr driver, VIR_FREE(devstr); VIR_FREE(configfd_name); VIR_FORCE_CLOSE(configfd); + virObjectUnref(cfg);
return 0;
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 33d2a77..c0f0719 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -3596,6 +3596,7 @@ int qemuProcessStart(virConnectPtr conn, unsigned int stop_flags; virQEMUDriverConfigPtr cfg; virCapsPtr caps = NULL; + unsigned int hostdev_flags = 0;
VIR_DEBUG("vm=%p name=%s id=%d pid=%llu", vm, vm->def->name, vm->def->id, @@ -3685,8 +3686,10 @@ int qemuProcessStart(virConnectPtr conn,
/* Must be run before security labelling */ VIR_DEBUG("Preparing host devices"); + if (!cfg->relaxedACS) + hostdev_flags |= VIR_STRICT_ACS_CHECK; if (qemuPrepareHostDevices(driver, vm->def, priv->qemuCaps, - !migrateFrom) < 0) + !migrateFrom, hostdev_flags) < 0) goto cleanup;
VIR_DEBUG("Preparing chr devices");
ACK.
With the unref problem fixed, and the polarity of the flag switched.
-- Cedric
-- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list

For extracting hostdev codes from qemu_hostdev.c to common library, change qemu specific COLD_BOOT handling to be a flag, and pass it to hostdev functions. Signed-off-by: Chunyan Liu <cyliu@suse.com> --- src/qemu/qemu_hostdev.c | 6 +++--- src/qemu/qemu_hostdev.h | 2 +- src/qemu/qemu_process.c | 4 +++- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/qemu/qemu_hostdev.c b/src/qemu/qemu_hostdev.c index 0d313c0..ce5012d 100644 --- a/src/qemu/qemu_hostdev.c +++ b/src/qemu/qemu_hostdev.c @@ -992,7 +992,7 @@ out: static int qemuPrepareHostUSBDevices(virQEMUDriverPtr driver, virDomainDefPtr def, - bool coldBoot) + unsigned int flags) { size_t i; int ret = -1; @@ -1000,6 +1000,7 @@ qemuPrepareHostUSBDevices(virQEMUDriverPtr driver, virUSBDevicePtr tmp; virDomainHostdevDefPtr *hostdevs = def->hostdevs; int nhostdevs = def->nhostdevs; + bool coldBoot = !!(flags & VIR_COLD_BOOT); /* To prevent situation where USB device is assigned to two domains * we need to keep a list of currently assigned USB devices. @@ -1189,7 +1190,6 @@ int qemuPrepareHostDevices(virQEMUDriverPtr driver, virDomainDefPtr def, virQEMUCapsPtr qemuCaps, - bool coldBoot, unsigned int flags) { if (!def->nhostdevs) @@ -1200,7 +1200,7 @@ qemuPrepareHostDevices(virQEMUDriverPtr driver, qemuCaps, flags) < 0) return -1; - if (qemuPrepareHostUSBDevices(driver, def, coldBoot) < 0) + if (qemuPrepareHostUSBDevices(driver, def, flags) < 0) return -1; if (qemuPrepareHostdevSCSIDevices(driver, def->name, diff --git a/src/qemu/qemu_hostdev.h b/src/qemu/qemu_hostdev.h index ab7fb9f..710867d 100644 --- a/src/qemu/qemu_hostdev.h +++ b/src/qemu/qemu_hostdev.h @@ -29,6 +29,7 @@ typedef enum { VIR_STRICT_ACS_CHECK = (1 << 0), /* strict acs check */ + VIR_COLD_BOOT = (1 << 1), /* cold boot */ } qemuHostdevFlag; int qemuUpdateActivePciHostdevs(virQEMUDriverPtr driver, @@ -59,7 +60,6 @@ int qemuPrepareHostdevSCSIDevices(virQEMUDriverPtr driver, int qemuPrepareHostDevices(virQEMUDriverPtr driver, virDomainDefPtr def, virQEMUCapsPtr qemuCaps, - bool coldBoot, unsigned int flags); void qemuDomainReAttachHostScsiDevices(virQEMUDriverPtr driver, const char *name, diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index c0f0719..f1fe35e 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -3688,8 +3688,10 @@ int qemuProcessStart(virConnectPtr conn, VIR_DEBUG("Preparing host devices"); if (!cfg->relaxedACS) hostdev_flags |= VIR_STRICT_ACS_CHECK; + if (!migrateFrom) + hostdev_flags |= VIR_COLD_BOOT; if (qemuPrepareHostDevices(driver, vm->def, priv->qemuCaps, - !migrateFrom, hostdev_flags) < 0) + hostdev_flags) < 0) goto cleanup; VIR_DEBUG("Preparing chr devices"); -- 1.6.0.2

On Mon, 2014-02-17 at 14:32 +0800, Chunyan Liu wrote:
For extracting hostdev codes from qemu_hostdev.c to common library, change qemu specific COLD_BOOT handling to be a flag, and pass it to hostdev functions.
Signed-off-by: Chunyan Liu <cyliu@suse.com> --- src/qemu/qemu_hostdev.c | 6 +++--- src/qemu/qemu_hostdev.h | 2 +- src/qemu/qemu_process.c | 4 +++- 3 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/src/qemu/qemu_hostdev.c b/src/qemu/qemu_hostdev.c index 0d313c0..ce5012d 100644 --- a/src/qemu/qemu_hostdev.c +++ b/src/qemu/qemu_hostdev.c @@ -992,7 +992,7 @@ out: static int qemuPrepareHostUSBDevices(virQEMUDriverPtr driver, virDomainDefPtr def, - bool coldBoot) + unsigned int flags) { size_t i; int ret = -1; @@ -1000,6 +1000,7 @@ qemuPrepareHostUSBDevices(virQEMUDriverPtr driver, virUSBDevicePtr tmp; virDomainHostdevDefPtr *hostdevs = def->hostdevs; int nhostdevs = def->nhostdevs; + bool coldBoot = !!(flags & VIR_COLD_BOOT);
/* To prevent situation where USB device is assigned to two domains * we need to keep a list of currently assigned USB devices. @@ -1189,7 +1190,6 @@ int qemuPrepareHostDevices(virQEMUDriverPtr driver, virDomainDefPtr def, virQEMUCapsPtr qemuCaps, - bool coldBoot, unsigned int flags) { if (!def->nhostdevs) @@ -1200,7 +1200,7 @@ qemuPrepareHostDevices(virQEMUDriverPtr driver, qemuCaps, flags) < 0) return -1;
- if (qemuPrepareHostUSBDevices(driver, def, coldBoot) < 0) + if (qemuPrepareHostUSBDevices(driver, def, flags) < 0) return -1;
if (qemuPrepareHostdevSCSIDevices(driver, def->name, diff --git a/src/qemu/qemu_hostdev.h b/src/qemu/qemu_hostdev.h index ab7fb9f..710867d 100644 --- a/src/qemu/qemu_hostdev.h +++ b/src/qemu/qemu_hostdev.h @@ -29,6 +29,7 @@
typedef enum { VIR_STRICT_ACS_CHECK = (1 << 0), /* strict acs check */ + VIR_COLD_BOOT = (1 << 1), /* cold boot */ } qemuHostdevFlag;
int qemuUpdateActivePciHostdevs(virQEMUDriverPtr driver, @@ -59,7 +60,6 @@ int qemuPrepareHostdevSCSIDevices(virQEMUDriverPtr driver, int qemuPrepareHostDevices(virQEMUDriverPtr driver, virDomainDefPtr def, virQEMUCapsPtr qemuCaps, - bool coldBoot, unsigned int flags); void qemuDomainReAttachHostScsiDevices(virQEMUDriverPtr driver, const char *name, diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index c0f0719..f1fe35e 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -3688,8 +3688,10 @@ int qemuProcessStart(virConnectPtr conn, VIR_DEBUG("Preparing host devices"); if (!cfg->relaxedACS) hostdev_flags |= VIR_STRICT_ACS_CHECK; + if (!migrateFrom) + hostdev_flags |= VIR_COLD_BOOT; if (qemuPrepareHostDevices(driver, vm->def, priv->qemuCaps, - !migrateFrom, hostdev_flags) < 0) + hostdev_flags) < 0) goto cleanup;
VIR_DEBUG("Preparing chr devices");
ACK -- Cedric

On 02/17/2014 08:32 AM, Chunyan Liu wrote:
For extracting hostdev codes from qemu_hostdev.c to common library, change qemu specific COLD_BOOT handling to be a flag, and pass it to hostdev functions.
Signed-off-by: Chunyan Liu <cyliu@suse.com> --- src/qemu/qemu_hostdev.c | 6 +++--- src/qemu/qemu_hostdev.h | 2 +- src/qemu/qemu_process.c | 4 +++- 3 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/src/qemu/qemu_hostdev.c b/src/qemu/qemu_hostdev.c index 0d313c0..ce5012d 100644 --- a/src/qemu/qemu_hostdev.c +++ b/src/qemu/qemu_hostdev.c @@ -992,7 +992,7 @@ out: static int qemuPrepareHostUSBDevices(virQEMUDriverPtr driver, virDomainDefPtr def, - bool coldBoot) + unsigned int flags) { size_t i; int ret = -1; @@ -1000,6 +1000,7 @@ qemuPrepareHostUSBDevices(virQEMUDriverPtr driver, virUSBDevicePtr tmp; virDomainHostdevDefPtr *hostdevs = def->hostdevs; int nhostdevs = def->nhostdevs; + bool coldBoot = !!(flags & VIR_COLD_BOOT);
/* To prevent situation where USB device is assigned to two domains * we need to keep a list of currently assigned USB devices. @@ -1189,7 +1190,6 @@ int qemuPrepareHostDevices(virQEMUDriverPtr driver, virDomainDefPtr def, virQEMUCapsPtr qemuCaps, - bool coldBoot, unsigned int flags) { if (!def->nhostdevs) @@ -1200,7 +1200,7 @@ qemuPrepareHostDevices(virQEMUDriverPtr driver, qemuCaps, flags) < 0) return -1;
- if (qemuPrepareHostUSBDevices(driver, def, coldBoot) < 0) + if (qemuPrepareHostUSBDevices(driver, def, flags) < 0) return -1;
if (qemuPrepareHostdevSCSIDevices(driver, def->name, diff --git a/src/qemu/qemu_hostdev.h b/src/qemu/qemu_hostdev.h index ab7fb9f..710867d 100644 --- a/src/qemu/qemu_hostdev.h +++ b/src/qemu/qemu_hostdev.h @@ -29,6 +29,7 @@
typedef enum { VIR_STRICT_ACS_CHECK = (1 << 0), /* strict acs check */ + VIR_COLD_BOOT = (1 << 1), /* cold boot */
I think these flags need a less generic preface - maybe VIR_HOSTDEV_* or VIR_HOSTDEV_FLAG_*.
} qemuHostdevFlag;
int qemuUpdateActivePciHostdevs(virQEMUDriverPtr driver, @@ -59,7 +60,6 @@ int qemuPrepareHostdevSCSIDevices(virQEMUDriverPtr driver, int qemuPrepareHostDevices(virQEMUDriverPtr driver, virDomainDefPtr def, virQEMUCapsPtr qemuCaps, - bool coldBoot, unsigned int flags); void qemuDomainReAttachHostScsiDevices(virQEMUDriverPtr driver, const char *name, diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index c0f0719..f1fe35e 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -3688,8 +3688,10 @@ int qemuProcessStart(virConnectPtr conn, VIR_DEBUG("Preparing host devices"); if (!cfg->relaxedACS) hostdev_flags |= VIR_STRICT_ACS_CHECK; + if (!migrateFrom) + hostdev_flags |= VIR_COLD_BOOT; if (qemuPrepareHostDevices(driver, vm->def, priv->qemuCaps, - !migrateFrom, hostdev_flags) < 0) + hostdev_flags) < 0) goto cleanup;
VIR_DEBUG("Preparing chr devices");

For extracting hostdev codes from qemu_hostdev.c to common library, change original paring VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT in hostdev function to qemuDomainDeviceDefPostParse. Signed-off-by: Chunyan Liu <cyliu@suse.com> --- src/qemu/qemu_domain.c | 22 +++++++++++++++ src/qemu/qemu_hostdev.c | 28 +++----------------- src/qemu/qemu_hostdev.h | 2 - src/qemu/qemu_hotplug.c | 2 +- src/qemu/qemu_process.c | 3 +- .../qemuxml2argv-hostdev-pci-address.xml | 1 + .../qemuxml2argvdata/qemuxml2argv-net-hostdev.xml | 1 + tests/qemuxml2argvdata/qemuxml2argv-pci-rom.xml | 2 + 8 files changed, 32 insertions(+), 29 deletions(-) diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index a665061..55e707e 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -38,6 +38,7 @@ #include "virtime.h" #include "virstoragefile.h" #include "virstring.h" +#include "qemu_hostdev.h" #include <sys/time.h> #include <fcntl.h> @@ -821,6 +822,7 @@ qemuDomainDeviceDefPostParse(virDomainDeviceDefPtr dev, int ret = -1; virQEMUDriverPtr driver = opaque; virQEMUDriverConfigPtr cfg = NULL; + virQEMUCapsPtr qemuCaps = NULL; if (dev->type == VIR_DOMAIN_DEVICE_NET && dev->data.net->type != VIR_DOMAIN_NET_TYPE_HOSTDEV && @@ -899,6 +901,26 @@ qemuDomainDeviceDefPostParse(virDomainDeviceDefPtr dev, dev->data.chr->source.data.nix.listen = true; } + if (dev->type == VIR_DOMAIN_DEVICE_HOSTDEV) { + virDomainHostdevDefPtr hostdev = dev->data.hostdev; + if (hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS && + hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI && + hostdev->source.subsys.u.pci.backend == VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT) { + + hostdev->source.subsys.u.pci.backend = VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM; + if (driver && driver->qemuCapsCache) { + bool supportsPassthroughVFIO = qemuHostdevHostSupportsPassthroughVFIO(); + qemuCaps = virQEMUCapsCacheLookupCopy(driver->qemuCapsCache, + def->emulator); + if (supportsPassthroughVFIO && qemuCaps && + virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VFIO_PCI)) + hostdev->source.subsys.u.pci.backend = VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO; + + virObjectUnref(qemuCaps); + } + } + } + ret = 0; cleanup: diff --git a/src/qemu/qemu_hostdev.c b/src/qemu/qemu_hostdev.c index ce5012d..80552cd 100644 --- a/src/qemu/qemu_hostdev.c +++ b/src/qemu/qemu_hostdev.c @@ -583,8 +583,7 @@ qemuHostdevHostSupportsPassthroughLegacy(void) static bool qemuPrepareHostdevPCICheckSupport(virDomainHostdevDefPtr *hostdevs, - size_t nhostdevs, - virQEMUCapsPtr qemuCaps) + size_t nhostdevs) { bool supportsPassthroughKVM = qemuHostdevHostSupportsPassthroughLegacy(); bool supportsPassthroughVFIO = qemuHostdevHostSupportsPassthroughVFIO(); @@ -601,23 +600,6 @@ qemuPrepareHostdevPCICheckSupport(virDomainHostdevDefPtr *hostdevs, continue; switch ((virDomainHostdevSubsysPciBackendType) *backend) { - case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT: - if (supportsPassthroughVFIO && - virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VFIO_PCI)) { - *backend = VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO; - } else if (supportsPassthroughKVM && - (virQEMUCapsGet(qemuCaps, QEMU_CAPS_PCIDEVICE) || - virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE))) { - *backend = VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM; - } else { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("host doesn't support passthrough of " - "host PCI devices")); - return false; - } - - break; - case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO: if (!supportsPassthroughVFIO) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", @@ -635,7 +617,7 @@ qemuPrepareHostdevPCICheckSupport(virDomainHostdevDefPtr *hostdevs, break; - case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_TYPE_LAST: + default: break; } } @@ -650,7 +632,6 @@ qemuPrepareHostdevPCIDevices(virQEMUDriverPtr driver, const unsigned char *uuid, virDomainHostdevDefPtr *hostdevs, int nhostdevs, - virQEMUCapsPtr qemuCaps, unsigned int flags) { virPCIDeviceListPtr pcidevs = NULL; @@ -659,7 +640,7 @@ qemuPrepareHostdevPCIDevices(virQEMUDriverPtr driver, int ret = -1; virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); - if (!qemuPrepareHostdevPCICheckSupport(hostdevs, nhostdevs, qemuCaps)) + if (!qemuPrepareHostdevPCICheckSupport(hostdevs, nhostdevs)) goto cleanup; virObjectLock(driver->activePciHostdevs); @@ -1189,7 +1170,6 @@ cleanup: int qemuPrepareHostDevices(virQEMUDriverPtr driver, virDomainDefPtr def, - virQEMUCapsPtr qemuCaps, unsigned int flags) { if (!def->nhostdevs) @@ -1197,7 +1177,7 @@ qemuPrepareHostDevices(virQEMUDriverPtr driver, if (qemuPrepareHostdevPCIDevices(driver, def->name, def->uuid, def->hostdevs, def->nhostdevs, - qemuCaps, flags) < 0) + flags) < 0) return -1; if (qemuPrepareHostUSBDevices(driver, def, flags) < 0) diff --git a/src/qemu/qemu_hostdev.h b/src/qemu/qemu_hostdev.h index 710867d..128032d 100644 --- a/src/qemu/qemu_hostdev.h +++ b/src/qemu/qemu_hostdev.h @@ -45,7 +45,6 @@ int qemuPrepareHostdevPCIDevices(virQEMUDriverPtr driver, const unsigned char *uuid, virDomainHostdevDefPtr *hostdevs, int nhostdevs, - virQEMUCapsPtr qemuCaps, unsigned int flags); int qemuFindHostdevUSBDevice(virDomainHostdevDefPtr hostdev, bool mandatory, @@ -59,7 +58,6 @@ int qemuPrepareHostdevSCSIDevices(virQEMUDriverPtr driver, int nhostdevs); int qemuPrepareHostDevices(virQEMUDriverPtr driver, virDomainDefPtr def, - virQEMUCapsPtr qemuCaps, unsigned int flags); void qemuDomainReAttachHostScsiDevices(virQEMUDriverPtr driver, const char *name, diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index c47c5e8..8486f25 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -1163,7 +1163,7 @@ qemuDomainAttachHostPciDevice(virQEMUDriverPtr driver, if (!cfg->relaxedACS) flags |= VIR_STRICT_ACS_CHECK; if (qemuPrepareHostdevPCIDevices(driver, vm->def->name, vm->def->uuid, - &hostdev, 1, priv->qemuCaps, flags) < 0) + &hostdev, 1, flags) < 0) return -1; /* this could have been changed by qemuPrepareHostdevPCIDevices */ diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index f1fe35e..e938649 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -3690,8 +3690,7 @@ int qemuProcessStart(virConnectPtr conn, hostdev_flags |= VIR_STRICT_ACS_CHECK; if (!migrateFrom) hostdev_flags |= VIR_COLD_BOOT; - if (qemuPrepareHostDevices(driver, vm->def, priv->qemuCaps, - hostdev_flags) < 0) + if (qemuPrepareHostDevices(driver, vm->def, hostdev_flags) < 0) goto cleanup; VIR_DEBUG("Preparing chr devices"); diff --git a/tests/qemuxml2argvdata/qemuxml2argv-hostdev-pci-address.xml b/tests/qemuxml2argvdata/qemuxml2argv-hostdev-pci-address.xml index 422127c..b9a221a 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-hostdev-pci-address.xml +++ b/tests/qemuxml2argvdata/qemuxml2argv-hostdev-pci-address.xml @@ -24,6 +24,7 @@ <controller type='ide' index='0'/> <controller type='pci' index='0' model='pci-root'/> <hostdev mode='subsystem' type='pci' managed='yes'> + <driver name='kvm'/> <source> <address domain='0x0000' bus='0x06' slot='0x12' function='0x5'/> </source> diff --git a/tests/qemuxml2argvdata/qemuxml2argv-net-hostdev.xml b/tests/qemuxml2argvdata/qemuxml2argv-net-hostdev.xml index d65ef87..9e79348 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-net-hostdev.xml +++ b/tests/qemuxml2argvdata/qemuxml2argv-net-hostdev.xml @@ -24,6 +24,7 @@ <controller type='pci' index='0' model='pci-root'/> <interface type='hostdev' managed='yes'> <mac address='00:11:22:33:44:55'/> + <driver name='kvm'/> <source> <address type='pci' domain='0x0002' bus='0x03' slot='0x07' function='0x1'/> </source> diff --git a/tests/qemuxml2argvdata/qemuxml2argv-pci-rom.xml b/tests/qemuxml2argvdata/qemuxml2argv-pci-rom.xml index a5e59b2..924842b 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-pci-rom.xml +++ b/tests/qemuxml2argvdata/qemuxml2argv-pci-rom.xml @@ -33,12 +33,14 @@ <rom file='/etc/fake/bootrom.bin'/> </interface> <hostdev mode='subsystem' type='pci' managed='yes'> + <driver name='kvm'/> <source> <address domain='0x0000' bus='0x06' slot='0x12' function='0x5'/> </source> <rom bar='off'/> </hostdev> <hostdev mode='subsystem' type='pci' managed='yes'> + <driver name='kvm'/> <source> <address domain='0x0000' bus='0x06' slot='0x12' function='0x6'/> </source> -- 1.6.0.2

On Mon, 2014-02-17 at 14:32 +0800, Chunyan Liu wrote:
For extracting hostdev codes from qemu_hostdev.c to common library, change original paring VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT in hostdev function to qemuDomainDeviceDefPostParse.
typo: paring -> parsing.
Signed-off-by: Chunyan Liu <cyliu@suse.com> --- src/qemu/qemu_domain.c | 22 +++++++++++++++ src/qemu/qemu_hostdev.c | 28 +++----------------- src/qemu/qemu_hostdev.h | 2 - src/qemu/qemu_hotplug.c | 2 +- src/qemu/qemu_process.c | 3 +- .../qemuxml2argv-hostdev-pci-address.xml | 1 + .../qemuxml2argvdata/qemuxml2argv-net-hostdev.xml | 1 + tests/qemuxml2argvdata/qemuxml2argv-pci-rom.xml | 2 + 8 files changed, 32 insertions(+), 29 deletions(-)
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index a665061..55e707e 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -38,6 +38,7 @@ #include "virtime.h" #include "virstoragefile.h" #include "virstring.h" +#include "qemu_hostdev.h"
#include <sys/time.h> #include <fcntl.h> @@ -821,6 +822,7 @@ qemuDomainDeviceDefPostParse(virDomainDeviceDefPtr dev, int ret = -1; virQEMUDriverPtr driver = opaque; virQEMUDriverConfigPtr cfg = NULL; + virQEMUCapsPtr qemuCaps = NULL;
if (dev->type == VIR_DOMAIN_DEVICE_NET && dev->data.net->type != VIR_DOMAIN_NET_TYPE_HOSTDEV && @@ -899,6 +901,26 @@ qemuDomainDeviceDefPostParse(virDomainDeviceDefPtr dev, dev->data.chr->source.data.nix.listen = true; }
+ if (dev->type == VIR_DOMAIN_DEVICE_HOSTDEV) { + virDomainHostdevDefPtr hostdev = dev->data.hostdev; + if (hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS && + hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI && + hostdev->source.subsys.u.pci.backend == VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT) { + + hostdev->source.subsys.u.pci.backend = VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM; + if (driver && driver->qemuCapsCache) { + bool supportsPassthroughVFIO = qemuHostdevHostSupportsPassthroughVFIO(); + qemuCaps = virQEMUCapsCacheLookupCopy(driver->qemuCapsCache, + def->emulator); + if (supportsPassthroughVFIO && qemuCaps && + virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VFIO_PCI)) + hostdev->source.subsys.u.pci.backend = VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO; + + virObjectUnref(qemuCaps); + } + } + } + ret = 0;
KVM passthrough support isn't checked here but was checked in the removed code, is that intended?
cleanup: diff --git a/src/qemu/qemu_hostdev.c b/src/qemu/qemu_hostdev.c index ce5012d..80552cd 100644 --- a/src/qemu/qemu_hostdev.c +++ b/src/qemu/qemu_hostdev.c @@ -583,8 +583,7 @@ qemuHostdevHostSupportsPassthroughLegacy(void)
static bool qemuPrepareHostdevPCICheckSupport(virDomainHostdevDefPtr *hostdevs, - size_t nhostdevs, - virQEMUCapsPtr qemuCaps) + size_t nhostdevs) { bool supportsPassthroughKVM = qemuHostdevHostSupportsPassthroughLegacy(); bool supportsPassthroughVFIO = qemuHostdevHostSupportsPassthroughVFIO(); @@ -601,23 +600,6 @@ qemuPrepareHostdevPCICheckSupport(virDomainHostdevDefPtr *hostdevs, continue;
switch ((virDomainHostdevSubsysPciBackendType) *backend) { - case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT: - if (supportsPassthroughVFIO && - virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VFIO_PCI)) { - *backend = VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO; - } else if (supportsPassthroughKVM && - (virQEMUCapsGet(qemuCaps, QEMU_CAPS_PCIDEVICE) || - virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE))) { - *backend = VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM; - } else { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("host doesn't support passthrough of " - "host PCI devices")); - return false; - } - - break; - case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO: if (!supportsPassthroughVFIO) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", @@ -635,7 +617,7 @@ qemuPrepareHostdevPCICheckSupport(virDomainHostdevDefPtr *hostdevs,
break;
- case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_TYPE_LAST: + default: break; } } @@ -650,7 +632,6 @@ qemuPrepareHostdevPCIDevices(virQEMUDriverPtr driver, const unsigned char *uuid, virDomainHostdevDefPtr *hostdevs, int nhostdevs, - virQEMUCapsPtr qemuCaps, unsigned int flags) { virPCIDeviceListPtr pcidevs = NULL; @@ -659,7 +640,7 @@ qemuPrepareHostdevPCIDevices(virQEMUDriverPtr driver, int ret = -1; virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver);
- if (!qemuPrepareHostdevPCICheckSupport(hostdevs, nhostdevs, qemuCaps)) + if (!qemuPrepareHostdevPCICheckSupport(hostdevs, nhostdevs)) goto cleanup;
virObjectLock(driver->activePciHostdevs); @@ -1189,7 +1170,6 @@ cleanup: int qemuPrepareHostDevices(virQEMUDriverPtr driver, virDomainDefPtr def, - virQEMUCapsPtr qemuCaps, unsigned int flags) { if (!def->nhostdevs) @@ -1197,7 +1177,7 @@ qemuPrepareHostDevices(virQEMUDriverPtr driver,
if (qemuPrepareHostdevPCIDevices(driver, def->name, def->uuid, def->hostdevs, def->nhostdevs, - qemuCaps, flags) < 0) + flags) < 0) return -1;
if (qemuPrepareHostUSBDevices(driver, def, flags) < 0) diff --git a/src/qemu/qemu_hostdev.h b/src/qemu/qemu_hostdev.h index 710867d..128032d 100644 --- a/src/qemu/qemu_hostdev.h +++ b/src/qemu/qemu_hostdev.h @@ -45,7 +45,6 @@ int qemuPrepareHostdevPCIDevices(virQEMUDriverPtr driver, const unsigned char *uuid, virDomainHostdevDefPtr *hostdevs, int nhostdevs, - virQEMUCapsPtr qemuCaps, unsigned int flags); int qemuFindHostdevUSBDevice(virDomainHostdevDefPtr hostdev, bool mandatory, @@ -59,7 +58,6 @@ int qemuPrepareHostdevSCSIDevices(virQEMUDriverPtr driver, int nhostdevs); int qemuPrepareHostDevices(virQEMUDriverPtr driver, virDomainDefPtr def, - virQEMUCapsPtr qemuCaps, unsigned int flags); void qemuDomainReAttachHostScsiDevices(virQEMUDriverPtr driver, const char *name, diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index c47c5e8..8486f25 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -1163,7 +1163,7 @@ qemuDomainAttachHostPciDevice(virQEMUDriverPtr driver, if (!cfg->relaxedACS) flags |= VIR_STRICT_ACS_CHECK; if (qemuPrepareHostdevPCIDevices(driver, vm->def->name, vm->def->uuid, - &hostdev, 1, priv->qemuCaps, flags) < 0) + &hostdev, 1, flags) < 0) return -1;
/* this could have been changed by qemuPrepareHostdevPCIDevices */ diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index f1fe35e..e938649 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -3690,8 +3690,7 @@ int qemuProcessStart(virConnectPtr conn, hostdev_flags |= VIR_STRICT_ACS_CHECK; if (!migrateFrom) hostdev_flags |= VIR_COLD_BOOT; - if (qemuPrepareHostDevices(driver, vm->def, priv->qemuCaps, - hostdev_flags) < 0) + if (qemuPrepareHostDevices(driver, vm->def, hostdev_flags) < 0) goto cleanup;
VIR_DEBUG("Preparing chr devices"); diff --git a/tests/qemuxml2argvdata/qemuxml2argv-hostdev-pci-address.xml b/tests/qemuxml2argvdata/qemuxml2argv-hostdev-pci-address.xml index 422127c..b9a221a 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-hostdev-pci-address.xml +++ b/tests/qemuxml2argvdata/qemuxml2argv-hostdev-pci-address.xml @@ -24,6 +24,7 @@ <controller type='ide' index='0'/> <controller type='pci' index='0' model='pci-root'/> <hostdev mode='subsystem' type='pci' managed='yes'> + <driver name='kvm'/> <source> <address domain='0x0000' bus='0x06' slot='0x12' function='0x5'/> </source> diff --git a/tests/qemuxml2argvdata/qemuxml2argv-net-hostdev.xml b/tests/qemuxml2argvdata/qemuxml2argv-net-hostdev.xml index d65ef87..9e79348 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-net-hostdev.xml +++ b/tests/qemuxml2argvdata/qemuxml2argv-net-hostdev.xml @@ -24,6 +24,7 @@ <controller type='pci' index='0' model='pci-root'/> <interface type='hostdev' managed='yes'> <mac address='00:11:22:33:44:55'/> + <driver name='kvm'/> <source> <address type='pci' domain='0x0002' bus='0x03' slot='0x07' function='0x1'/> </source> diff --git a/tests/qemuxml2argvdata/qemuxml2argv-pci-rom.xml b/tests/qemuxml2argvdata/qemuxml2argv-pci-rom.xml index a5e59b2..924842b 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-pci-rom.xml +++ b/tests/qemuxml2argvdata/qemuxml2argv-pci-rom.xml @@ -33,12 +33,14 @@ <rom file='/etc/fake/bootrom.bin'/> </interface> <hostdev mode='subsystem' type='pci' managed='yes'> + <driver name='kvm'/> <source> <address domain='0x0000' bus='0x06' slot='0x12' function='0x5'/> </source> <rom bar='off'/> </hostdev> <hostdev mode='subsystem' type='pci' managed='yes'> + <driver name='kvm'/> <source> <address domain='0x0000' bus='0x06' slot='0x12' function='0x6'/> </source>
Can't those hostdev definitions keep the default backend like before? -- Cedric

On 02/17/2014 11:38 AM, Cedric Bosdonnat wrote:
On Mon, 2014-02-17 at 14:32 +0800, Chunyan Liu wrote:
For extracting hostdev codes from qemu_hostdev.c to common library, change original paring VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT in hostdev function to qemuDomainDeviceDefPostParse. typo: paring -> parsing.
Signed-off-by: Chunyan Liu <cyliu@suse.com> --- src/qemu/qemu_domain.c | 22 +++++++++++++++ src/qemu/qemu_hostdev.c | 28 +++----------------- src/qemu/qemu_hostdev.h | 2 - src/qemu/qemu_hotplug.c | 2 +- src/qemu/qemu_process.c | 3 +- .../qemuxml2argv-hostdev-pci-address.xml | 1 + .../qemuxml2argvdata/qemuxml2argv-net-hostdev.xml | 1 + tests/qemuxml2argvdata/qemuxml2argv-pci-rom.xml | 2 + 8 files changed, 32 insertions(+), 29 deletions(-)
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index a665061..55e707e 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -38,6 +38,7 @@ #include "virtime.h" #include "virstoragefile.h" #include "virstring.h" +#include "qemu_hostdev.h"
#include <sys/time.h> #include <fcntl.h> @@ -821,6 +822,7 @@ qemuDomainDeviceDefPostParse(virDomainDeviceDefPtr dev, int ret = -1; virQEMUDriverPtr driver = opaque; virQEMUDriverConfigPtr cfg = NULL; + virQEMUCapsPtr qemuCaps = NULL;
if (dev->type == VIR_DOMAIN_DEVICE_NET && dev->data.net->type != VIR_DOMAIN_NET_TYPE_HOSTDEV && @@ -899,6 +901,26 @@ qemuDomainDeviceDefPostParse(virDomainDeviceDefPtr dev, dev->data.chr->source.data.nix.listen = true; }
+ if (dev->type == VIR_DOMAIN_DEVICE_HOSTDEV) { + virDomainHostdevDefPtr hostdev = dev->data.hostdev; + if (hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS && + hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI && + hostdev->source.subsys.u.pci.backend == VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT) { + + hostdev->source.subsys.u.pci.backend = VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM; + if (driver && driver->qemuCapsCache) { + bool supportsPassthroughVFIO = qemuHostdevHostSupportsPassthroughVFIO(); + qemuCaps = virQEMUCapsCacheLookupCopy(driver->qemuCapsCache, + def->emulator); + if (supportsPassthroughVFIO && qemuCaps && + virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VFIO_PCI)) + hostdev->source.subsys.u.pci.backend = VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO; + + virObjectUnref(qemuCaps); + } + } + } + ret = 0; KVM passthrough support isn't checked here but was checked in the removed code, is that intended?
The fact that the code doing the KVM check is fairly new, suggests to me that this omission *wasn't* intentional. I'm really glad that you're looking at that in detail, because it's the potential omission of recent bugfixes that has me nervous. Beyond that, this isn't the proper place to be moving this to - anything that is done by the PostParse callback function ends up being written to persistent config and is there effectively forever, but the interpretation of the hostdev driver "default" backend is something that must be re-done exactly at the moment the device it going to be attached. This patch instead causes that decision to be made when the domain is defined, and forever encoded in the config. I think that instead you need to either call it from qemuPrepareHostDevices() and qemuDomainAttachHostPciDevice(), OR send a callback function pointer into your new virHostdevPreparePciHostdevs(), with that function returning the "backend to use this time" based on the config setting and current system state.
cleanup: diff --git a/src/qemu/qemu_hostdev.c b/src/qemu/qemu_hostdev.c index ce5012d..80552cd 100644 --- a/src/qemu/qemu_hostdev.c +++ b/src/qemu/qemu_hostdev.c @@ -583,8 +583,7 @@ qemuHostdevHostSupportsPassthroughLegacy(void)
static bool qemuPrepareHostdevPCICheckSupport(virDomainHostdevDefPtr *hostdevs, - size_t nhostdevs, - virQEMUCapsPtr qemuCaps) + size_t nhostdevs) { bool supportsPassthroughKVM = qemuHostdevHostSupportsPassthroughLegacy(); bool supportsPassthroughVFIO = qemuHostdevHostSupportsPassthroughVFIO(); @@ -601,23 +600,6 @@ qemuPrepareHostdevPCICheckSupport(virDomainHostdevDefPtr *hostdevs, continue;
switch ((virDomainHostdevSubsysPciBackendType) *backend) { - case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT: - if (supportsPassthroughVFIO && - virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VFIO_PCI)) { - *backend = VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO; - } else if (supportsPassthroughKVM && - (virQEMUCapsGet(qemuCaps, QEMU_CAPS_PCIDEVICE) || - virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE))) { - *backend = VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM; - } else { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("host doesn't support passthrough of " - "host PCI devices")); - return false; - } - - break; - case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO: if (!supportsPassthroughVFIO) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", @@ -635,7 +617,7 @@ qemuPrepareHostdevPCICheckSupport(virDomainHostdevDefPtr *hostdevs,
break;
- case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_TYPE_LAST: + default:
I'm pretty sure that Peter intentionally *didn't* put a default case into this switch (and many others) specifically so that somebody adding a new enum value would get compiler errors telling them all the places they needed to handle the new case, i.e. please do not replace "case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_TYPE_LAST" with "default".
break; } } @@ -650,7 +632,6 @@ qemuPrepareHostdevPCIDevices(virQEMUDriverPtr driver, const unsigned char *uuid, virDomainHostdevDefPtr *hostdevs, int nhostdevs, - virQEMUCapsPtr qemuCaps, unsigned int flags) { virPCIDeviceListPtr pcidevs = NULL; @@ -659,7 +640,7 @@ qemuPrepareHostdevPCIDevices(virQEMUDriverPtr driver, int ret = -1; virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver);
- if (!qemuPrepareHostdevPCICheckSupport(hostdevs, nhostdevs, qemuCaps)) + if (!qemuPrepareHostdevPCICheckSupport(hostdevs, nhostdevs)) goto cleanup;
virObjectLock(driver->activePciHostdevs); @@ -1189,7 +1170,6 @@ cleanup: int qemuPrepareHostDevices(virQEMUDriverPtr driver, virDomainDefPtr def, - virQEMUCapsPtr qemuCaps, unsigned int flags) { if (!def->nhostdevs) @@ -1197,7 +1177,7 @@ qemuPrepareHostDevices(virQEMUDriverPtr driver,
if (qemuPrepareHostdevPCIDevices(driver, def->name, def->uuid, def->hostdevs, def->nhostdevs, - qemuCaps, flags) < 0) + flags) < 0) return -1;
if (qemuPrepareHostUSBDevices(driver, def, flags) < 0) diff --git a/src/qemu/qemu_hostdev.h b/src/qemu/qemu_hostdev.h index 710867d..128032d 100644 --- a/src/qemu/qemu_hostdev.h +++ b/src/qemu/qemu_hostdev.h @@ -45,7 +45,6 @@ int qemuPrepareHostdevPCIDevices(virQEMUDriverPtr driver, const unsigned char *uuid, virDomainHostdevDefPtr *hostdevs, int nhostdevs, - virQEMUCapsPtr qemuCaps, unsigned int flags); int qemuFindHostdevUSBDevice(virDomainHostdevDefPtr hostdev, bool mandatory, @@ -59,7 +58,6 @@ int qemuPrepareHostdevSCSIDevices(virQEMUDriverPtr driver, int nhostdevs); int qemuPrepareHostDevices(virQEMUDriverPtr driver, virDomainDefPtr def, - virQEMUCapsPtr qemuCaps, unsigned int flags); void qemuDomainReAttachHostScsiDevices(virQEMUDriverPtr driver, const char *name, diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index c47c5e8..8486f25 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -1163,7 +1163,7 @@ qemuDomainAttachHostPciDevice(virQEMUDriverPtr driver, if (!cfg->relaxedACS) flags |= VIR_STRICT_ACS_CHECK; if (qemuPrepareHostdevPCIDevices(driver, vm->def->name, vm->def->uuid, - &hostdev, 1, priv->qemuCaps, flags) < 0) + &hostdev, 1, flags) < 0) return -1;
/* this could have been changed by qemuPrepareHostdevPCIDevices */ diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index f1fe35e..e938649 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -3690,8 +3690,7 @@ int qemuProcessStart(virConnectPtr conn, hostdev_flags |= VIR_STRICT_ACS_CHECK; if (!migrateFrom) hostdev_flags |= VIR_COLD_BOOT; - if (qemuPrepareHostDevices(driver, vm->def, priv->qemuCaps, - hostdev_flags) < 0) + if (qemuPrepareHostDevices(driver, vm->def, hostdev_flags) < 0) goto cleanup;
VIR_DEBUG("Preparing chr devices"); diff --git a/tests/qemuxml2argvdata/qemuxml2argv-hostdev-pci-address.xml b/tests/qemuxml2argvdata/qemuxml2argv-hostdev-pci-address.xml index 422127c..b9a221a 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-hostdev-pci-address.xml +++ b/tests/qemuxml2argvdata/qemuxml2argv-hostdev-pci-address.xml @@ -24,6 +24,7 @@ <controller type='ide' index='0'/> <controller type='pci' index='0' model='pci-root'/> <hostdev mode='subsystem' type='pci' managed='yes'> + <driver name='kvm'/> <source> <address domain='0x0000' bus='0x06' slot='0x12' function='0x5'/> </source> diff --git a/tests/qemuxml2argvdata/qemuxml2argv-net-hostdev.xml b/tests/qemuxml2argvdata/qemuxml2argv-net-hostdev.xml index d65ef87..9e79348 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-net-hostdev.xml +++ b/tests/qemuxml2argvdata/qemuxml2argv-net-hostdev.xml @@ -24,6 +24,7 @@ <controller type='pci' index='0' model='pci-root'/> <interface type='hostdev' managed='yes'> <mac address='00:11:22:33:44:55'/> + <driver name='kvm'/> <source> <address type='pci' domain='0x0002' bus='0x03' slot='0x07' function='0x1'/> </source> diff --git a/tests/qemuxml2argvdata/qemuxml2argv-pci-rom.xml b/tests/qemuxml2argvdata/qemuxml2argv-pci-rom.xml index a5e59b2..924842b 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-pci-rom.xml +++ b/tests/qemuxml2argvdata/qemuxml2argv-pci-rom.xml @@ -33,12 +33,14 @@ <rom file='/etc/fake/bootrom.bin'/> </interface> <hostdev mode='subsystem' type='pci' managed='yes'> + <driver name='kvm'/> <source> <address domain='0x0000' bus='0x06' slot='0x12' function='0x5'/> </source> <rom bar='off'/> </hostdev> <hostdev mode='subsystem' type='pci' managed='yes'> + <driver name='kvm'/> <source> <address domain='0x0000' bus='0x06' slot='0x12' function='0x6'/> </source>
Can't those hostdev definitions keep the default backend like before?
A changes in the test case is sometimes correct, but should be considered a red flag. My first guest would be that it was necessary due to the code that was added to the PostParse function (i.e. it's indicating a problem)

2014-02-17 23:26 GMT+08:00 Laine Stump <laine@laine.org>:
On 02/17/2014 11:38 AM, Cedric Bosdonnat wrote:
On Mon, 2014-02-17 at 14:32 +0800, Chunyan Liu wrote:
For extracting hostdev codes from qemu_hostdev.c to common library, change original paring VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT in hostdev function to qemuDomainDeviceDefPostParse. typo: paring -> parsing.
Signed-off-by: Chunyan Liu <cyliu@suse.com> --- src/qemu/qemu_domain.c | 22 +++++++++++++++ src/qemu/qemu_hostdev.c | 28 +++----------------- src/qemu/qemu_hostdev.h | 2 - src/qemu/qemu_hotplug.c | 2 +- src/qemu/qemu_process.c | 3 +- .../qemuxml2argv-hostdev-pci-address.xml | 1 + .../qemuxml2argvdata/qemuxml2argv-net-hostdev.xml | 1 + tests/qemuxml2argvdata/qemuxml2argv-pci-rom.xml | 2 + 8 files changed, 32 insertions(+), 29 deletions(-)
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index a665061..55e707e 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -38,6 +38,7 @@ #include "virtime.h" #include "virstoragefile.h" #include "virstring.h" +#include "qemu_hostdev.h"
#include <sys/time.h> #include <fcntl.h> @@ -821,6 +822,7 @@ qemuDomainDeviceDefPostParse(virDomainDeviceDefPtr dev, int ret = -1; virQEMUDriverPtr driver = opaque; virQEMUDriverConfigPtr cfg = NULL; + virQEMUCapsPtr qemuCaps = NULL;
if (dev->type == VIR_DOMAIN_DEVICE_NET && dev->data.net->type != VIR_DOMAIN_NET_TYPE_HOSTDEV && @@ -899,6 +901,26 @@ qemuDomainDeviceDefPostParse(virDomainDeviceDefPtr dev, dev->data.chr->source.data.nix.listen = true; }
+ if (dev->type == VIR_DOMAIN_DEVICE_HOSTDEV) { + virDomainHostdevDefPtr hostdev = dev->data.hostdev; + if (hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS && + hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI && + hostdev->source.subsys.u.pci.backend == VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT) { + + hostdev->source.subsys.u.pci.backend = VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM; + if (driver && driver->qemuCapsCache) { + bool supportsPassthroughVFIO = qemuHostdevHostSupportsPassthroughVFIO(); + qemuCaps = virQEMUCapsCacheLookupCopy(driver->qemuCapsCache, + def->emulator); + if (supportsPassthroughVFIO && qemuCaps && + virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VFIO_PCI)) + hostdev->source.subsys.u.pci.backend = VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO; + + virObjectUnref(qemuCaps); + } + } + } + ret = 0; KVM passthrough support isn't checked here but was checked in the removed code, is that intended?
The fact that the code doing the KVM check is fairly new, suggests to me that this omission *wasn't* intentional. I'm really glad that you're looking at that in detail, because it's the potential omission of recent bugfixes that has me nervous.
Beyond that, this isn't the proper place to be moving this to - anything that is done by the PostParse callback function ends up being written to persistent config and is there effectively forever, but the interpretation of the hostdev driver "default" backend is something that must be re-done exactly at the moment the device it going to be attached. This patch instead causes that decision to be made when the domain is defined, and forever encoded in the config.
I think that instead you need to either call it from qemuPrepareHostDevices() and qemuDomainAttachHostPciDevice(), OR send a callback function pointer into your new virHostdevPreparePciHostdevs(), with that function returning the "backend to use this time" based on the config setting and current system state.
Thanks for explanation. Will correct that.
cleanup: diff --git a/src/qemu/qemu_hostdev.c b/src/qemu/qemu_hostdev.c index ce5012d..80552cd 100644 --- a/src/qemu/qemu_hostdev.c +++ b/src/qemu/qemu_hostdev.c @@ -583,8 +583,7 @@ qemuHostdevHostSupportsPassthroughLegacy(void)
static bool qemuPrepareHostdevPCICheckSupport(virDomainHostdevDefPtr *hostdevs, - size_t nhostdevs, - virQEMUCapsPtr qemuCaps) + size_t nhostdevs) { bool supportsPassthroughKVM = qemuHostdevHostSupportsPassthroughLegacy(); bool supportsPassthroughVFIO = qemuHostdevHostSupportsPassthroughVFIO(); @@ -601,23 +600,6 @@ qemuPrepareHostdevPCICheckSupport(virDomainHostdevDefPtr *hostdevs, continue;
switch ((virDomainHostdevSubsysPciBackendType) *backend) { - case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT: - if (supportsPassthroughVFIO && - virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VFIO_PCI)) { - *backend = VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO; - } else if (supportsPassthroughKVM && - (virQEMUCapsGet(qemuCaps, QEMU_CAPS_PCIDEVICE) || - virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE))) { - *backend = VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM; - } else { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("host doesn't support passthrough of " - "host PCI devices")); - return false; - } - - break; - case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO: if (!supportsPassthroughVFIO) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", @@ -635,7 +617,7 @@ qemuPrepareHostdevPCICheckSupport(virDomainHostdevDefPtr *hostdevs,
break;
- case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_TYPE_LAST: + default:
I'm pretty sure that Peter intentionally *didn't* put a default case into this switch (and many others) specifically so that somebody adding a new enum value would get compiler errors telling them all the places they needed to handle the new case, i.e. please do not replace "case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_TYPE_LAST" with "default".
break; } } @@ -650,7 +632,6 @@ qemuPrepareHostdevPCIDevices(virQEMUDriverPtr
driver,
const unsigned char *uuid, virDomainHostdevDefPtr *hostdevs, int nhostdevs, - virQEMUCapsPtr qemuCaps, unsigned int flags) { virPCIDeviceListPtr pcidevs = NULL; @@ -659,7 +640,7 @@ qemuPrepareHostdevPCIDevices(virQEMUDriverPtr
driver,
int ret = -1; virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver);
- if (!qemuPrepareHostdevPCICheckSupport(hostdevs, nhostdevs,
qemuCaps))
+ if (!qemuPrepareHostdevPCICheckSupport(hostdevs, nhostdevs)) goto cleanup;
virObjectLock(driver->activePciHostdevs); @@ -1189,7 +1170,6 @@ cleanup: int qemuPrepareHostDevices(virQEMUDriverPtr driver, virDomainDefPtr def, - virQEMUCapsPtr qemuCaps, unsigned int flags) { if (!def->nhostdevs) @@ -1197,7 +1177,7 @@ qemuPrepareHostDevices(virQEMUDriverPtr driver,
if (qemuPrepareHostdevPCIDevices(driver, def->name, def->uuid, def->hostdevs, def->nhostdevs, - qemuCaps, flags) < 0) + flags) < 0) return -1;
if (qemuPrepareHostUSBDevices(driver, def, flags) < 0) diff --git a/src/qemu/qemu_hostdev.h b/src/qemu/qemu_hostdev.h index 710867d..128032d 100644 --- a/src/qemu/qemu_hostdev.h +++ b/src/qemu/qemu_hostdev.h @@ -45,7 +45,6 @@ int qemuPrepareHostdevPCIDevices(virQEMUDriverPtr driver, const unsigned char *uuid, virDomainHostdevDefPtr *hostdevs, int nhostdevs, - virQEMUCapsPtr qemuCaps, unsigned int flags); int qemuFindHostdevUSBDevice(virDomainHostdevDefPtr hostdev, bool mandatory, @@ -59,7 +58,6 @@ int qemuPrepareHostdevSCSIDevices(virQEMUDriverPtr driver, int nhostdevs); int qemuPrepareHostDevices(virQEMUDriverPtr driver, virDomainDefPtr def, - virQEMUCapsPtr qemuCaps, unsigned int flags); void qemuDomainReAttachHostScsiDevices(virQEMUDriverPtr driver, const char *name, diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index c47c5e8..8486f25 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -1163,7 +1163,7 @@ qemuDomainAttachHostPciDevice(virQEMUDriverPtr driver, if (!cfg->relaxedACS) flags |= VIR_STRICT_ACS_CHECK; if (qemuPrepareHostdevPCIDevices(driver, vm->def->name, vm->def->uuid, - &hostdev, 1, priv->qemuCaps, flags) < 0) + &hostdev, 1, flags) < 0) return -1;
/* this could have been changed by qemuPrepareHostdevPCIDevices */ diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index f1fe35e..e938649 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -3690,8 +3690,7 @@ int qemuProcessStart(virConnectPtr conn, hostdev_flags |= VIR_STRICT_ACS_CHECK; if (!migrateFrom) hostdev_flags |= VIR_COLD_BOOT; - if (qemuPrepareHostDevices(driver, vm->def, priv->qemuCaps, - hostdev_flags) < 0) + if (qemuPrepareHostDevices(driver, vm->def, hostdev_flags) < 0) goto cleanup;
VIR_DEBUG("Preparing chr devices"); diff --git a/tests/qemuxml2argvdata/qemuxml2argv-hostdev-pci-address.xml b/tests/qemuxml2argvdata/qemuxml2argv-hostdev-pci-address.xml index 422127c..b9a221a 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-hostdev-pci-address.xml +++ b/tests/qemuxml2argvdata/qemuxml2argv-hostdev-pci-address.xml @@ -24,6 +24,7 @@ <controller type='ide' index='0'/> <controller type='pci' index='0' model='pci-root'/> <hostdev mode='subsystem' type='pci' managed='yes'> + <driver name='kvm'/> <source> <address domain='0x0000' bus='0x06' slot='0x12' function='0x5'/> </source> diff --git a/tests/qemuxml2argvdata/qemuxml2argv-net-hostdev.xml b/tests/qemuxml2argvdata/qemuxml2argv-net-hostdev.xml index d65ef87..9e79348 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-net-hostdev.xml +++ b/tests/qemuxml2argvdata/qemuxml2argv-net-hostdev.xml @@ -24,6 +24,7 @@ <controller type='pci' index='0' model='pci-root'/> <interface type='hostdev' managed='yes'> <mac address='00:11:22:33:44:55'/> + <driver name='kvm'/> <source> <address type='pci' domain='0x0002' bus='0x03' slot='0x07' function='0x1'/> </source> diff --git a/tests/qemuxml2argvdata/qemuxml2argv-pci-rom.xml b/tests/qemuxml2argvdata/qemuxml2argv-pci-rom.xml index a5e59b2..924842b 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-pci-rom.xml +++ b/tests/qemuxml2argvdata/qemuxml2argv-pci-rom.xml @@ -33,12 +33,14 @@ <rom file='/etc/fake/bootrom.bin'/> </interface> <hostdev mode='subsystem' type='pci' managed='yes'> + <driver name='kvm'/> <source> <address domain='0x0000' bus='0x06' slot='0x12' function='0x5'/> </source> <rom bar='off'/> </hostdev> <hostdev mode='subsystem' type='pci' managed='yes'> + <driver name='kvm'/> <source> <address domain='0x0000' bus='0x06' slot='0x12' function='0x6'/> </source> Can't those hostdev definitions keep the default backend like before?
A changes in the test case is sometimes correct, but should be considered a red flag. My first guest would be that it was necessary due to the code that was added to the PostParse function (i.e. it's indicating a problem)

For extracting hostdev codes from qemu_hostdev.c to common library, move add/remove a shareable csi hostdev to/from shared device table outside hostdev functions. Signed-off-by: Chunyan Liu <cyliu@suse.com> --- src/qemu/qemu_hostdev.c | 9 --------- src/qemu/qemu_hotplug.c | 14 ++++++++++++++ src/qemu/qemu_process.c | 28 ++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 9 deletions(-) diff --git a/src/qemu/qemu_hostdev.c b/src/qemu/qemu_hostdev.c index 80552cd..cfdfb03 100644 --- a/src/qemu/qemu_hostdev.c +++ b/src/qemu/qemu_hostdev.c @@ -1061,9 +1061,6 @@ qemuPrepareHostdevSCSIDevices(virQEMUDriverPtr driver, dev.type = VIR_DOMAIN_DEVICE_HOSTDEV; dev.data.hostdev = hostdevs[i]; - if (qemuAddSharedDevice(driver, &dev, name) < 0) - return -1; - if (qemuSetUnprivSGIO(&dev) < 0) return -1; } @@ -1389,12 +1386,6 @@ qemuDomainReAttachHostScsiDevices(virQEMUDriverPtr driver, virDomainHostdevDefPtr hostdev = hostdevs[i]; virSCSIDevicePtr scsi; virSCSIDevicePtr tmp; - virDomainDeviceDef dev; - - dev.type = VIR_DOMAIN_DEVICE_HOSTDEV; - dev.data.hostdev = hostdev; - - ignore_value(qemuRemoveSharedDevice(driver, &dev, name)); if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS || hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI) diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 8486f25..afa92da 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -1548,6 +1548,7 @@ qemuDomainAttachHostScsiDevice(virQEMUDriverPtr driver, char *drvstr = NULL; bool teardowncgroup = false; bool teardownlabel = false; + virDomainDeviceDef dev; if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DRIVE) || !virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE) || @@ -1561,6 +1562,12 @@ qemuDomainAttachHostScsiDevice(virQEMUDriverPtr driver, if (!cont) return -1; + /* check sharable scsi device */ + dev.type = VIR_DOMAIN_DEVICE_HOSTDEV; + dev.data.hostdev = hostdev; + if (qemuAddSharedDevice(driver, &dev, vm->def->name) < 0) + return -1; + if (qemuPrepareHostdevSCSIDevices(driver, vm->def->name, &hostdev, 1)) { virReportError(VIR_ERR_INTERNAL_ERROR, @@ -1619,6 +1626,7 @@ qemuDomainAttachHostScsiDevice(virQEMUDriverPtr driver, ret = 0; cleanup: if (ret < 0) { + ignore_value(qemuRemoveSharedDevice(driver, &dev, vm->def->name)); qemuDomainReAttachHostScsiDevices(driver, vm->def->name, &hostdev, 1); if (teardowncgroup && qemuTeardownHostdevCgroup(vm, hostdev) < 0) VIR_WARN("Unable to remove host device cgroup ACL on hotplug fail"); @@ -2587,6 +2595,12 @@ qemuDomainRemoveSCSIHostDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainHostdevDefPtr hostdev) { + virDomainDeviceDef dev; + + dev.type = VIR_DOMAIN_DEVICE_HOSTDEV; + dev.data.hostdev = hostdev; + + ignore_value(qemuRemoveSharedDevice(driver, &dev, vm->def->name)); qemuDomainReAttachHostScsiDevices(driver, vm->def->name, &hostdev, 1); } diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index e938649..5ea42c7 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -3158,6 +3158,16 @@ qemuProcessReconnect(void *opaque) priv->agentError = true; } + /* update hostdevs */ + for (i = 0; i < obj->def->nhostdevs; i++) { + virDomainDeviceDef dev; + dev.type = VIR_DOMAIN_DEVICE_HOSTDEV; + dev.data.hostdev = obj->def->hostdevs[i]; + /* handle sharable scsi hostdev */ + if (qemuAddSharedDevice(driver, &dev, obj->def->name) < 0) + goto error; + } + if (qemuUpdateActivePciHostdevs(driver, obj->def) < 0) { goto error; } @@ -3686,6 +3696,15 @@ int qemuProcessStart(virConnectPtr conn, /* Must be run before security labelling */ VIR_DEBUG("Preparing host devices"); + for (i = 0; i < vm->def->nhostdevs; i++) { + virDomainDeviceDef dev; + dev.type = VIR_DOMAIN_DEVICE_HOSTDEV; + dev.data.hostdev = vm->def->hostdevs[i]; + /* handle sharable scsi hostdev */ + if (qemuAddSharedDevice(driver, &dev, vm->def->name) < 0) + goto cleanup; + } + if (!cfg->relaxedACS) hostdev_flags |= VIR_STRICT_ACS_CHECK; if (!migrateFrom) @@ -4372,6 +4391,15 @@ void qemuProcessStop(virQEMUDriverPtr driver, priv->ccwaddrs = NULL; } + /* handle shared scsi hostdev */ + for (i = 0; i < vm->def->nhostdevs; i++) { + virDomainDeviceDef dev; + + dev.type = VIR_DOMAIN_DEVICE_HOSTDEV; + dev.data.hostdev = vm->def->hostdevs[i]; + ignore_value(qemuRemoveSharedDevice(driver, &dev, vm->def->name)); + } + qemuDomainReAttachHostDevices(driver, vm->def); def = vm->def; -- 1.6.0.2

On Mon, 2014-02-17 at 14:32 +0800, Chunyan Liu wrote:
For extracting hostdev codes from qemu_hostdev.c to common library, move add/remove a shareable csi hostdev to/from shared device table outside hostdev functions.
Typo csi -> scsi
Signed-off-by: Chunyan Liu <cyliu@suse.com> --- src/qemu/qemu_hostdev.c | 9 --------- src/qemu/qemu_hotplug.c | 14 ++++++++++++++ src/qemu/qemu_process.c | 28 ++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 9 deletions(-)
diff --git a/src/qemu/qemu_hostdev.c b/src/qemu/qemu_hostdev.c index 80552cd..cfdfb03 100644 --- a/src/qemu/qemu_hostdev.c +++ b/src/qemu/qemu_hostdev.c @@ -1061,9 +1061,6 @@ qemuPrepareHostdevSCSIDevices(virQEMUDriverPtr driver, dev.type = VIR_DOMAIN_DEVICE_HOSTDEV; dev.data.hostdev = hostdevs[i];
- if (qemuAddSharedDevice(driver, &dev, name) < 0) - return -1; - if (qemuSetUnprivSGIO(&dev) < 0) return -1; } @@ -1389,12 +1386,6 @@ qemuDomainReAttachHostScsiDevices(virQEMUDriverPtr driver, virDomainHostdevDefPtr hostdev = hostdevs[i]; virSCSIDevicePtr scsi; virSCSIDevicePtr tmp; - virDomainDeviceDef dev; - - dev.type = VIR_DOMAIN_DEVICE_HOSTDEV; - dev.data.hostdev = hostdev; - - ignore_value(qemuRemoveSharedDevice(driver, &dev, name));
if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS || hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI) diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 8486f25..afa92da 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -1548,6 +1548,7 @@ qemuDomainAttachHostScsiDevice(virQEMUDriverPtr driver, char *drvstr = NULL; bool teardowncgroup = false; bool teardownlabel = false; + virDomainDeviceDef dev;
if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DRIVE) || !virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE) || @@ -1561,6 +1562,12 @@ qemuDomainAttachHostScsiDevice(virQEMUDriverPtr driver, if (!cont) return -1;
+ /* check sharable scsi device */ + dev.type = VIR_DOMAIN_DEVICE_HOSTDEV; + dev.data.hostdev = hostdev; + if (qemuAddSharedDevice(driver, &dev, vm->def->name) < 0) + return -1; + if (qemuPrepareHostdevSCSIDevices(driver, vm->def->name, &hostdev, 1)) { virReportError(VIR_ERR_INTERNAL_ERROR, @@ -1619,6 +1626,7 @@ qemuDomainAttachHostScsiDevice(virQEMUDriverPtr driver, ret = 0; cleanup: if (ret < 0) { + ignore_value(qemuRemoveSharedDevice(driver, &dev, vm->def->name)); qemuDomainReAttachHostScsiDevices(driver, vm->def->name, &hostdev, 1); if (teardowncgroup && qemuTeardownHostdevCgroup(vm, hostdev) < 0) VIR_WARN("Unable to remove host device cgroup ACL on hotplug fail"); @@ -2587,6 +2595,12 @@ qemuDomainRemoveSCSIHostDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainHostdevDefPtr hostdev) { + virDomainDeviceDef dev; + + dev.type = VIR_DOMAIN_DEVICE_HOSTDEV; + dev.data.hostdev = hostdev; + + ignore_value(qemuRemoveSharedDevice(driver, &dev, vm->def->name)); qemuDomainReAttachHostScsiDevices(driver, vm->def->name, &hostdev, 1); }
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index e938649..5ea42c7 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -3158,6 +3158,16 @@ qemuProcessReconnect(void *opaque) priv->agentError = true; }
+ /* update hostdevs */ + for (i = 0; i < obj->def->nhostdevs; i++) { + virDomainDeviceDef dev; + dev.type = VIR_DOMAIN_DEVICE_HOSTDEV; + dev.data.hostdev = obj->def->hostdevs[i]; + /* handle sharable scsi hostdev */ + if (qemuAddSharedDevice(driver, &dev, obj->def->name) < 0) + goto error; + } + if (qemuUpdateActivePciHostdevs(driver, obj->def) < 0) { goto error; } @@ -3686,6 +3696,15 @@ int qemuProcessStart(virConnectPtr conn,
/* Must be run before security labelling */ VIR_DEBUG("Preparing host devices"); + for (i = 0; i < vm->def->nhostdevs; i++) { + virDomainDeviceDef dev; + dev.type = VIR_DOMAIN_DEVICE_HOSTDEV; + dev.data.hostdev = vm->def->hostdevs[i]; + /* handle sharable scsi hostdev */ + if (qemuAddSharedDevice(driver, &dev, vm->def->name) < 0) + goto cleanup; + } + if (!cfg->relaxedACS) hostdev_flags |= VIR_STRICT_ACS_CHECK; if (!migrateFrom) @@ -4372,6 +4391,15 @@ void qemuProcessStop(virQEMUDriverPtr driver, priv->ccwaddrs = NULL; }
+ /* handle shared scsi hostdev */ + for (i = 0; i < vm->def->nhostdevs; i++) { + virDomainDeviceDef dev; + + dev.type = VIR_DOMAIN_DEVICE_HOSTDEV; + dev.data.hostdev = vm->def->hostdevs[i]; + ignore_value(qemuRemoveSharedDevice(driver, &dev, vm->def->name)); + } + qemuDomainReAttachHostDevices(driver, vm->def);
def = vm->def;
ACK -- Cedric

Extract code from qemu_hostdev.c and make it reusable for multiple drivers, meanwhile maintain a global hostdev state to solve conflict between different drivers. Signed-off-by: Chunyan Liu <cyliu@suse.com> --- po/POTFILES.in | 1 + src/Makefile.am | 1 + src/libvirt_private.syms | 21 + src/util/virhostdev.c | 1775 ++++++++++++++++++++++++++++++++++++++++++++++ src/util/virhostdev.h | 134 ++++ 5 files changed, 1932 insertions(+), 0 deletions(-) create mode 100644 src/util/virhostdev.c create mode 100644 src/util/virhostdev.h diff --git a/po/POTFILES.in b/po/POTFILES.in index 0359b2f..60c226a 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -160,6 +160,7 @@ src/util/vireventpoll.c src/util/virfile.c src/util/virhash.c src/util/virhook.c +src/util/virhostdev.c src/util/viridentity.c src/util/virinitctl.c src/util/viriptables.c diff --git a/src/Makefile.am b/src/Makefile.am index 3f8d22f..04ea01b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -104,6 +104,7 @@ UTIL_SOURCES = \ util/virhash.c util/virhash.h \ util/virhashcode.c util/virhashcode.h \ util/virhook.c util/virhook.h \ + util/virhostdev.c util/virhostdev.h \ util/viridentity.c util/viridentity.h \ util/virinitctl.c util/virinitctl.h \ util/viriptables.c util/viriptables.h \ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 2c9536a..b283f8e 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1276,6 +1276,27 @@ virHookInitialize; virHookPresent; +#util/virhostdev.h +virHostdevHostSupportsPassthroughKVM; +virHostdevHostSupportsPassthroughVFIO; +virHostdevManagerGetDefault; +virHostdevPciNodeDeviceDetach; +virHostdevPciNodeDeviceReAttach; +virHostdevPciNodeDeviceReset; +virHostdevPrepareDomainHostdevs; +virHostdevPreparePciHostdevs; +virHostdevPrepareScsiHostdevs; +virHostdevPrepareUsbHostdevs; +virHostdevReAttachDomainHostdevs; +virHostdevReAttachPciHostdevs; +virHostdevReAttachScsiHostdevs; +virHostdevReAttachUsbHostdevs; +virHostdevUpdateActiveHostdevs; +virHostdevUpdateActivePciHostdevs; +virHostdevUpdateActiveScsiHostdevs; +virHostdevUpdateActiveUsbHostdevs; + + # util/viridentity.h virIdentityGetAttr; virIdentityGetCurrent; diff --git a/src/util/virhostdev.c b/src/util/virhostdev.c new file mode 100644 index 0000000..8d75a77 --- /dev/null +++ b/src/util/virhostdev.c @@ -0,0 +1,1775 @@ +/* virhostdev.c: hostdev management + * + * Copyright (C) 2014 SUSE LINUX Products GmbH, Nuernberg, Germany. + * Copyright (C) 2006-2007, 2009-2014 Red Hat, Inc. + * Copyright (C) 2006 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * Author: Chunyan Liu <cyliu@suse.com> + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#include <config.h> + +#include <dirent.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> + +#include "virhostdev.h" +#include "viralloc.h" +#include "virstring.h" +#include "virfile.h" +#include "virerror.h" +#include "virlog.h" +#include "virnetdev.h" +#include "virutil.h" +#include "configmake.h" + +#define VIR_FROM_THIS VIR_FROM_NONE + +#define HOSTDEV_STATE_DIR LOCALSTATEDIR "/run/libvirt/hostdevmgr" + +/* for upgrade, may need to find netconfig file in old qemu state dir */ +#define QEMU_STATE_DIR LOCALSTATEDIR "/run/libvirt/qemu" + +static virHostdevManagerPtr hostdevMgr; + +static void +virHostdevManagerCleanup(void) +{ + if (!hostdevMgr) + return; + + virObjectUnref(hostdevMgr->activePciHostdevs); + virObjectUnref(hostdevMgr->inactivePciHostdevs); + virObjectUnref(hostdevMgr->activeUsbHostdevs); + VIR_FREE(hostdevMgr->stateDir); + + VIR_FREE(hostdevMgr); +} + +static int +virHostdevOnceInit(void) +{ + if (VIR_ALLOC(hostdevMgr) < 0) + goto error; + + if ((hostdevMgr->activePciHostdevs = virPCIDeviceListNew()) == NULL) + goto error; + + if ((hostdevMgr->activeUsbHostdevs = virUSBDeviceListNew()) == NULL) + goto error; + + if ((hostdevMgr->inactivePciHostdevs = virPCIDeviceListNew()) == NULL) + goto error; + + if ((hostdevMgr->activeScsiHostdevs = virSCSIDeviceListNew()) == NULL) + goto error; + + if (VIR_STRDUP(hostdevMgr->stateDir, HOSTDEV_STATE_DIR) < 0) + goto error; + + if (virFileMakePath(hostdevMgr->stateDir) < 0) { + virReportError(VIR_ERR_OPERATION_FAILED, + _("Failed to create state dir '%s'"), + hostdevMgr->stateDir); + goto error; + } + + return 0; + +error: + virHostdevManagerCleanup(); + return -1; +} + +VIR_ONCE_GLOBAL_INIT(virHostdev) + +virHostdevManagerPtr +virHostdevManagerGetDefault(void) +{ + if (virHostdevInitialize() < 0) + return NULL; + return hostdevMgr; +} + +static int +virHostdevPciSysfsPath(virDomainHostdevDefPtr hostdev, char **sysfs_path) +{ + virPCIDeviceAddress config_address; + + config_address.domain = hostdev->source.subsys.u.pci.addr.domain; + config_address.bus = hostdev->source.subsys.u.pci.addr.bus; + config_address.slot = hostdev->source.subsys.u.pci.addr.slot; + config_address.function = hostdev->source.subsys.u.pci.addr.function; + + return virPCIDeviceAddressGetSysfsFile(&config_address, sysfs_path); +} + +static int +virHostdevIsVirtualFunction(virDomainHostdevDefPtr hostdev) +{ + char *sysfs_path = NULL; + int ret = -1; + + if (virHostdevPciSysfsPath(hostdev, &sysfs_path) < 0) + return ret; + + ret = virPCIIsVirtualFunction(sysfs_path); + + VIR_FREE(sysfs_path); + + return ret; +} + +static int +virHostdevNetDevice(virDomainHostdevDefPtr hostdev, + char **linkdev, + int *vf) +{ + int ret = -1; + char *sysfs_path = NULL; + + if (virHostdevPciSysfsPath(hostdev, &sysfs_path) < 0) + return ret; + + if (virPCIIsVirtualFunction(sysfs_path) == 1) { + if (virPCIGetVirtualFunctionInfo(sysfs_path, linkdev, vf) < 0) + goto cleanup; + } else { + if (virPCIGetNetName(sysfs_path, linkdev) < 0) + goto cleanup; + *vf = -1; + } + + ret = 0; + +cleanup: + VIR_FREE(sysfs_path); + + return ret; +} + +static int +virHostdevNetConfigVirtPortProfile(const char *linkdev, int vf, + virNetDevVPortProfilePtr virtPort, + const virMacAddr *macaddr, + const unsigned char *uuid, + int associate) +{ + int ret = -1; + + if (!virtPort) + return ret; + + switch (virtPort->virtPortType) { + case VIR_NETDEV_VPORT_PROFILE_NONE: + case VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH: + case VIR_NETDEV_VPORT_PROFILE_8021QBG: + case VIR_NETDEV_VPORT_PROFILE_LAST: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("virtualport type %s is " + "currently not supported on interfaces of type " + "hostdev"), + virNetDevVPortTypeToString(virtPort->virtPortType)); + break; + + case VIR_NETDEV_VPORT_PROFILE_8021QBH: + if (associate) + ret = virNetDevVPortProfileAssociate(NULL, virtPort, macaddr, + linkdev, vf, uuid, + VIR_NETDEV_VPORT_PROFILE_OP_CREATE, false); + else + ret = virNetDevVPortProfileDisassociate(NULL, virtPort, + macaddr, linkdev, vf, + VIR_NETDEV_VPORT_PROFILE_OP_DESTROY); + break; + } + + return ret; +} + +static int +virHostdevNetConfigReplace(virDomainHostdevDefPtr hostdev, + const unsigned char *uuid, + char *stateDir) +{ + char *linkdev = NULL; + virNetDevVlanPtr vlan; + virNetDevVPortProfilePtr virtPort; + int ret = -1; + int vf = -1; + int vlanid = -1; + int port_profile_associate = 1; + int isvf; + + isvf = virHostdevIsVirtualFunction(hostdev); + if (isvf <= 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Interface type hostdev is currently supported on " + "SR-IOV Virtual Functions only")); + return ret; + } + + if (virHostdevNetDevice(hostdev, &linkdev, &vf) < 0) + return ret; + + vlan = virDomainNetGetActualVlan(hostdev->parent.data.net); + virtPort = virDomainNetGetActualVirtPortProfile(hostdev->parent.data.net); + if (virtPort) { + if (vlan) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("direct setting of the vlan tag is not allowed " + "for hostdev devices using %s mode"), + virNetDevVPortTypeToString(virtPort->virtPortType)); + goto cleanup; + } + ret = virHostdevNetConfigVirtPortProfile(linkdev, vf, + virtPort, + &hostdev->parent.data.net->mac, + uuid, + port_profile_associate); + } else { + /* Set only mac and vlan */ + if (vlan) { + if (vlan->nTags != 1 || vlan->trunk) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("vlan trunking is not supported " + "by SR-IOV network devices")); + goto cleanup; + } + if (vf == -1) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("vlan can only be set for SR-IOV VFs, but " + "%s is not a VF"), linkdev); + goto cleanup; + } + vlanid = vlan->tag[0]; + } else if (vf >= 0) { + vlanid = 0; /* assure any current vlan tag is reset */ + } + + ret = virNetDevReplaceNetConfig(linkdev, vf, + &hostdev->parent.data.net->mac, + vlanid, stateDir); + } +cleanup: + VIR_FREE(linkdev); + return ret; +} + +/* oldStateDir: + * For upgrade, if there is an existing VM on QEMU, the hostdev netconfig file + * is previously stored in /var/run/libvirt/qemu. With new version, it tries to + * find in hostdevManager->stateDir location but certainly it cannot find it. + * In this case, we should find in the old state dir. + */ +static int +virHostdevNetConfigRestore(virDomainHostdevDefPtr hostdev, + char *stateDir, + char *oldStateDir) +{ + char *linkdev = NULL; + virNetDevVPortProfilePtr virtPort; + int ret = -1; + int vf = -1; + int port_profile_associate = 0; + int isvf; + + /* This is only needed for PCI devices that have been defined + * using <interface type='hostdev'>. For all others, it is a NOP. + */ + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS || + hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI || + hostdev->parent.type != VIR_DOMAIN_DEVICE_NET || + !hostdev->parent.data.net) + return 0; + + isvf = virHostdevIsVirtualFunction(hostdev); + if (isvf <= 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Interface type hostdev is currently supported on " + "SR-IOV Virtual Functions only")); + return ret; + } + + if (virHostdevNetDevice(hostdev, &linkdev, &vf) < 0) + return ret; + + virtPort = virDomainNetGetActualVirtPortProfile(hostdev->parent.data.net); + if (virtPort) + ret = virHostdevNetConfigVirtPortProfile(linkdev, vf, virtPort, + &hostdev->parent.data.net->mac, + NULL, port_profile_associate); + else { + ret = virNetDevRestoreNetConfig(linkdev, vf, stateDir); + if (ret < 0 && oldStateDir != NULL) + ret = virNetDevRestoreNetConfig(linkdev, vf, oldStateDir); + } + + VIR_FREE(linkdev); + + return ret; +} + +static virPCIDeviceListPtr +virHostdevGetPciHostDeviceList(virDomainHostdevDefPtr *hostdevs, int nhostdevs) +{ + virPCIDeviceListPtr list; + size_t i; + + if (!(list = virPCIDeviceListNew())) + return NULL; + + for (i = 0; i < nhostdevs; i++) { + virDomainHostdevDefPtr hostdev = hostdevs[i]; + virPCIDevicePtr dev; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) + continue; + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) + continue; + + dev = virPCIDeviceNew(hostdev->source.subsys.u.pci.addr.domain, + hostdev->source.subsys.u.pci.addr.bus, + hostdev->source.subsys.u.pci.addr.slot, + hostdev->source.subsys.u.pci.addr.function); + if (!dev) { + virObjectUnref(list); + return NULL; + } + + if (virPCIDeviceListAdd(list, dev) < 0) { + virPCIDeviceFree(dev); + virObjectUnref(list); + return NULL; + } + + virPCIDeviceSetManaged(dev, hostdev->managed); + + switch (hostdev->source.subsys.u.pci.backend) { + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO: + if (virPCIDeviceSetStubDriver(dev, "vfio-pci") < 0) { + virObjectUnref(list); + return NULL; + } + break; + + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM : + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT: + if (virPCIDeviceSetStubDriver(dev, "pci-stub") < 0) { + virObjectUnref(list); + return NULL; + } + break; + } + } + + return list; +} + +bool +virHostdevHostSupportsPassthroughVFIO(void) +{ + DIR *iommuDir = NULL; + struct dirent *iommuGroup = NULL; + bool ret = false; + + /* condition 1 - /sys/kernel/iommu_groups/ contains entries */ + if (!(iommuDir = opendir("/sys/kernel/iommu_groups/"))) + goto cleanup; + + while ((iommuGroup = readdir(iommuDir))) { + /* skip ./ ../ */ + if (STRPREFIX(iommuGroup->d_name, ".")) + continue; + + /* assume we found a group */ + break; + } + + if (!iommuGroup) + goto cleanup; + /* okay, iommu is on and recognizes groups */ + + /* condition 2 - /dev/vfio/vfio exists */ + if (!virFileExists("/dev/vfio/vfio")) + goto cleanup; + + ret = true; + +cleanup: + if (iommuDir) + closedir(iommuDir); + + return ret; +} + + +#if HAVE_LINUX_KVM_H +# include <linux/kvm.h> +bool +virHostdevHostSupportsPassthroughKVM(void) +{ + int kvmfd = -1; + bool ret = false; + + if ((kvmfd = open("/dev/kvm", O_RDONLY)) < 0) + goto cleanup; + +# ifdef KVM_CAP_IOMMU + if ((ioctl(kvmfd, KVM_CHECK_EXTENSION, KVM_CAP_IOMMU)) <= 0) + goto cleanup; + + ret = true; +# endif + +cleanup: + VIR_FORCE_CLOSE(kvmfd); + + return ret; +} +#else +bool +virHostdevHostSupportsPassthroughKVM(void) +{ + return false; +} +#endif + +static bool +virHostdevPCICheckSupport(virDomainHostdevDefPtr *hostdevs, + size_t nhostdevs) +{ + bool supportsPassthroughVFIO = virHostdevHostSupportsPassthroughVFIO(); + bool supportsPassthroughKVM = virHostdevHostSupportsPassthroughKVM(); + size_t i; + + /* assign defaults for hostdev passthrough */ + for (i = 0; i < nhostdevs; i++) { + virDomainHostdevDefPtr hostdev = hostdevs[i]; + int *backend = &hostdev->source.subsys.u.pci.backend; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) + continue; + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) + continue; + + switch ((virDomainHostdevSubsysPciBackendType) *backend) { + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO: + if (!supportsPassthroughVFIO) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("host doesn't support VFIO PCI passthrough")); + return false; + } + break; + + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM: + if (!supportsPassthroughKVM) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("host doesn't support legacy PCI passthrough")); + return false; + } + + break; + + default: + break; + } + } + + return true; +} +int +virHostdevPreparePciHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name, + const unsigned char *uuid, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs, + unsigned int flags) +{ + virPCIDeviceListPtr pcidevs = NULL; + int last_processed_hostdev_vf = -1; + size_t i; + int ret = -1; + + if (!nhostdevs) + return 0; + if (mgr == NULL) + return -1; + + virObjectLock(mgr->activePciHostdevs); + virObjectLock(mgr->inactivePciHostdevs); + + if (!virHostdevPCICheckSupport(hostdevs, nhostdevs)) + goto cleanup; + + if (!(pcidevs = virHostdevGetPciHostDeviceList(hostdevs, nhostdevs))) + goto cleanup; + + /* We have to use 9 loops here. *All* devices must + * be detached before we reset any of them, because + * in some cases you have to reset the whole PCI, + * which impacts all devices on it. Also, all devices + * must be reset before being marked as active. + */ + + /* Loop 1: validate that non-managed device isn't in use, eg + * by checking that device is either un-bound, or bound + * to stub driver + */ + + for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) { + virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i); + virPCIDevicePtr other; + bool strict_acs_check = !!(flags & VIR_STRICT_ACS_CHECK); + + if (!virPCIDeviceIsAssignable(dev, strict_acs_check)) { + virReportError(VIR_ERR_OPERATION_INVALID, + _("PCI device %s is not assignable"), + virPCIDeviceGetName(dev)); + goto cleanup; + } + /* The device is in use by other active domain if + * the dev is in list activePciHostdevs. + */ + if ((other = virPCIDeviceListFind(mgr->activePciHostdevs, dev))) { + const char *other_drvname; + const char *other_domname; + + virPCIDeviceGetUsedBy(other, &other_drvname, &other_domname); + if (other_drvname && other_domname) + virReportError(VIR_ERR_OPERATION_INVALID, + _("PCI device %s is in use by driver %s,domain %s"), + virPCIDeviceGetName(dev), other_drvname, + other_domname); + else + virReportError(VIR_ERR_OPERATION_INVALID, + _("PCI device %s is already in use"), + virPCIDeviceGetName(dev)); + goto cleanup; + } + } + + /* Loop 2: detach managed devices */ + for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) { + virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i); + if (virPCIDeviceGetManaged(dev) && + virPCIDeviceDetach(dev, mgr->activePciHostdevs, NULL) < 0) + goto reattachdevs; + } + + /* Loop 3: Now that all the PCI hostdevs have been detached, we + * can safely reset them */ + for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) { + virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i); + if (virPCIDeviceReset(dev, mgr->activePciHostdevs, + mgr->inactivePciHostdevs) < 0) + goto reattachdevs; + } + + /* Loop 4: For SRIOV network devices, Now that we have detached the + * the network device, set the netdev config */ + for (i = 0; i < nhostdevs; i++) { + virDomainHostdevDefPtr hostdev = hostdevs[i]; + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) + continue; + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) + continue; + if (hostdev->parent.type == VIR_DOMAIN_DEVICE_NET && + hostdev->parent.data.net) { + if (virHostdevNetConfigReplace(hostdev, uuid, mgr->stateDir) < 0) + goto resetvfnetconfig; + } + last_processed_hostdev_vf = i; + } + + /* Loop 5: Now mark all the devices as active */ + for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) { + virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i); + if (virPCIDeviceListAdd(mgr->activePciHostdevs, dev) < 0) + goto inactivedevs; + } + + /* Loop 6: Now remove the devices from inactive list. */ + for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) { + virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i); + virPCIDeviceListDel(mgr->inactivePciHostdevs, dev); + } + + /* Loop 7: Now set the used_by_domain of the device in + * driver->activePciHostdevs as domain name. + */ + for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) { + virPCIDevicePtr dev, activeDev; + + dev = virPCIDeviceListGet(pcidevs, i); + activeDev = virPCIDeviceListFind(mgr->activePciHostdevs, dev); + + if (activeDev) + virPCIDeviceSetUsedBy(activeDev, drv_name, dom_name); + } + + /* Loop 8: Now set the original states for hostdev def */ + for (i = 0; i < nhostdevs; i++) { + virPCIDevicePtr dev; + virPCIDevicePtr pcidev; + virDomainHostdevDefPtr hostdev = hostdevs[i]; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) + continue; + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) + continue; + + dev = virPCIDeviceNew(hostdev->source.subsys.u.pci.addr.domain, + hostdev->source.subsys.u.pci.addr.bus, + hostdev->source.subsys.u.pci.addr.slot, + hostdev->source.subsys.u.pci.addr.function); + + /* original states "unbind_from_stub", "remove_slot", + * "reprobe" were already set by pciDettachDevice in + * loop 2. + */ + if ((pcidev = virPCIDeviceListFind(pcidevs, dev))) { + hostdev->origstates.states.pci.unbind_from_stub = + virPCIDeviceGetUnbindFromStub(pcidev); + hostdev->origstates.states.pci.remove_slot = + virPCIDeviceGetRemoveSlot(pcidev); + hostdev->origstates.states.pci.reprobe = + virPCIDeviceGetReprobe(pcidev); + } + + virPCIDeviceFree(dev); + } + + /* Loop 9: Now steal all the devices from pcidevs */ + while (virPCIDeviceListCount(pcidevs) > 0) + virPCIDeviceListStealIndex(pcidevs, 0); + + ret = 0; + goto cleanup; + +inactivedevs: + /* Only steal all the devices from driver->activePciHostdevs. We will + * free them in virObjectUnref(). + */ + while (virPCIDeviceListCount(pcidevs) > 0) { + virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, 0); + virPCIDeviceListSteal(mgr->activePciHostdevs, dev); + } + +resetvfnetconfig: + for (i = 0; + last_processed_hostdev_vf != -1 && i < last_processed_hostdev_vf; i++) + virHostdevNetConfigRestore(hostdevs[i], mgr->stateDir, NULL); + +reattachdevs: + for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) { + virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i); + virPCIDeviceReattach(dev, mgr->activePciHostdevs, NULL); + } + +cleanup: + virObjectUnlock(mgr->activePciHostdevs); + virObjectUnlock(mgr->inactivePciHostdevs); + virObjectUnref(pcidevs); + return ret; +} + +static int +virHostdevFindUSBDevice(virDomainHostdevDefPtr hostdev, + bool mandatory, + virUSBDevicePtr *usb) +{ + unsigned vendor = hostdev->source.subsys.u.usb.vendor; + unsigned product = hostdev->source.subsys.u.usb.product; + unsigned bus = hostdev->source.subsys.u.usb.bus; + unsigned device = hostdev->source.subsys.u.usb.device; + bool autoAddress = hostdev->source.subsys.u.usb.autoAddress; + int rc; + + *usb = NULL; + + if (vendor && bus) { + rc = virUSBDeviceFind(vendor, product, bus, device, + NULL, + autoAddress ? false : mandatory, + usb); + if (rc < 0) { + return -1; + } else if (!autoAddress) { + goto out; + } else { + VIR_INFO("USB device %x:%x could not be found at previous" + " address (bus:%u device:%u)", + vendor, product, bus, device); + } + } + + /* When vendor is specified, its USB address is either unspecified or the + * device could not be found at the USB device where it had been + * automatically found before. + */ + if (vendor) { + virUSBDeviceListPtr devs; + + rc = virUSBDeviceFindByVendor(vendor, product, NULL, mandatory, &devs); + if (rc < 0) + return -1; + + if (rc == 1) { + *usb = virUSBDeviceListGet(devs, 0); + virUSBDeviceListSteal(devs, *usb); + } + virObjectUnref(devs); + + if (rc == 0) { + goto out; + } else if (rc > 1) { + if (autoAddress) { + virReportError(VIR_ERR_OPERATION_FAILED, + _("Multiple USB devices for %x:%x were found, " + "but none of them is at bus:%u device:%u"), + vendor, product, bus, device); + } else { + virReportError(VIR_ERR_OPERATION_FAILED, + _("Multiple USB devices for %x:%x, " + "use <address> to specify one"), + vendor, product); + } + return -1; + } + + hostdev->source.subsys.u.usb.bus = virUSBDeviceGetBus(*usb); + hostdev->source.subsys.u.usb.device = virUSBDeviceGetDevno(*usb); + hostdev->source.subsys.u.usb.autoAddress = true; + + if (autoAddress) { + VIR_INFO("USB device %x:%x found at bus:%u device:%u (moved" + " from bus:%u device:%u)", + vendor, product, + hostdev->source.subsys.u.usb.bus, + hostdev->source.subsys.u.usb.device, + bus, device); + } + } else if (!vendor && bus) { + if (virUSBDeviceFindByBus(bus, device, NULL, mandatory, usb) < 0) + return -1; + } + +out: + if (!*usb) + hostdev->missing = true; + return 0; +} + +static int +virHostdevMarkUsbHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name, + virUSBDeviceListPtr list) +{ + size_t i, j; + unsigned int count; + virUSBDevicePtr tmp; + + virObjectLock(mgr->activeUsbHostdevs); + count = virUSBDeviceListCount(list); + + for (i = 0; i < count; i++) { + virUSBDevicePtr usb = virUSBDeviceListGet(list, i); + if ((tmp = virUSBDeviceListFind(mgr->activeUsbHostdevs, usb))) { + const char *other_drvname; + const char *other_domname; + + virUSBDeviceGetUsedBy(tmp, &other_drvname, &other_domname); + if (other_drvname && other_domname) + virReportError(VIR_ERR_OPERATION_INVALID, + _("USB device %s is in use by " + "driver %s,domain %s"), + virUSBDeviceGetName(tmp), + other_drvname, other_domname); + else + virReportError(VIR_ERR_OPERATION_INVALID, + _("USB device %s is already in use"), + virUSBDeviceGetName(tmp)); + goto error; + } + + virUSBDeviceSetUsedBy(usb, drv_name, dom_name); + VIR_DEBUG("Adding %03d.%03d driver= %s dom=%s to activeUsbHostdevs", + virUSBDeviceGetBus(usb), virUSBDeviceGetDevno(usb), + drv_name, dom_name); + /* + * The caller is responsible to steal these usb devices + * from the virUSBDeviceList that passed in on success, + * perform rollback on failure. + */ + if (virUSBDeviceListAdd(mgr->activeUsbHostdevs, usb) < 0) + goto error; + } + + virObjectUnlock(mgr->activeUsbHostdevs); + return 0; + +error: + for (j = 0; j < i; j++) { + tmp = virUSBDeviceListGet(list, j); + virUSBDeviceListSteal(mgr->activeUsbHostdevs, tmp); + } + virObjectUnlock(mgr->activeUsbHostdevs); + return -1; +} + +int +virHostdevPrepareUsbHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs, + unsigned int flags) +{ + size_t i; + int ret = -1; + virUSBDeviceListPtr list; + virUSBDevicePtr tmp; + bool coldBoot = !!(flags & VIR_COLD_BOOT); + + if (!nhostdevs) + return 0; + if (mgr == NULL) + return -1; + + /* To prevent situation where USB device is assigned to two domains + * we need to keep a list of currently assigned USB devices. + * This is done in several loops which cannot be joined into one + * big loop. + */ + if (!(list = virUSBDeviceListNew())) + goto cleanup; + + /* Loop 1: build temporary list + */ + for (i = 0; i < nhostdevs; i++) { + virDomainHostdevDefPtr hostdev = hostdevs[i]; + bool required = true; + virUSBDevicePtr usb; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) + continue; + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) + continue; + + if (hostdev->startupPolicy == VIR_DOMAIN_STARTUP_POLICY_OPTIONAL || + (hostdev->startupPolicy == VIR_DOMAIN_STARTUP_POLICY_REQUISITE && + !coldBoot)) + required = false; + + if (virHostdevFindUSBDevice(hostdev, required, &usb) < 0) + goto cleanup; + + if (usb && virUSBDeviceListAdd(list, usb) < 0) { + virUSBDeviceFree(usb); + goto cleanup; + } + } + + /* Mark devices in temporary list as used by @name + * and add them do active list. However, if something goes + * wrong, perform rollback. + */ + if (virHostdevMarkUsbHostdevs(mgr, drv_name, dom_name, list) < 0) + goto cleanup; + + /* Loop 2: Temporary list was successfully merged with + * active list, so steal all items to avoid freeing them + * in cleanup label. + */ + while (virUSBDeviceListCount(list) > 0) { + tmp = virUSBDeviceListGet(list, 0); + virUSBDeviceListSteal(list, tmp); + } + + ret = 0; + +cleanup: + virObjectUnref(list); + return ret; +} + +static int +virHostdevSetUnprivSGIO(virDomainDeviceDefPtr dev) +{ + virDomainHostdevDefPtr hostdev = NULL; + char *sysfs_path = NULL; + char *path = NULL; + char *hostdev_name = NULL; + char *hostdev_path = NULL; + int val = -1; + int ret = 0; + + if (dev->type != VIR_DOMAIN_DEVICE_HOSTDEV) { + hostdev = dev->data.hostdev; + + if (!hostdev->shareable || + !(hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS && + hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI)) + return 0; + + if (!(hostdev_name = virSCSIDeviceGetDevName(NULL, + hostdev->source.subsys.u.scsi.adapter, + hostdev->source.subsys.u.scsi.bus, + hostdev->source.subsys.u.scsi.target, + hostdev->source.subsys.u.scsi.unit))) + goto cleanup; + + if (virAsprintf(&hostdev_path, "/dev/%s", hostdev_name) < 0) + goto cleanup; + + path = hostdev_path; + } else { + return 0; + } + + sysfs_path = virGetUnprivSGIOSysfsPath(path, NULL); + if (sysfs_path == NULL) { + ret = -1; + goto cleanup; + } + + /* By default, filter the SG_IO commands, i.e. set unpriv_sgio to 0. */ + val = (hostdev->source.subsys.u.scsi.sgio == + VIR_DOMAIN_DEVICE_SGIO_UNFILTERED); + + /* Do not do anything if unpriv_sgio is not supported by the kernel and the + * whitelist is enabled. But if requesting unfiltered access, always call + * virSetDeviceUnprivSGIO, to report an error for unsupported unpriv_sgio. + */ + if ((virFileExists(sysfs_path) || val == 1) && + virSetDeviceUnprivSGIO(path, NULL, val) < 0) + ret = -1; + +cleanup: + VIR_FREE(sysfs_path); + VIR_FREE(hostdev_name); + VIR_FREE(hostdev_path); + return ret; +} + +int +virHostdevPrepareScsiHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs) +{ + size_t i, j; + int count; + virSCSIDeviceListPtr list; + virSCSIDevicePtr tmp; + + if (!nhostdevs) + return 0; + if (mgr == NULL) + return -1; + + /* Loop 1 */ + for (i = 0; i < nhostdevs; i++) { + virDomainDeviceDef dev; + + dev.type = VIR_DOMAIN_DEVICE_HOSTDEV; + dev.data.hostdev = hostdevs[i]; + + if (virHostdevSetUnprivSGIO(&dev) < 0) + return -1; + } + + /* To prevent situation where SCSI device is assigned to two domains + * we need to keep a list of currently assigned SCSI devices. + */ + if (!(list = virSCSIDeviceListNew())) + goto cleanup; + + /* Loop 2: build temporary list */ + for (i = 0; i < nhostdevs; i++) { + virDomainHostdevDefPtr hostdev = hostdevs[i]; + virSCSIDevicePtr scsi; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS || + hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI) + continue; + + if (hostdev->managed) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("SCSI host device doesn't support managed mode")); + goto cleanup; + } + + if (!(scsi = virSCSIDeviceNew(NULL, + hostdev->source.subsys.u.scsi.adapter, + hostdev->source.subsys.u.scsi.bus, + hostdev->source.subsys.u.scsi.target, + hostdev->source.subsys.u.scsi.unit, + hostdev->readonly, + hostdev->shareable))) + goto cleanup; + + if (scsi && virSCSIDeviceListAdd(list, scsi) < 0) { + virSCSIDeviceFree(scsi); + goto cleanup; + } + } + + /* Loop 3: Mark devices in temporary list as used by @drv_name & @dom_name, + * and add them to driver list. However, if something goes wrong, perform + * rollback. + */ + virObjectLock(mgr->activeScsiHostdevs); + count = virSCSIDeviceListCount(list); + + for (i = 0; i < count; i++) { + virSCSIDevicePtr scsi = virSCSIDeviceListGet(list, i); + if ((tmp = virSCSIDeviceListFind(mgr->activeScsiHostdevs, scsi))) { + bool scsi_shareable = virSCSIDeviceGetShareable(scsi); + bool tmp_shareable = virSCSIDeviceGetShareable(tmp); + + if (!(scsi_shareable && tmp_shareable)) { + virReportError(VIR_ERR_OPERATION_INVALID, + _("SCSI device %s is already in use by " + "other domain(s) as '%s'"), + virSCSIDeviceGetName(tmp), + tmp_shareable ? "shareable" : "non-shareable"); + goto error; + } + + if (virSCSIDeviceSetUsedBy(tmp, drv_name, dom_name) < 0) + goto error; + } else { + if (virSCSIDeviceSetUsedBy(scsi, drv_name, dom_name) < 0) + goto error; + + VIR_DEBUG("Adding %s to activeScsiHostdevs", virSCSIDeviceGetName(scsi)); + + if (virSCSIDeviceListAdd(mgr->activeScsiHostdevs, scsi) < 0) + goto error; + + } + } + + virObjectUnlock(mgr->activeScsiHostdevs); + + /* Loop 3: Temporary list was successfully merged with + * driver list, so steal all items to avoid freeing them + * when freeing temporary list. + */ + while (virSCSIDeviceListCount(list) > 0) { + tmp = virSCSIDeviceListGet(list, 0); + virSCSIDeviceListSteal(list, tmp); + } + + virObjectUnref(list); + return 0; + +error: + for (j = 0; j < i; j++) { + tmp = virSCSIDeviceListGet(list, j); + virSCSIDeviceListSteal(mgr->activeScsiHostdevs, tmp); + } + virObjectUnlock(mgr->activeScsiHostdevs); +cleanup: + virObjectUnref(list); + return -1; +} + +int +virHostdevPrepareDomainHostdevs(virHostdevManagerPtr mgr, + const char *driver, + virDomainDefPtr def, + unsigned int flags) +{ + if (!def->nhostdevs) + return 0; + + if (mgr == NULL) + return -1; + + if (flags & VIR_SP_PCI_HOSTDEV) { + if (virHostdevPreparePciHostdevs(mgr, driver, + def->name, def->uuid, + def->hostdevs, + def->nhostdevs, + flags) < 0) + return -1; + } + + if (flags & VIR_SP_USB_HOSTDEV) { + if (virHostdevPrepareUsbHostdevs(mgr, driver, def->name, + def->hostdevs, def->nhostdevs, + flags) < 0) + return -1; + } + + if (flags & VIR_SP_SCSI_HOSTDEV) { + if (virHostdevPrepareScsiHostdevs(mgr, driver, def->name, + def->hostdevs, def->nhostdevs) < 0) + return -1; + } + + return 0; +} + +/* + * Pre-condition: mgr->inactivePciHostdevs & mgr->activePciHostdevs + * are locked + */ +static void +virHostdevReAttachPciDevice(virHostdevManagerPtr mgr, virPCIDevicePtr dev) +{ + /* If the device is not managed and was attached to guest + * successfully, it must have been inactive. + */ + if (!virPCIDeviceGetManaged(dev)) { + if (virPCIDeviceListAdd(mgr->inactivePciHostdevs, dev) < 0) + virPCIDeviceFree(dev); + return; + } + + /* Wait for device cleanup if it is qemu/kvm */ + if (STREQ(virPCIDeviceGetStubDriver(dev), "pci-stub")) { + int retries = 100; + while (virPCIDeviceWaitForCleanup(dev, "kvm_assigned_device") + && retries) { + usleep(100*1000); + retries--; + } + } + + if (virPCIDeviceReattach(dev, mgr->activePciHostdevs, + mgr->inactivePciHostdevs) < 0) { + virErrorPtr err = virGetLastError(); + VIR_ERROR(_("Failed to re-attach PCI device: %s"), + err ? err->message : _("unknown error")); + virResetError(err); + } + virPCIDeviceFree(dev); +} + +/* + * Pre-condition: mgr->activePciHostdevs is locked + */ +static virPCIDeviceListPtr +virHostdevGetActivePciHostDeviceList(virHostdevManagerPtr mgr, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs) +{ + virPCIDeviceListPtr list; + size_t i; + + if (!(list = virPCIDeviceListNew())) + return NULL; + + for (i = 0; i < nhostdevs; i++) { + virDomainHostdevDefPtr hostdev = hostdevs[i]; + virPCIDevicePtr dev; + virPCIDevicePtr activeDev; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) + continue; + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) + continue; + + dev = virPCIDeviceNew(hostdev->source.subsys.u.pci.addr.domain, + hostdev->source.subsys.u.pci.addr.bus, + hostdev->source.subsys.u.pci.addr.slot, + hostdev->source.subsys.u.pci.addr.function); + if (!dev) { + virObjectUnref(list); + return NULL; + } + + if ((activeDev = virPCIDeviceListFind(mgr->activePciHostdevs, dev))) { + if (virPCIDeviceListAdd(list, activeDev) < 0) { + virPCIDeviceFree(dev); + virObjectUnref(list); + return NULL; + } + } + + virPCIDeviceFree(dev); + } + + return list; +} + +int +virHostdevUpdateActivePciHostdevs(virHostdevManagerPtr mgr, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs, + const char *drv_name, + const char *dom_name) +{ + virDomainHostdevDefPtr hostdev = NULL; + virPCIDevicePtr dev = NULL; + size_t i; + int ret = -1; + + if (!nhostdevs) + return 0; + + if (!mgr) + return -1; + + virObjectLock(mgr->activePciHostdevs); + virObjectLock(mgr->inactivePciHostdevs); + + for (i = 0; i < nhostdevs; i++) { + hostdev = hostdevs[i]; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) + continue; + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) + continue; + + dev = virPCIDeviceNew(hostdev->source.subsys.u.pci.addr.domain, + hostdev->source.subsys.u.pci.addr.bus, + hostdev->source.subsys.u.pci.addr.slot, + hostdev->source.subsys.u.pci.addr.function); + + if (!dev) + goto cleanup; + + virPCIDeviceSetManaged(dev, hostdev->managed); + switch (hostdev->source.subsys.u.pci.backend) { + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO: + if (virPCIDeviceSetStubDriver(dev, "vfio-pci") < 0) + goto cleanup; + break; + + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM : + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT: + if (virPCIDeviceSetStubDriver(dev, "pci-stub") < 0) + goto cleanup; + break; + } + virPCIDeviceSetUsedBy(dev, drv_name, dom_name); + + /* Setup the original states for the PCI device */ + virPCIDeviceSetUnbindFromStub(dev, hostdev->origstates.states.pci.unbind_from_stub); + virPCIDeviceSetRemoveSlot(dev, hostdev->origstates.states.pci.remove_slot); + virPCIDeviceSetReprobe(dev, hostdev->origstates.states.pci.reprobe); + + if (virPCIDeviceListAdd(mgr->activePciHostdevs, dev) < 0) + goto cleanup; + dev = NULL; + } + + ret = 0; +cleanup: + virPCIDeviceFree(dev); + virObjectUnlock(mgr->activePciHostdevs); + virObjectUnlock(mgr->inactivePciHostdevs); + return ret; +} + + +int +virHostdevUpdateActiveUsbHostdevs(virHostdevManagerPtr mgr, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs, + const char *drv_name, + const char *dom_name) +{ + virDomainHostdevDefPtr hostdev = NULL; + size_t i; + int ret = -1; + + if (!nhostdevs) + return 0; + + if (!mgr) + return -1; + + virObjectLock(mgr->activeUsbHostdevs); + for (i = 0; i < nhostdevs; i++) { + virUSBDevicePtr usb = NULL; + hostdev = hostdevs[i]; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) + continue; + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) + continue; + + usb = virUSBDeviceNew(hostdev->source.subsys.u.usb.bus, + hostdev->source.subsys.u.usb.device, + NULL); + if (!usb) { + VIR_WARN("Unable to reattach USB device %03d.%03d " + "on driver %s domain %s", + hostdev->source.subsys.u.usb.bus, + hostdev->source.subsys.u.usb.device, + drv_name, dom_name); + continue; + } + + virUSBDeviceSetUsedBy(usb, drv_name, dom_name); + + if (virUSBDeviceListAdd(mgr->activeUsbHostdevs, usb) < 0) { + virUSBDeviceFree(usb); + goto cleanup; + } + } + ret = 0; +cleanup: + virObjectUnlock(mgr->activeUsbHostdevs); + return ret; +} + +int +virHostdevUpdateActiveScsiHostdevs(virHostdevManagerPtr mgr, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs, + const char *drv_name, + const char *dom_name) +{ + virDomainHostdevDefPtr hostdev = NULL; + size_t i; + int ret = -1; + virSCSIDevicePtr scsi = NULL; + virSCSIDevicePtr tmp = NULL; + + if (!nhostdevs) + return 0; + + if (!mgr) + return -1; + + virObjectLock(mgr->activeScsiHostdevs); + for (i = 0; i < nhostdevs; i++) { + hostdev = hostdevs[i]; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS || + hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI) + continue; + + if (!(scsi = virSCSIDeviceNew(NULL, + hostdev->source.subsys.u.scsi.adapter, + hostdev->source.subsys.u.scsi.bus, + hostdev->source.subsys.u.scsi.target, + hostdev->source.subsys.u.scsi.unit, + hostdev->readonly, + hostdev->shareable))) + goto cleanup; + + if ((tmp = virSCSIDeviceListFind(mgr->activeScsiHostdevs, scsi))) { + if (virSCSIDeviceSetUsedBy(tmp, drv_name, dom_name) < 0) { + virSCSIDeviceFree(scsi); + goto cleanup; + } + virSCSIDeviceFree(scsi); + } else { + if (virSCSIDeviceSetUsedBy(scsi, drv_name, dom_name) < 0 || + virSCSIDeviceListAdd(mgr->activeScsiHostdevs, scsi) < 0) { + virSCSIDeviceFree(scsi); + goto cleanup; + } + } + } + ret = 0; + +cleanup: + virObjectUnlock(mgr->activeScsiHostdevs); + return ret; +} + +int +virHostdevUpdateActiveHostdevs(virHostdevManagerPtr mgr, + const char *driver, + virDomainDefPtr def, + unsigned int flags) +{ + if (!def->nhostdevs) + return 0; + + if (flags & VIR_SP_PCI_HOSTDEV) { + if (virHostdevUpdateActivePciHostdevs(mgr, + def->hostdevs, + def->nhostdevs, + driver, def->name) < 0) + return -1; + } + + if (flags & VIR_SP_USB_HOSTDEV) { + if (virHostdevUpdateActiveUsbHostdevs(mgr, + def->hostdevs, + def->nhostdevs, + driver, def->name) < 0) + return -1; + } + + if (flags & VIR_SP_SCSI_HOSTDEV) { + if (virHostdevUpdateActiveScsiHostdevs(mgr, + def->hostdevs, + def->nhostdevs, + driver, def->name) < 0) + return -1; + } + + return 0; +} + + +void +virHostdevReAttachPciHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs) +{ + virPCIDeviceListPtr pcidevs; + size_t i; + char *oldStateDir = NULL; + + if (!nhostdevs || !mgr) + return; + + virObjectLock(mgr->activePciHostdevs); + virObjectLock(mgr->inactivePciHostdevs); + + /* for upgrade, if netconfig file is not found in mgr->stateDir, + * find in old state dir */ + if (VIR_STRDUP(oldStateDir, QEMU_STATE_DIR) < 0) + goto cleanup; + + if (!(pcidevs = virHostdevGetActivePciHostDeviceList(mgr, + hostdevs, + nhostdevs))) { + virErrorPtr err = virGetLastError(); + VIR_ERROR(_("Failed to allocate PCI device list: %s"), + err ? err->message : _("unknown error")); + virResetError(err); + goto cleanup; + } + + /* Again 4 loops; mark all devices as inactive before reset + * them and reset all the devices before re-attach. + * Attach mac and port profile parameters to devices + */ + for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) { + virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i); + virPCIDevicePtr activeDev = NULL; + const char *usedby_drvname; + const char *usedby_domname; + + /* Never delete the dev from list driver->activePciHostdevs + * if it's used by other domain. + */ + activeDev = virPCIDeviceListFind(mgr->activePciHostdevs, dev); + if (activeDev) { + virPCIDeviceGetUsedBy(activeDev, &usedby_drvname, &usedby_domname); + if (STRNEQ_NULLABLE(drv_name, usedby_drvname) || + STRNEQ_NULLABLE(dom_name, usedby_domname)) { + virPCIDeviceListSteal(pcidevs, dev); + continue; + } + } + + virPCIDeviceListSteal(mgr->activePciHostdevs, dev); + } + + /* + * For SRIOV net host devices, unset mac and port profile before + * reset and reattach device + */ + for (i = 0; i < nhostdevs; i++) + virHostdevNetConfigRestore(hostdevs[i], mgr->stateDir, oldStateDir); + + for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) { + virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i); + if (virPCIDeviceReset(dev, mgr->activePciHostdevs, + mgr->inactivePciHostdevs) < 0) { + virErrorPtr err = virGetLastError(); + VIR_ERROR(_("Failed to reset PCI device: %s"), + err ? err->message : _("unknown error")); + virResetError(err); + } + } + + while (virPCIDeviceListCount(pcidevs) > 0) { + virPCIDevicePtr dev = virPCIDeviceListStealIndex(pcidevs, 0); + virHostdevReAttachPciDevice(mgr, dev); + } + + virObjectUnref(pcidevs); +cleanup: + VIR_FREE(oldStateDir); + virObjectUnlock(mgr->activePciHostdevs); + virObjectUnlock(mgr->inactivePciHostdevs); +} + +void +virHostdevReAttachUsbHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs) +{ + size_t i; + + if (!nhostdevs || !mgr) + return; + + virObjectLock(mgr->activeUsbHostdevs); + for (i = 0; i < nhostdevs; i++) { + virDomainHostdevDefPtr hostdev = hostdevs[i]; + virUSBDevicePtr usb, tmp; + const char *usedby_drvname; + const char *usedby_domname; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) + continue; + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) + continue; + if (hostdev->missing) + continue; + + usb = virUSBDeviceNew(hostdev->source.subsys.u.usb.bus, + hostdev->source.subsys.u.usb.device, + NULL); + + if (!usb) { + VIR_WARN("Unable to reattach USB device %03d.%03d " + "on driver %s domain %s", + hostdev->source.subsys.u.usb.bus, + hostdev->source.subsys.u.usb.device, + drv_name, dom_name); + continue; + } + + /* Delete only those USB devices which belongs + * to domain. Therefore we want to steal only + * those devices from the list which were taken + * by domain */ + + tmp = virUSBDeviceListFind(mgr->activeUsbHostdevs, usb); + virUSBDeviceFree(usb); + + if (!tmp) { + VIR_WARN("Unable to find device %03d.%03d " + "in list of active USB devices", + hostdev->source.subsys.u.usb.bus, + hostdev->source.subsys.u.usb.device); + continue; + } + + virUSBDeviceGetUsedBy(tmp, &usedby_drvname, &usedby_domname); + if (STREQ_NULLABLE(drv_name, usedby_drvname) && + STREQ_NULLABLE(dom_name, usedby_domname)) { + VIR_DEBUG("Removing %03d.%03d dom=%s:%s", + hostdev->source.subsys.u.usb.bus, + hostdev->source.subsys.u.usb.device, + drv_name, dom_name); + virUSBDeviceListDel(mgr->activeUsbHostdevs, tmp); + } + + } + + virObjectUnlock(mgr->activeUsbHostdevs); +} + +void +virHostdevReAttachScsiHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs) +{ + size_t i; + + if (!nhostdevs || !mgr) + return; + + virObjectLock(mgr->activeScsiHostdevs); + for (i = 0; i < nhostdevs; i++) { + virDomainHostdevDefPtr hostdev = hostdevs[i]; + virSCSIDevicePtr scsi; + virSCSIDevicePtr tmp; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS || + hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI) + continue; + + if (!(scsi = virSCSIDeviceNew(NULL, + hostdev->source.subsys.u.scsi.adapter, + hostdev->source.subsys.u.scsi.bus, + hostdev->source.subsys.u.scsi.target, + hostdev->source.subsys.u.scsi.unit, + hostdev->readonly, + hostdev->shareable))) { + VIR_WARN("Unable to reattach SCSI device %s:%d:%d:%d on driver %s, " + "domain %s", + hostdev->source.subsys.u.scsi.adapter, + hostdev->source.subsys.u.scsi.bus, + hostdev->source.subsys.u.scsi.target, + hostdev->source.subsys.u.scsi.unit, + drv_name, dom_name); + continue; + } + + /* Only delete the devices which are marked as being used by @name, + * because qemuProcessStart could fail on the half way. */ + + if (!(tmp = virSCSIDeviceListFind(mgr->activeScsiHostdevs, scsi))) { + VIR_WARN("Unable to find device %s:%d:%d:%d " + "in list of active SCSI devices", + hostdev->source.subsys.u.scsi.adapter, + hostdev->source.subsys.u.scsi.bus, + hostdev->source.subsys.u.scsi.target, + hostdev->source.subsys.u.scsi.unit); + virSCSIDeviceFree(scsi); + continue; + } + + VIR_DEBUG("Removing %s:%d:%d:%d drv= %s dom=%s from activeScsiHostdevs", + hostdev->source.subsys.u.scsi.adapter, + hostdev->source.subsys.u.scsi.bus, + hostdev->source.subsys.u.scsi.target, + hostdev->source.subsys.u.scsi.unit, + drv_name, dom_name); + + virSCSIDeviceListDel(mgr->activeScsiHostdevs, tmp, drv_name, dom_name); + virSCSIDeviceFree(scsi); + } + + virObjectUnlock(mgr->activeScsiHostdevs); +} + +void +virHostdevReAttachDomainHostdevs(virHostdevManagerPtr mgr, + const char *driver, + virDomainDefPtr def, + unsigned int flags) +{ + if (!def->nhostdevs || !mgr) + return; + + if (flags & VIR_SP_PCI_HOSTDEV) { + virHostdevReAttachPciHostdevs(mgr, driver, def->name, + def->hostdevs, def->nhostdevs); + } + + if (flags & VIR_SP_USB_HOSTDEV) { + virHostdevReAttachUsbHostdevs(mgr, driver, def->name, + def->hostdevs, def->nhostdevs); + } + + if (flags & VIR_SP_SCSI_HOSTDEV) { + virHostdevReAttachScsiHostdevs(mgr, driver, def->name, + def->hostdevs, def->nhostdevs); + } +} + +/* following functions are used by NodeDevDetach/Reattach/Reset */ +int +virHostdevPciNodeDeviceDetach(virHostdevManagerPtr mgr, + virPCIDevicePtr pci) +{ + int ret; + + if (!mgr || !pci) + return -1; + + virObjectLock(mgr->activePciHostdevs); + virObjectLock(mgr->inactivePciHostdevs); + if (virPCIDeviceDetach(pci, mgr->activePciHostdevs, + mgr->inactivePciHostdevs) < 0) + ret = -1; + else + ret = 0; + + virObjectUnlock(mgr->inactivePciHostdevs); + virObjectUnlock(mgr->activePciHostdevs); + return ret; +} + +int +virHostdevPciNodeDeviceReAttach(virHostdevManagerPtr mgr, + virPCIDevicePtr pci) +{ + int ret = -1; + virPCIDevicePtr other; + + if (!mgr || !pci) + return -1; + + virObjectLock(mgr->activePciHostdevs); + virObjectLock(mgr->inactivePciHostdevs); + other = virPCIDeviceListFind(mgr->activePciHostdevs, pci); + if (other) { + const char *other_drvname; + const char *other_domname; + + virPCIDeviceGetUsedBy(other, &other_drvname, &other_domname); + if (other_drvname && other_domname) + virReportError(VIR_ERR_OPERATION_INVALID, + _("PCI device %s is still in use by driver %s, domain %s"), + virPCIDeviceGetName(pci), other_drvname, other_domname); + else + virReportError(VIR_ERR_OPERATION_INVALID, + _("PCI device %s is still in use"), + virPCIDeviceGetName(pci)); + goto out; + } + + virPCIDeviceReattachInit(pci); + + if (virPCIDeviceReattach(pci, mgr->activePciHostdevs, + mgr->inactivePciHostdevs) < 0) + goto out; + + ret = 0; +out: + virObjectUnlock(mgr->inactivePciHostdevs); + virObjectUnlock(mgr->activePciHostdevs); + return ret; +} + +int +virHostdevPciNodeDeviceReset(virHostdevManagerPtr mgr, + virPCIDevicePtr pci) +{ + int ret; + + if (!mgr || !pci) + return -1; + + virObjectLock(mgr->activePciHostdevs); + virObjectLock(mgr->inactivePciHostdevs); + if (virPCIDeviceReset(pci, mgr->activePciHostdevs, + mgr->inactivePciHostdevs) < 0) + ret = -1; + else + ret = 0; + + virObjectUnlock(mgr->inactivePciHostdevs); + virObjectUnlock(mgr->activePciHostdevs); + return ret; +} diff --git a/src/util/virhostdev.h b/src/util/virhostdev.h new file mode 100644 index 0000000..e4b5b86 --- /dev/null +++ b/src/util/virhostdev.h @@ -0,0 +1,134 @@ +/* virhostdev.h: hostdev management + * + * Copyright (C) 2014 SUSE LINUX Products GmbH, Nuernberg, Germany. + * Copyright (C) 2006-2007, 2009-2014 Red Hat, Inc. + * Copyright (C) 2006 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * Author: Chunyan Liu <cyliu@suse.com> + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#ifndef __VIR_HOSTDEV_H__ +# define __VIR_HOSTDEV_H__ + +# include "internal.h" + +# include "domain_conf.h" +# include "virpci.h" +# include "virusb.h" +# include "virscsi.h" + +typedef enum { + VIR_SP_PCI_HOSTDEV = (1 << 0), /* support pci passthrough */ + VIR_SP_USB_HOSTDEV = (1 << 1), /* support usb passthrough */ + VIR_SP_SCSI_HOSTDEV = (1 << 2), /* support scsi passthrough */ + + VIR_COLD_BOOT = (1 << 8), /* cold boot */ + VIR_STRICT_ACS_CHECK = (1 << 9), /* strict acs check */ +} virHostdevManagerFlag; + +typedef struct _virHostdevManager virHostdevManager; +typedef virHostdevManager *virHostdevManagerPtr; +struct _virHostdevManager{ + char *stateDir; + + virPCIDeviceListPtr activePciHostdevs; + virPCIDeviceListPtr inactivePciHostdevs; + virUSBDeviceListPtr activeUsbHostdevs; + virSCSIDeviceListPtr activeScsiHostdevs; +}; + +virHostdevManagerPtr virHostdevManagerGetDefault(void); + +bool virHostdevHostSupportsPassthroughVFIO(void); +bool virHostdevHostSupportsPassthroughKVM(void); + +/* functions used to prepare/unprepare hostdevs for domain */ +int virHostdevPrepareDomainHostdevs(virHostdevManagerPtr mgr, + const char *driver, + virDomainDefPtr def, + unsigned int flags); +void virHostdevReAttachDomainHostdevs(virHostdevManagerPtr mgr, + const char *driver, + virDomainDefPtr def, + unsigned int flags); +int virHostdevPreparePciHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name, + const unsigned char *uuid, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs, + unsigned int flags); +int virHostdevPrepareUsbHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs, + unsigned int flags); +int virHostdevPrepareScsiHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs); +void virHostdevReAttachPciHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs); +void virHostdevReAttachUsbHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs); +void virHostdevReAttachScsiHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs); +int +virHostdevUpdateActivePciHostdevs(virHostdevManagerPtr mgr, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs, + const char *drv_name, + const char *dom_name); +int +virHostdevUpdateActiveUsbHostdevs(virHostdevManagerPtr mgr, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs, + const char *drv_name, + const char *dom_name); +int +virHostdevUpdateActiveScsiHostdevs(virHostdevManagerPtr mgr, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs, + const char *drv_name, + const char *dom_name); +int +virHostdevUpdateActiveHostdevs(virHostdevManagerPtr mgr, + const char *driver, + virDomainDefPtr def, + unsigned int flags); + +/* functions used by NodeDevDetach/Reattach/Reset */ +int virHostdevPciNodeDeviceDetach(virHostdevManagerPtr mgr, + virPCIDevicePtr pci); +int virHostdevPciNodeDeviceReAttach(virHostdevManagerPtr mgr, + virPCIDevicePtr pci); +int virHostdevPciNodeDeviceReset(virHostdevManagerPtr mgr, + virPCIDevicePtr pci); + +#endif /* __VIR_HOSTDEV_H__ */ -- 1.6.0.2

On Mon, 2014-02-17 at 14:32 +0800, Chunyan Liu wrote:
Extract code from qemu_hostdev.c and make it reusable for multiple drivers, meanwhile maintain a global hostdev state to solve conflict between different drivers.
It would be much easier to review if you added and removed the code piece by piece from qemu_hostdev.c to virhostdev.c. -- Cedric
Signed-off-by: Chunyan Liu <cyliu@suse.com> --- po/POTFILES.in | 1 + src/Makefile.am | 1 + src/libvirt_private.syms | 21 + src/util/virhostdev.c | 1775 ++++++++++++++++++++++++++++++++++++++++++++++ src/util/virhostdev.h | 134 ++++ 5 files changed, 1932 insertions(+), 0 deletions(-) create mode 100644 src/util/virhostdev.c create mode 100644 src/util/virhostdev.h
diff --git a/po/POTFILES.in b/po/POTFILES.in index 0359b2f..60c226a 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -160,6 +160,7 @@ src/util/vireventpoll.c src/util/virfile.c src/util/virhash.c src/util/virhook.c +src/util/virhostdev.c src/util/viridentity.c src/util/virinitctl.c src/util/viriptables.c diff --git a/src/Makefile.am b/src/Makefile.am index 3f8d22f..04ea01b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -104,6 +104,7 @@ UTIL_SOURCES = \ util/virhash.c util/virhash.h \ util/virhashcode.c util/virhashcode.h \ util/virhook.c util/virhook.h \ + util/virhostdev.c util/virhostdev.h \ util/viridentity.c util/viridentity.h \ util/virinitctl.c util/virinitctl.h \ util/viriptables.c util/viriptables.h \ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 2c9536a..b283f8e 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1276,6 +1276,27 @@ virHookInitialize; virHookPresent;
+#util/virhostdev.h +virHostdevHostSupportsPassthroughKVM; +virHostdevHostSupportsPassthroughVFIO; +virHostdevManagerGetDefault; +virHostdevPciNodeDeviceDetach; +virHostdevPciNodeDeviceReAttach; +virHostdevPciNodeDeviceReset; +virHostdevPrepareDomainHostdevs; +virHostdevPreparePciHostdevs; +virHostdevPrepareScsiHostdevs; +virHostdevPrepareUsbHostdevs; +virHostdevReAttachDomainHostdevs; +virHostdevReAttachPciHostdevs; +virHostdevReAttachScsiHostdevs; +virHostdevReAttachUsbHostdevs; +virHostdevUpdateActiveHostdevs; +virHostdevUpdateActivePciHostdevs; +virHostdevUpdateActiveScsiHostdevs; +virHostdevUpdateActiveUsbHostdevs; + + # util/viridentity.h virIdentityGetAttr; virIdentityGetCurrent; diff --git a/src/util/virhostdev.c b/src/util/virhostdev.c new file mode 100644 index 0000000..8d75a77 --- /dev/null +++ b/src/util/virhostdev.c @@ -0,0 +1,1775 @@ +/* virhostdev.c: hostdev management + * + * Copyright (C) 2014 SUSE LINUX Products GmbH, Nuernberg, Germany. + * Copyright (C) 2006-2007, 2009-2014 Red Hat, Inc. + * Copyright (C) 2006 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * Author: Chunyan Liu <cyliu@suse.com> + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#include <config.h> + +#include <dirent.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> + +#include "virhostdev.h" +#include "viralloc.h" +#include "virstring.h" +#include "virfile.h" +#include "virerror.h" +#include "virlog.h" +#include "virnetdev.h" +#include "virutil.h" +#include "configmake.h" + +#define VIR_FROM_THIS VIR_FROM_NONE + +#define HOSTDEV_STATE_DIR LOCALSTATEDIR "/run/libvirt/hostdevmgr" + +/* for upgrade, may need to find netconfig file in old qemu state dir */ +#define QEMU_STATE_DIR LOCALSTATEDIR "/run/libvirt/qemu" + +static virHostdevManagerPtr hostdevMgr; + +static void +virHostdevManagerCleanup(void) +{ + if (!hostdevMgr) + return; + + virObjectUnref(hostdevMgr->activePciHostdevs); + virObjectUnref(hostdevMgr->inactivePciHostdevs); + virObjectUnref(hostdevMgr->activeUsbHostdevs); + VIR_FREE(hostdevMgr->stateDir); + + VIR_FREE(hostdevMgr); +} + +static int +virHostdevOnceInit(void) +{ + if (VIR_ALLOC(hostdevMgr) < 0) + goto error; + + if ((hostdevMgr->activePciHostdevs = virPCIDeviceListNew()) == NULL) + goto error; + + if ((hostdevMgr->activeUsbHostdevs = virUSBDeviceListNew()) == NULL) + goto error; + + if ((hostdevMgr->inactivePciHostdevs = virPCIDeviceListNew()) == NULL) + goto error; + + if ((hostdevMgr->activeScsiHostdevs = virSCSIDeviceListNew()) == NULL) + goto error; + + if (VIR_STRDUP(hostdevMgr->stateDir, HOSTDEV_STATE_DIR) < 0) + goto error; + + if (virFileMakePath(hostdevMgr->stateDir) < 0) { + virReportError(VIR_ERR_OPERATION_FAILED, + _("Failed to create state dir '%s'"), + hostdevMgr->stateDir); + goto error; + } + + return 0; + +error: + virHostdevManagerCleanup(); + return -1; +} + +VIR_ONCE_GLOBAL_INIT(virHostdev) + +virHostdevManagerPtr +virHostdevManagerGetDefault(void) +{ + if (virHostdevInitialize() < 0) + return NULL; + return hostdevMgr; +} + +static int +virHostdevPciSysfsPath(virDomainHostdevDefPtr hostdev, char **sysfs_path) +{ + virPCIDeviceAddress config_address; + + config_address.domain = hostdev->source.subsys.u.pci.addr.domain; + config_address.bus = hostdev->source.subsys.u.pci.addr.bus; + config_address.slot = hostdev->source.subsys.u.pci.addr.slot; + config_address.function = hostdev->source.subsys.u.pci.addr.function; + + return virPCIDeviceAddressGetSysfsFile(&config_address, sysfs_path); +} + +static int +virHostdevIsVirtualFunction(virDomainHostdevDefPtr hostdev) +{ + char *sysfs_path = NULL; + int ret = -1; + + if (virHostdevPciSysfsPath(hostdev, &sysfs_path) < 0) + return ret; + + ret = virPCIIsVirtualFunction(sysfs_path); + + VIR_FREE(sysfs_path); + + return ret; +} + +static int +virHostdevNetDevice(virDomainHostdevDefPtr hostdev, + char **linkdev, + int *vf) +{ + int ret = -1; + char *sysfs_path = NULL; + + if (virHostdevPciSysfsPath(hostdev, &sysfs_path) < 0) + return ret; + + if (virPCIIsVirtualFunction(sysfs_path) == 1) { + if (virPCIGetVirtualFunctionInfo(sysfs_path, linkdev, vf) < 0) + goto cleanup; + } else { + if (virPCIGetNetName(sysfs_path, linkdev) < 0) + goto cleanup; + *vf = -1; + } + + ret = 0; + +cleanup: + VIR_FREE(sysfs_path); + + return ret; +} + +static int +virHostdevNetConfigVirtPortProfile(const char *linkdev, int vf, + virNetDevVPortProfilePtr virtPort, + const virMacAddr *macaddr, + const unsigned char *uuid, + int associate) +{ + int ret = -1; + + if (!virtPort) + return ret; + + switch (virtPort->virtPortType) { + case VIR_NETDEV_VPORT_PROFILE_NONE: + case VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH: + case VIR_NETDEV_VPORT_PROFILE_8021QBG: + case VIR_NETDEV_VPORT_PROFILE_LAST: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("virtualport type %s is " + "currently not supported on interfaces of type " + "hostdev"), + virNetDevVPortTypeToString(virtPort->virtPortType)); + break; + + case VIR_NETDEV_VPORT_PROFILE_8021QBH: + if (associate) + ret = virNetDevVPortProfileAssociate(NULL, virtPort, macaddr, + linkdev, vf, uuid, + VIR_NETDEV_VPORT_PROFILE_OP_CREATE, false); + else + ret = virNetDevVPortProfileDisassociate(NULL, virtPort, + macaddr, linkdev, vf, + VIR_NETDEV_VPORT_PROFILE_OP_DESTROY); + break; + } + + return ret; +} + +static int +virHostdevNetConfigReplace(virDomainHostdevDefPtr hostdev, + const unsigned char *uuid, + char *stateDir) +{ + char *linkdev = NULL; + virNetDevVlanPtr vlan; + virNetDevVPortProfilePtr virtPort; + int ret = -1; + int vf = -1; + int vlanid = -1; + int port_profile_associate = 1; + int isvf; + + isvf = virHostdevIsVirtualFunction(hostdev); + if (isvf <= 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Interface type hostdev is currently supported on " + "SR-IOV Virtual Functions only")); + return ret; + } + + if (virHostdevNetDevice(hostdev, &linkdev, &vf) < 0) + return ret; + + vlan = virDomainNetGetActualVlan(hostdev->parent.data.net); + virtPort = virDomainNetGetActualVirtPortProfile(hostdev->parent.data.net); + if (virtPort) { + if (vlan) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("direct setting of the vlan tag is not allowed " + "for hostdev devices using %s mode"), + virNetDevVPortTypeToString(virtPort->virtPortType)); + goto cleanup; + } + ret = virHostdevNetConfigVirtPortProfile(linkdev, vf, + virtPort, + &hostdev->parent.data.net->mac, + uuid, + port_profile_associate); + } else { + /* Set only mac and vlan */ + if (vlan) { + if (vlan->nTags != 1 || vlan->trunk) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("vlan trunking is not supported " + "by SR-IOV network devices")); + goto cleanup; + } + if (vf == -1) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("vlan can only be set for SR-IOV VFs, but " + "%s is not a VF"), linkdev); + goto cleanup; + } + vlanid = vlan->tag[0]; + } else if (vf >= 0) { + vlanid = 0; /* assure any current vlan tag is reset */ + } + + ret = virNetDevReplaceNetConfig(linkdev, vf, + &hostdev->parent.data.net->mac, + vlanid, stateDir); + } +cleanup: + VIR_FREE(linkdev); + return ret; +} + +/* oldStateDir: + * For upgrade, if there is an existing VM on QEMU, the hostdev netconfig file + * is previously stored in /var/run/libvirt/qemu. With new version, it tries to + * find in hostdevManager->stateDir location but certainly it cannot find it. + * In this case, we should find in the old state dir. + */ +static int +virHostdevNetConfigRestore(virDomainHostdevDefPtr hostdev, + char *stateDir, + char *oldStateDir) +{ + char *linkdev = NULL; + virNetDevVPortProfilePtr virtPort; + int ret = -1; + int vf = -1; + int port_profile_associate = 0; + int isvf; + + /* This is only needed for PCI devices that have been defined + * using <interface type='hostdev'>. For all others, it is a NOP. + */ + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS || + hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI || + hostdev->parent.type != VIR_DOMAIN_DEVICE_NET || + !hostdev->parent.data.net) + return 0; + + isvf = virHostdevIsVirtualFunction(hostdev); + if (isvf <= 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Interface type hostdev is currently supported on " + "SR-IOV Virtual Functions only")); + return ret; + } + + if (virHostdevNetDevice(hostdev, &linkdev, &vf) < 0) + return ret; + + virtPort = virDomainNetGetActualVirtPortProfile(hostdev->parent.data.net); + if (virtPort) + ret = virHostdevNetConfigVirtPortProfile(linkdev, vf, virtPort, + &hostdev->parent.data.net->mac, + NULL, port_profile_associate); + else { + ret = virNetDevRestoreNetConfig(linkdev, vf, stateDir); + if (ret < 0 && oldStateDir != NULL) + ret = virNetDevRestoreNetConfig(linkdev, vf, oldStateDir); + } + + VIR_FREE(linkdev); + + return ret; +} + +static virPCIDeviceListPtr +virHostdevGetPciHostDeviceList(virDomainHostdevDefPtr *hostdevs, int nhostdevs) +{ + virPCIDeviceListPtr list; + size_t i; + + if (!(list = virPCIDeviceListNew())) + return NULL; + + for (i = 0; i < nhostdevs; i++) { + virDomainHostdevDefPtr hostdev = hostdevs[i]; + virPCIDevicePtr dev; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) + continue; + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) + continue; + + dev = virPCIDeviceNew(hostdev->source.subsys.u.pci.addr.domain, + hostdev->source.subsys.u.pci.addr.bus, + hostdev->source.subsys.u.pci.addr.slot, + hostdev->source.subsys.u.pci.addr.function); + if (!dev) { + virObjectUnref(list); + return NULL; + } + + if (virPCIDeviceListAdd(list, dev) < 0) { + virPCIDeviceFree(dev); + virObjectUnref(list); + return NULL; + } + + virPCIDeviceSetManaged(dev, hostdev->managed); + + switch (hostdev->source.subsys.u.pci.backend) { + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO: + if (virPCIDeviceSetStubDriver(dev, "vfio-pci") < 0) { + virObjectUnref(list); + return NULL; + } + break; + + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM : + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT: + if (virPCIDeviceSetStubDriver(dev, "pci-stub") < 0) { + virObjectUnref(list); + return NULL; + } + break; + } + } + + return list; +} + +bool +virHostdevHostSupportsPassthroughVFIO(void) +{ + DIR *iommuDir = NULL; + struct dirent *iommuGroup = NULL; + bool ret = false; + + /* condition 1 - /sys/kernel/iommu_groups/ contains entries */ + if (!(iommuDir = opendir("/sys/kernel/iommu_groups/"))) + goto cleanup; + + while ((iommuGroup = readdir(iommuDir))) { + /* skip ./ ../ */ + if (STRPREFIX(iommuGroup->d_name, ".")) + continue; + + /* assume we found a group */ + break; + } + + if (!iommuGroup) + goto cleanup; + /* okay, iommu is on and recognizes groups */ + + /* condition 2 - /dev/vfio/vfio exists */ + if (!virFileExists("/dev/vfio/vfio")) + goto cleanup; + + ret = true; + +cleanup: + if (iommuDir) + closedir(iommuDir); + + return ret; +} + + +#if HAVE_LINUX_KVM_H +# include <linux/kvm.h> +bool +virHostdevHostSupportsPassthroughKVM(void) +{ + int kvmfd = -1; + bool ret = false; + + if ((kvmfd = open("/dev/kvm", O_RDONLY)) < 0) + goto cleanup; + +# ifdef KVM_CAP_IOMMU + if ((ioctl(kvmfd, KVM_CHECK_EXTENSION, KVM_CAP_IOMMU)) <= 0) + goto cleanup; + + ret = true; +# endif + +cleanup: + VIR_FORCE_CLOSE(kvmfd); + + return ret; +} +#else +bool +virHostdevHostSupportsPassthroughKVM(void) +{ + return false; +} +#endif + +static bool +virHostdevPCICheckSupport(virDomainHostdevDefPtr *hostdevs, + size_t nhostdevs) +{ + bool supportsPassthroughVFIO = virHostdevHostSupportsPassthroughVFIO(); + bool supportsPassthroughKVM = virHostdevHostSupportsPassthroughKVM(); + size_t i; + + /* assign defaults for hostdev passthrough */ + for (i = 0; i < nhostdevs; i++) { + virDomainHostdevDefPtr hostdev = hostdevs[i]; + int *backend = &hostdev->source.subsys.u.pci.backend; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) + continue; + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) + continue; + + switch ((virDomainHostdevSubsysPciBackendType) *backend) { + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO: + if (!supportsPassthroughVFIO) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("host doesn't support VFIO PCI passthrough")); + return false; + } + break; + + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM: + if (!supportsPassthroughKVM) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("host doesn't support legacy PCI passthrough")); + return false; + } + + break; + + default: + break; + } + } + + return true; +} +int +virHostdevPreparePciHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name, + const unsigned char *uuid, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs, + unsigned int flags) +{ + virPCIDeviceListPtr pcidevs = NULL; + int last_processed_hostdev_vf = -1; + size_t i; + int ret = -1; + + if (!nhostdevs) + return 0; + if (mgr == NULL) + return -1; + + virObjectLock(mgr->activePciHostdevs); + virObjectLock(mgr->inactivePciHostdevs); + + if (!virHostdevPCICheckSupport(hostdevs, nhostdevs)) + goto cleanup; + + if (!(pcidevs = virHostdevGetPciHostDeviceList(hostdevs, nhostdevs))) + goto cleanup; + + /* We have to use 9 loops here. *All* devices must + * be detached before we reset any of them, because + * in some cases you have to reset the whole PCI, + * which impacts all devices on it. Also, all devices + * must be reset before being marked as active. + */ + + /* Loop 1: validate that non-managed device isn't in use, eg + * by checking that device is either un-bound, or bound + * to stub driver + */ + + for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) { + virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i); + virPCIDevicePtr other; + bool strict_acs_check = !!(flags & VIR_STRICT_ACS_CHECK); + + if (!virPCIDeviceIsAssignable(dev, strict_acs_check)) { + virReportError(VIR_ERR_OPERATION_INVALID, + _("PCI device %s is not assignable"), + virPCIDeviceGetName(dev)); + goto cleanup; + } + /* The device is in use by other active domain if + * the dev is in list activePciHostdevs. + */ + if ((other = virPCIDeviceListFind(mgr->activePciHostdevs, dev))) { + const char *other_drvname; + const char *other_domname; + + virPCIDeviceGetUsedBy(other, &other_drvname, &other_domname); + if (other_drvname && other_domname) + virReportError(VIR_ERR_OPERATION_INVALID, + _("PCI device %s is in use by driver %s,domain %s"), + virPCIDeviceGetName(dev), other_drvname, + other_domname); + else + virReportError(VIR_ERR_OPERATION_INVALID, + _("PCI device %s is already in use"), + virPCIDeviceGetName(dev)); + goto cleanup; + } + } + + /* Loop 2: detach managed devices */ + for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) { + virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i); + if (virPCIDeviceGetManaged(dev) && + virPCIDeviceDetach(dev, mgr->activePciHostdevs, NULL) < 0) + goto reattachdevs; + } + + /* Loop 3: Now that all the PCI hostdevs have been detached, we + * can safely reset them */ + for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) { + virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i); + if (virPCIDeviceReset(dev, mgr->activePciHostdevs, + mgr->inactivePciHostdevs) < 0) + goto reattachdevs; + } + + /* Loop 4: For SRIOV network devices, Now that we have detached the + * the network device, set the netdev config */ + for (i = 0; i < nhostdevs; i++) { + virDomainHostdevDefPtr hostdev = hostdevs[i]; + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) + continue; + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) + continue; + if (hostdev->parent.type == VIR_DOMAIN_DEVICE_NET && + hostdev->parent.data.net) { + if (virHostdevNetConfigReplace(hostdev, uuid, mgr->stateDir) < 0) + goto resetvfnetconfig; + } + last_processed_hostdev_vf = i; + } + + /* Loop 5: Now mark all the devices as active */ + for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) { + virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i); + if (virPCIDeviceListAdd(mgr->activePciHostdevs, dev) < 0) + goto inactivedevs; + } + + /* Loop 6: Now remove the devices from inactive list. */ + for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) { + virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i); + virPCIDeviceListDel(mgr->inactivePciHostdevs, dev); + } + + /* Loop 7: Now set the used_by_domain of the device in + * driver->activePciHostdevs as domain name. + */ + for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) { + virPCIDevicePtr dev, activeDev; + + dev = virPCIDeviceListGet(pcidevs, i); + activeDev = virPCIDeviceListFind(mgr->activePciHostdevs, dev); + + if (activeDev) + virPCIDeviceSetUsedBy(activeDev, drv_name, dom_name); + } + + /* Loop 8: Now set the original states for hostdev def */ + for (i = 0; i < nhostdevs; i++) { + virPCIDevicePtr dev; + virPCIDevicePtr pcidev; + virDomainHostdevDefPtr hostdev = hostdevs[i]; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) + continue; + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) + continue; + + dev = virPCIDeviceNew(hostdev->source.subsys.u.pci.addr.domain, + hostdev->source.subsys.u.pci.addr.bus, + hostdev->source.subsys.u.pci.addr.slot, + hostdev->source.subsys.u.pci.addr.function); + + /* original states "unbind_from_stub", "remove_slot", + * "reprobe" were already set by pciDettachDevice in + * loop 2. + */ + if ((pcidev = virPCIDeviceListFind(pcidevs, dev))) { + hostdev->origstates.states.pci.unbind_from_stub = + virPCIDeviceGetUnbindFromStub(pcidev); + hostdev->origstates.states.pci.remove_slot = + virPCIDeviceGetRemoveSlot(pcidev); + hostdev->origstates.states.pci.reprobe = + virPCIDeviceGetReprobe(pcidev); + } + + virPCIDeviceFree(dev); + } + + /* Loop 9: Now steal all the devices from pcidevs */ + while (virPCIDeviceListCount(pcidevs) > 0) + virPCIDeviceListStealIndex(pcidevs, 0); + + ret = 0; + goto cleanup; + +inactivedevs: + /* Only steal all the devices from driver->activePciHostdevs. We will + * free them in virObjectUnref(). + */ + while (virPCIDeviceListCount(pcidevs) > 0) { + virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, 0); + virPCIDeviceListSteal(mgr->activePciHostdevs, dev); + } + +resetvfnetconfig: + for (i = 0; + last_processed_hostdev_vf != -1 && i < last_processed_hostdev_vf; i++) + virHostdevNetConfigRestore(hostdevs[i], mgr->stateDir, NULL); + +reattachdevs: + for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) { + virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i); + virPCIDeviceReattach(dev, mgr->activePciHostdevs, NULL); + } + +cleanup: + virObjectUnlock(mgr->activePciHostdevs); + virObjectUnlock(mgr->inactivePciHostdevs); + virObjectUnref(pcidevs); + return ret; +} + +static int +virHostdevFindUSBDevice(virDomainHostdevDefPtr hostdev, + bool mandatory, + virUSBDevicePtr *usb) +{ + unsigned vendor = hostdev->source.subsys.u.usb.vendor; + unsigned product = hostdev->source.subsys.u.usb.product; + unsigned bus = hostdev->source.subsys.u.usb.bus; + unsigned device = hostdev->source.subsys.u.usb.device; + bool autoAddress = hostdev->source.subsys.u.usb.autoAddress; + int rc; + + *usb = NULL; + + if (vendor && bus) { + rc = virUSBDeviceFind(vendor, product, bus, device, + NULL, + autoAddress ? false : mandatory, + usb); + if (rc < 0) { + return -1; + } else if (!autoAddress) { + goto out; + } else { + VIR_INFO("USB device %x:%x could not be found at previous" + " address (bus:%u device:%u)", + vendor, product, bus, device); + } + } + + /* When vendor is specified, its USB address is either unspecified or the + * device could not be found at the USB device where it had been + * automatically found before. + */ + if (vendor) { + virUSBDeviceListPtr devs; + + rc = virUSBDeviceFindByVendor(vendor, product, NULL, mandatory, &devs); + if (rc < 0) + return -1; + + if (rc == 1) { + *usb = virUSBDeviceListGet(devs, 0); + virUSBDeviceListSteal(devs, *usb); + } + virObjectUnref(devs); + + if (rc == 0) { + goto out; + } else if (rc > 1) { + if (autoAddress) { + virReportError(VIR_ERR_OPERATION_FAILED, + _("Multiple USB devices for %x:%x were found, " + "but none of them is at bus:%u device:%u"), + vendor, product, bus, device); + } else { + virReportError(VIR_ERR_OPERATION_FAILED, + _("Multiple USB devices for %x:%x, " + "use <address> to specify one"), + vendor, product); + } + return -1; + } + + hostdev->source.subsys.u.usb.bus = virUSBDeviceGetBus(*usb); + hostdev->source.subsys.u.usb.device = virUSBDeviceGetDevno(*usb); + hostdev->source.subsys.u.usb.autoAddress = true; + + if (autoAddress) { + VIR_INFO("USB device %x:%x found at bus:%u device:%u (moved" + " from bus:%u device:%u)", + vendor, product, + hostdev->source.subsys.u.usb.bus, + hostdev->source.subsys.u.usb.device, + bus, device); + } + } else if (!vendor && bus) { + if (virUSBDeviceFindByBus(bus, device, NULL, mandatory, usb) < 0) + return -1; + } + +out: + if (!*usb) + hostdev->missing = true; + return 0; +} + +static int +virHostdevMarkUsbHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name, + virUSBDeviceListPtr list) +{ + size_t i, j; + unsigned int count; + virUSBDevicePtr tmp; + + virObjectLock(mgr->activeUsbHostdevs); + count = virUSBDeviceListCount(list); + + for (i = 0; i < count; i++) { + virUSBDevicePtr usb = virUSBDeviceListGet(list, i); + if ((tmp = virUSBDeviceListFind(mgr->activeUsbHostdevs, usb))) { + const char *other_drvname; + const char *other_domname; + + virUSBDeviceGetUsedBy(tmp, &other_drvname, &other_domname); + if (other_drvname && other_domname) + virReportError(VIR_ERR_OPERATION_INVALID, + _("USB device %s is in use by " + "driver %s,domain %s"), + virUSBDeviceGetName(tmp), + other_drvname, other_domname); + else + virReportError(VIR_ERR_OPERATION_INVALID, + _("USB device %s is already in use"), + virUSBDeviceGetName(tmp)); + goto error; + } + + virUSBDeviceSetUsedBy(usb, drv_name, dom_name); + VIR_DEBUG("Adding %03d.%03d driver= %s dom=%s to activeUsbHostdevs", + virUSBDeviceGetBus(usb), virUSBDeviceGetDevno(usb), + drv_name, dom_name); + /* + * The caller is responsible to steal these usb devices + * from the virUSBDeviceList that passed in on success, + * perform rollback on failure. + */ + if (virUSBDeviceListAdd(mgr->activeUsbHostdevs, usb) < 0) + goto error; + } + + virObjectUnlock(mgr->activeUsbHostdevs); + return 0; + +error: + for (j = 0; j < i; j++) { + tmp = virUSBDeviceListGet(list, j); + virUSBDeviceListSteal(mgr->activeUsbHostdevs, tmp); + } + virObjectUnlock(mgr->activeUsbHostdevs); + return -1; +} + +int +virHostdevPrepareUsbHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs, + unsigned int flags) +{ + size_t i; + int ret = -1; + virUSBDeviceListPtr list; + virUSBDevicePtr tmp; + bool coldBoot = !!(flags & VIR_COLD_BOOT); + + if (!nhostdevs) + return 0; + if (mgr == NULL) + return -1; + + /* To prevent situation where USB device is assigned to two domains + * we need to keep a list of currently assigned USB devices. + * This is done in several loops which cannot be joined into one + * big loop. + */ + if (!(list = virUSBDeviceListNew())) + goto cleanup; + + /* Loop 1: build temporary list + */ + for (i = 0; i < nhostdevs; i++) { + virDomainHostdevDefPtr hostdev = hostdevs[i]; + bool required = true; + virUSBDevicePtr usb; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) + continue; + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) + continue; + + if (hostdev->startupPolicy == VIR_DOMAIN_STARTUP_POLICY_OPTIONAL || + (hostdev->startupPolicy == VIR_DOMAIN_STARTUP_POLICY_REQUISITE && + !coldBoot)) + required = false; + + if (virHostdevFindUSBDevice(hostdev, required, &usb) < 0) + goto cleanup; + + if (usb && virUSBDeviceListAdd(list, usb) < 0) { + virUSBDeviceFree(usb); + goto cleanup; + } + } + + /* Mark devices in temporary list as used by @name + * and add them do active list. However, if something goes + * wrong, perform rollback. + */ + if (virHostdevMarkUsbHostdevs(mgr, drv_name, dom_name, list) < 0) + goto cleanup; + + /* Loop 2: Temporary list was successfully merged with + * active list, so steal all items to avoid freeing them + * in cleanup label. + */ + while (virUSBDeviceListCount(list) > 0) { + tmp = virUSBDeviceListGet(list, 0); + virUSBDeviceListSteal(list, tmp); + } + + ret = 0; + +cleanup: + virObjectUnref(list); + return ret; +} + +static int +virHostdevSetUnprivSGIO(virDomainDeviceDefPtr dev) +{ + virDomainHostdevDefPtr hostdev = NULL; + char *sysfs_path = NULL; + char *path = NULL; + char *hostdev_name = NULL; + char *hostdev_path = NULL; + int val = -1; + int ret = 0; + + if (dev->type != VIR_DOMAIN_DEVICE_HOSTDEV) { + hostdev = dev->data.hostdev; + + if (!hostdev->shareable || + !(hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS && + hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI)) + return 0; + + if (!(hostdev_name = virSCSIDeviceGetDevName(NULL, + hostdev->source.subsys.u.scsi.adapter, + hostdev->source.subsys.u.scsi.bus, + hostdev->source.subsys.u.scsi.target, + hostdev->source.subsys.u.scsi.unit))) + goto cleanup; + + if (virAsprintf(&hostdev_path, "/dev/%s", hostdev_name) < 0) + goto cleanup; + + path = hostdev_path; + } else { + return 0; + } + + sysfs_path = virGetUnprivSGIOSysfsPath(path, NULL); + if (sysfs_path == NULL) { + ret = -1; + goto cleanup; + } + + /* By default, filter the SG_IO commands, i.e. set unpriv_sgio to 0. */ + val = (hostdev->source.subsys.u.scsi.sgio == + VIR_DOMAIN_DEVICE_SGIO_UNFILTERED); + + /* Do not do anything if unpriv_sgio is not supported by the kernel and the + * whitelist is enabled. But if requesting unfiltered access, always call + * virSetDeviceUnprivSGIO, to report an error for unsupported unpriv_sgio. + */ + if ((virFileExists(sysfs_path) || val == 1) && + virSetDeviceUnprivSGIO(path, NULL, val) < 0) + ret = -1; + +cleanup: + VIR_FREE(sysfs_path); + VIR_FREE(hostdev_name); + VIR_FREE(hostdev_path); + return ret; +} + +int +virHostdevPrepareScsiHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs) +{ + size_t i, j; + int count; + virSCSIDeviceListPtr list; + virSCSIDevicePtr tmp; + + if (!nhostdevs) + return 0; + if (mgr == NULL) + return -1; + + /* Loop 1 */ + for (i = 0; i < nhostdevs; i++) { + virDomainDeviceDef dev; + + dev.type = VIR_DOMAIN_DEVICE_HOSTDEV; + dev.data.hostdev = hostdevs[i]; + + if (virHostdevSetUnprivSGIO(&dev) < 0) + return -1; + } + + /* To prevent situation where SCSI device is assigned to two domains + * we need to keep a list of currently assigned SCSI devices. + */ + if (!(list = virSCSIDeviceListNew())) + goto cleanup; + + /* Loop 2: build temporary list */ + for (i = 0; i < nhostdevs; i++) { + virDomainHostdevDefPtr hostdev = hostdevs[i]; + virSCSIDevicePtr scsi; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS || + hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI) + continue; + + if (hostdev->managed) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("SCSI host device doesn't support managed mode")); + goto cleanup; + } + + if (!(scsi = virSCSIDeviceNew(NULL, + hostdev->source.subsys.u.scsi.adapter, + hostdev->source.subsys.u.scsi.bus, + hostdev->source.subsys.u.scsi.target, + hostdev->source.subsys.u.scsi.unit, + hostdev->readonly, + hostdev->shareable))) + goto cleanup; + + if (scsi && virSCSIDeviceListAdd(list, scsi) < 0) { + virSCSIDeviceFree(scsi); + goto cleanup; + } + } + + /* Loop 3: Mark devices in temporary list as used by @drv_name & @dom_name, + * and add them to driver list. However, if something goes wrong, perform + * rollback. + */ + virObjectLock(mgr->activeScsiHostdevs); + count = virSCSIDeviceListCount(list); + + for (i = 0; i < count; i++) { + virSCSIDevicePtr scsi = virSCSIDeviceListGet(list, i); + if ((tmp = virSCSIDeviceListFind(mgr->activeScsiHostdevs, scsi))) { + bool scsi_shareable = virSCSIDeviceGetShareable(scsi); + bool tmp_shareable = virSCSIDeviceGetShareable(tmp); + + if (!(scsi_shareable && tmp_shareable)) { + virReportError(VIR_ERR_OPERATION_INVALID, + _("SCSI device %s is already in use by " + "other domain(s) as '%s'"), + virSCSIDeviceGetName(tmp), + tmp_shareable ? "shareable" : "non-shareable"); + goto error; + } + + if (virSCSIDeviceSetUsedBy(tmp, drv_name, dom_name) < 0) + goto error; + } else { + if (virSCSIDeviceSetUsedBy(scsi, drv_name, dom_name) < 0) + goto error; + + VIR_DEBUG("Adding %s to activeScsiHostdevs", virSCSIDeviceGetName(scsi)); + + if (virSCSIDeviceListAdd(mgr->activeScsiHostdevs, scsi) < 0) + goto error; + + } + } + + virObjectUnlock(mgr->activeScsiHostdevs); + + /* Loop 3: Temporary list was successfully merged with + * driver list, so steal all items to avoid freeing them + * when freeing temporary list. + */ + while (virSCSIDeviceListCount(list) > 0) { + tmp = virSCSIDeviceListGet(list, 0); + virSCSIDeviceListSteal(list, tmp); + } + + virObjectUnref(list); + return 0; + +error: + for (j = 0; j < i; j++) { + tmp = virSCSIDeviceListGet(list, j); + virSCSIDeviceListSteal(mgr->activeScsiHostdevs, tmp); + } + virObjectUnlock(mgr->activeScsiHostdevs); +cleanup: + virObjectUnref(list); + return -1; +} + +int +virHostdevPrepareDomainHostdevs(virHostdevManagerPtr mgr, + const char *driver, + virDomainDefPtr def, + unsigned int flags) +{ + if (!def->nhostdevs) + return 0; + + if (mgr == NULL) + return -1; + + if (flags & VIR_SP_PCI_HOSTDEV) { + if (virHostdevPreparePciHostdevs(mgr, driver, + def->name, def->uuid, + def->hostdevs, + def->nhostdevs, + flags) < 0) + return -1; + } + + if (flags & VIR_SP_USB_HOSTDEV) { + if (virHostdevPrepareUsbHostdevs(mgr, driver, def->name, + def->hostdevs, def->nhostdevs, + flags) < 0) + return -1; + } + + if (flags & VIR_SP_SCSI_HOSTDEV) { + if (virHostdevPrepareScsiHostdevs(mgr, driver, def->name, + def->hostdevs, def->nhostdevs) < 0) + return -1; + } + + return 0; +} + +/* + * Pre-condition: mgr->inactivePciHostdevs & mgr->activePciHostdevs + * are locked + */ +static void +virHostdevReAttachPciDevice(virHostdevManagerPtr mgr, virPCIDevicePtr dev) +{ + /* If the device is not managed and was attached to guest + * successfully, it must have been inactive. + */ + if (!virPCIDeviceGetManaged(dev)) { + if (virPCIDeviceListAdd(mgr->inactivePciHostdevs, dev) < 0) + virPCIDeviceFree(dev); + return; + } + + /* Wait for device cleanup if it is qemu/kvm */ + if (STREQ(virPCIDeviceGetStubDriver(dev), "pci-stub")) { + int retries = 100; + while (virPCIDeviceWaitForCleanup(dev, "kvm_assigned_device") + && retries) { + usleep(100*1000); + retries--; + } + } + + if (virPCIDeviceReattach(dev, mgr->activePciHostdevs, + mgr->inactivePciHostdevs) < 0) { + virErrorPtr err = virGetLastError(); + VIR_ERROR(_("Failed to re-attach PCI device: %s"), + err ? err->message : _("unknown error")); + virResetError(err); + } + virPCIDeviceFree(dev); +} + +/* + * Pre-condition: mgr->activePciHostdevs is locked + */ +static virPCIDeviceListPtr +virHostdevGetActivePciHostDeviceList(virHostdevManagerPtr mgr, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs) +{ + virPCIDeviceListPtr list; + size_t i; + + if (!(list = virPCIDeviceListNew())) + return NULL; + + for (i = 0; i < nhostdevs; i++) { + virDomainHostdevDefPtr hostdev = hostdevs[i]; + virPCIDevicePtr dev; + virPCIDevicePtr activeDev; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) + continue; + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) + continue; + + dev = virPCIDeviceNew(hostdev->source.subsys.u.pci.addr.domain, + hostdev->source.subsys.u.pci.addr.bus, + hostdev->source.subsys.u.pci.addr.slot, + hostdev->source.subsys.u.pci.addr.function); + if (!dev) { + virObjectUnref(list); + return NULL; + } + + if ((activeDev = virPCIDeviceListFind(mgr->activePciHostdevs, dev))) { + if (virPCIDeviceListAdd(list, activeDev) < 0) { + virPCIDeviceFree(dev); + virObjectUnref(list); + return NULL; + } + } + + virPCIDeviceFree(dev); + } + + return list; +} + +int +virHostdevUpdateActivePciHostdevs(virHostdevManagerPtr mgr, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs, + const char *drv_name, + const char *dom_name) +{ + virDomainHostdevDefPtr hostdev = NULL; + virPCIDevicePtr dev = NULL; + size_t i; + int ret = -1; + + if (!nhostdevs) + return 0; + + if (!mgr) + return -1; + + virObjectLock(mgr->activePciHostdevs); + virObjectLock(mgr->inactivePciHostdevs); + + for (i = 0; i < nhostdevs; i++) { + hostdev = hostdevs[i]; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) + continue; + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) + continue; + + dev = virPCIDeviceNew(hostdev->source.subsys.u.pci.addr.domain, + hostdev->source.subsys.u.pci.addr.bus, + hostdev->source.subsys.u.pci.addr.slot, + hostdev->source.subsys.u.pci.addr.function); + + if (!dev) + goto cleanup; + + virPCIDeviceSetManaged(dev, hostdev->managed); + switch (hostdev->source.subsys.u.pci.backend) { + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO: + if (virPCIDeviceSetStubDriver(dev, "vfio-pci") < 0) + goto cleanup; + break; + + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM : + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT: + if (virPCIDeviceSetStubDriver(dev, "pci-stub") < 0) + goto cleanup; + break; + } + virPCIDeviceSetUsedBy(dev, drv_name, dom_name); + + /* Setup the original states for the PCI device */ + virPCIDeviceSetUnbindFromStub(dev, hostdev->origstates.states.pci.unbind_from_stub); + virPCIDeviceSetRemoveSlot(dev, hostdev->origstates.states.pci.remove_slot); + virPCIDeviceSetReprobe(dev, hostdev->origstates.states.pci.reprobe); + + if (virPCIDeviceListAdd(mgr->activePciHostdevs, dev) < 0) + goto cleanup; + dev = NULL; + } + + ret = 0; +cleanup: + virPCIDeviceFree(dev); + virObjectUnlock(mgr->activePciHostdevs); + virObjectUnlock(mgr->inactivePciHostdevs); + return ret; +} + + +int +virHostdevUpdateActiveUsbHostdevs(virHostdevManagerPtr mgr, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs, + const char *drv_name, + const char *dom_name) +{ + virDomainHostdevDefPtr hostdev = NULL; + size_t i; + int ret = -1; + + if (!nhostdevs) + return 0; + + if (!mgr) + return -1; + + virObjectLock(mgr->activeUsbHostdevs); + for (i = 0; i < nhostdevs; i++) { + virUSBDevicePtr usb = NULL; + hostdev = hostdevs[i]; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) + continue; + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) + continue; + + usb = virUSBDeviceNew(hostdev->source.subsys.u.usb.bus, + hostdev->source.subsys.u.usb.device, + NULL); + if (!usb) { + VIR_WARN("Unable to reattach USB device %03d.%03d " + "on driver %s domain %s", + hostdev->source.subsys.u.usb.bus, + hostdev->source.subsys.u.usb.device, + drv_name, dom_name); + continue; + } + + virUSBDeviceSetUsedBy(usb, drv_name, dom_name); + + if (virUSBDeviceListAdd(mgr->activeUsbHostdevs, usb) < 0) { + virUSBDeviceFree(usb); + goto cleanup; + } + } + ret = 0; +cleanup: + virObjectUnlock(mgr->activeUsbHostdevs); + return ret; +} + +int +virHostdevUpdateActiveScsiHostdevs(virHostdevManagerPtr mgr, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs, + const char *drv_name, + const char *dom_name) +{ + virDomainHostdevDefPtr hostdev = NULL; + size_t i; + int ret = -1; + virSCSIDevicePtr scsi = NULL; + virSCSIDevicePtr tmp = NULL; + + if (!nhostdevs) + return 0; + + if (!mgr) + return -1; + + virObjectLock(mgr->activeScsiHostdevs); + for (i = 0; i < nhostdevs; i++) { + hostdev = hostdevs[i]; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS || + hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI) + continue; + + if (!(scsi = virSCSIDeviceNew(NULL, + hostdev->source.subsys.u.scsi.adapter, + hostdev->source.subsys.u.scsi.bus, + hostdev->source.subsys.u.scsi.target, + hostdev->source.subsys.u.scsi.unit, + hostdev->readonly, + hostdev->shareable))) + goto cleanup; + + if ((tmp = virSCSIDeviceListFind(mgr->activeScsiHostdevs, scsi))) { + if (virSCSIDeviceSetUsedBy(tmp, drv_name, dom_name) < 0) { + virSCSIDeviceFree(scsi); + goto cleanup; + } + virSCSIDeviceFree(scsi); + } else { + if (virSCSIDeviceSetUsedBy(scsi, drv_name, dom_name) < 0 || + virSCSIDeviceListAdd(mgr->activeScsiHostdevs, scsi) < 0) { + virSCSIDeviceFree(scsi); + goto cleanup; + } + } + } + ret = 0; + +cleanup: + virObjectUnlock(mgr->activeScsiHostdevs); + return ret; +} + +int +virHostdevUpdateActiveHostdevs(virHostdevManagerPtr mgr, + const char *driver, + virDomainDefPtr def, + unsigned int flags) +{ + if (!def->nhostdevs) + return 0; + + if (flags & VIR_SP_PCI_HOSTDEV) { + if (virHostdevUpdateActivePciHostdevs(mgr, + def->hostdevs, + def->nhostdevs, + driver, def->name) < 0) + return -1; + } + + if (flags & VIR_SP_USB_HOSTDEV) { + if (virHostdevUpdateActiveUsbHostdevs(mgr, + def->hostdevs, + def->nhostdevs, + driver, def->name) < 0) + return -1; + } + + if (flags & VIR_SP_SCSI_HOSTDEV) { + if (virHostdevUpdateActiveScsiHostdevs(mgr, + def->hostdevs, + def->nhostdevs, + driver, def->name) < 0) + return -1; + } + + return 0; +} + + +void +virHostdevReAttachPciHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs) +{ + virPCIDeviceListPtr pcidevs; + size_t i; + char *oldStateDir = NULL; + + if (!nhostdevs || !mgr) + return; + + virObjectLock(mgr->activePciHostdevs); + virObjectLock(mgr->inactivePciHostdevs); + + /* for upgrade, if netconfig file is not found in mgr->stateDir, + * find in old state dir */ + if (VIR_STRDUP(oldStateDir, QEMU_STATE_DIR) < 0) + goto cleanup; + + if (!(pcidevs = virHostdevGetActivePciHostDeviceList(mgr, + hostdevs, + nhostdevs))) { + virErrorPtr err = virGetLastError(); + VIR_ERROR(_("Failed to allocate PCI device list: %s"), + err ? err->message : _("unknown error")); + virResetError(err); + goto cleanup; + } + + /* Again 4 loops; mark all devices as inactive before reset + * them and reset all the devices before re-attach. + * Attach mac and port profile parameters to devices + */ + for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) { + virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i); + virPCIDevicePtr activeDev = NULL; + const char *usedby_drvname; + const char *usedby_domname; + + /* Never delete the dev from list driver->activePciHostdevs + * if it's used by other domain. + */ + activeDev = virPCIDeviceListFind(mgr->activePciHostdevs, dev); + if (activeDev) { + virPCIDeviceGetUsedBy(activeDev, &usedby_drvname, &usedby_domname); + if (STRNEQ_NULLABLE(drv_name, usedby_drvname) || + STRNEQ_NULLABLE(dom_name, usedby_domname)) { + virPCIDeviceListSteal(pcidevs, dev); + continue; + } + } + + virPCIDeviceListSteal(mgr->activePciHostdevs, dev); + } + + /* + * For SRIOV net host devices, unset mac and port profile before + * reset and reattach device + */ + for (i = 0; i < nhostdevs; i++) + virHostdevNetConfigRestore(hostdevs[i], mgr->stateDir, oldStateDir); + + for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) { + virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i); + if (virPCIDeviceReset(dev, mgr->activePciHostdevs, + mgr->inactivePciHostdevs) < 0) { + virErrorPtr err = virGetLastError(); + VIR_ERROR(_("Failed to reset PCI device: %s"), + err ? err->message : _("unknown error")); + virResetError(err); + } + } + + while (virPCIDeviceListCount(pcidevs) > 0) { + virPCIDevicePtr dev = virPCIDeviceListStealIndex(pcidevs, 0); + virHostdevReAttachPciDevice(mgr, dev); + } + + virObjectUnref(pcidevs); +cleanup: + VIR_FREE(oldStateDir); + virObjectUnlock(mgr->activePciHostdevs); + virObjectUnlock(mgr->inactivePciHostdevs); +} + +void +virHostdevReAttachUsbHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs) +{ + size_t i; + + if (!nhostdevs || !mgr) + return; + + virObjectLock(mgr->activeUsbHostdevs); + for (i = 0; i < nhostdevs; i++) { + virDomainHostdevDefPtr hostdev = hostdevs[i]; + virUSBDevicePtr usb, tmp; + const char *usedby_drvname; + const char *usedby_domname; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) + continue; + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) + continue; + if (hostdev->missing) + continue; + + usb = virUSBDeviceNew(hostdev->source.subsys.u.usb.bus, + hostdev->source.subsys.u.usb.device, + NULL); + + if (!usb) { + VIR_WARN("Unable to reattach USB device %03d.%03d " + "on driver %s domain %s", + hostdev->source.subsys.u.usb.bus, + hostdev->source.subsys.u.usb.device, + drv_name, dom_name); + continue; + } + + /* Delete only those USB devices which belongs + * to domain. Therefore we want to steal only + * those devices from the list which were taken + * by domain */ + + tmp = virUSBDeviceListFind(mgr->activeUsbHostdevs, usb); + virUSBDeviceFree(usb); + + if (!tmp) { + VIR_WARN("Unable to find device %03d.%03d " + "in list of active USB devices", + hostdev->source.subsys.u.usb.bus, + hostdev->source.subsys.u.usb.device); + continue; + } + + virUSBDeviceGetUsedBy(tmp, &usedby_drvname, &usedby_domname); + if (STREQ_NULLABLE(drv_name, usedby_drvname) && + STREQ_NULLABLE(dom_name, usedby_domname)) { + VIR_DEBUG("Removing %03d.%03d dom=%s:%s", + hostdev->source.subsys.u.usb.bus, + hostdev->source.subsys.u.usb.device, + drv_name, dom_name); + virUSBDeviceListDel(mgr->activeUsbHostdevs, tmp); + } + + } + + virObjectUnlock(mgr->activeUsbHostdevs); +} + +void +virHostdevReAttachScsiHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs) +{ + size_t i; + + if (!nhostdevs || !mgr) + return; + + virObjectLock(mgr->activeScsiHostdevs); + for (i = 0; i < nhostdevs; i++) { + virDomainHostdevDefPtr hostdev = hostdevs[i]; + virSCSIDevicePtr scsi; + virSCSIDevicePtr tmp; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS || + hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI) + continue; + + if (!(scsi = virSCSIDeviceNew(NULL, + hostdev->source.subsys.u.scsi.adapter, + hostdev->source.subsys.u.scsi.bus, + hostdev->source.subsys.u.scsi.target, + hostdev->source.subsys.u.scsi.unit, + hostdev->readonly, + hostdev->shareable))) { + VIR_WARN("Unable to reattach SCSI device %s:%d:%d:%d on driver %s, " + "domain %s", + hostdev->source.subsys.u.scsi.adapter, + hostdev->source.subsys.u.scsi.bus, + hostdev->source.subsys.u.scsi.target, + hostdev->source.subsys.u.scsi.unit, + drv_name, dom_name); + continue; + } + + /* Only delete the devices which are marked as being used by @name, + * because qemuProcessStart could fail on the half way. */ + + if (!(tmp = virSCSIDeviceListFind(mgr->activeScsiHostdevs, scsi))) { + VIR_WARN("Unable to find device %s:%d:%d:%d " + "in list of active SCSI devices", + hostdev->source.subsys.u.scsi.adapter, + hostdev->source.subsys.u.scsi.bus, + hostdev->source.subsys.u.scsi.target, + hostdev->source.subsys.u.scsi.unit); + virSCSIDeviceFree(scsi); + continue; + } + + VIR_DEBUG("Removing %s:%d:%d:%d drv= %s dom=%s from activeScsiHostdevs", + hostdev->source.subsys.u.scsi.adapter, + hostdev->source.subsys.u.scsi.bus, + hostdev->source.subsys.u.scsi.target, + hostdev->source.subsys.u.scsi.unit, + drv_name, dom_name); + + virSCSIDeviceListDel(mgr->activeScsiHostdevs, tmp, drv_name, dom_name); + virSCSIDeviceFree(scsi); + } + + virObjectUnlock(mgr->activeScsiHostdevs); +} + +void +virHostdevReAttachDomainHostdevs(virHostdevManagerPtr mgr, + const char *driver, + virDomainDefPtr def, + unsigned int flags) +{ + if (!def->nhostdevs || !mgr) + return; + + if (flags & VIR_SP_PCI_HOSTDEV) { + virHostdevReAttachPciHostdevs(mgr, driver, def->name, + def->hostdevs, def->nhostdevs); + } + + if (flags & VIR_SP_USB_HOSTDEV) { + virHostdevReAttachUsbHostdevs(mgr, driver, def->name, + def->hostdevs, def->nhostdevs); + } + + if (flags & VIR_SP_SCSI_HOSTDEV) { + virHostdevReAttachScsiHostdevs(mgr, driver, def->name, + def->hostdevs, def->nhostdevs); + } +} + +/* following functions are used by NodeDevDetach/Reattach/Reset */ +int +virHostdevPciNodeDeviceDetach(virHostdevManagerPtr mgr, + virPCIDevicePtr pci) +{ + int ret; + + if (!mgr || !pci) + return -1; + + virObjectLock(mgr->activePciHostdevs); + virObjectLock(mgr->inactivePciHostdevs); + if (virPCIDeviceDetach(pci, mgr->activePciHostdevs, + mgr->inactivePciHostdevs) < 0) + ret = -1; + else + ret = 0; + + virObjectUnlock(mgr->inactivePciHostdevs); + virObjectUnlock(mgr->activePciHostdevs); + return ret; +} + +int +virHostdevPciNodeDeviceReAttach(virHostdevManagerPtr mgr, + virPCIDevicePtr pci) +{ + int ret = -1; + virPCIDevicePtr other; + + if (!mgr || !pci) + return -1; + + virObjectLock(mgr->activePciHostdevs); + virObjectLock(mgr->inactivePciHostdevs); + other = virPCIDeviceListFind(mgr->activePciHostdevs, pci); + if (other) { + const char *other_drvname; + const char *other_domname; + + virPCIDeviceGetUsedBy(other, &other_drvname, &other_domname); + if (other_drvname && other_domname) + virReportError(VIR_ERR_OPERATION_INVALID, + _("PCI device %s is still in use by driver %s, domain %s"), + virPCIDeviceGetName(pci), other_drvname, other_domname); + else + virReportError(VIR_ERR_OPERATION_INVALID, + _("PCI device %s is still in use"), + virPCIDeviceGetName(pci)); + goto out; + } + + virPCIDeviceReattachInit(pci); + + if (virPCIDeviceReattach(pci, mgr->activePciHostdevs, + mgr->inactivePciHostdevs) < 0) + goto out; + + ret = 0; +out: + virObjectUnlock(mgr->inactivePciHostdevs); + virObjectUnlock(mgr->activePciHostdevs); + return ret; +} + +int +virHostdevPciNodeDeviceReset(virHostdevManagerPtr mgr, + virPCIDevicePtr pci) +{ + int ret; + + if (!mgr || !pci) + return -1; + + virObjectLock(mgr->activePciHostdevs); + virObjectLock(mgr->inactivePciHostdevs); + if (virPCIDeviceReset(pci, mgr->activePciHostdevs, + mgr->inactivePciHostdevs) < 0) + ret = -1; + else + ret = 0; + + virObjectUnlock(mgr->inactivePciHostdevs); + virObjectUnlock(mgr->activePciHostdevs); + return ret; +} diff --git a/src/util/virhostdev.h b/src/util/virhostdev.h new file mode 100644 index 0000000..e4b5b86 --- /dev/null +++ b/src/util/virhostdev.h @@ -0,0 +1,134 @@ +/* virhostdev.h: hostdev management + * + * Copyright (C) 2014 SUSE LINUX Products GmbH, Nuernberg, Germany. + * Copyright (C) 2006-2007, 2009-2014 Red Hat, Inc. + * Copyright (C) 2006 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * Author: Chunyan Liu <cyliu@suse.com> + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#ifndef __VIR_HOSTDEV_H__ +# define __VIR_HOSTDEV_H__ + +# include "internal.h" + +# include "domain_conf.h" +# include "virpci.h" +# include "virusb.h" +# include "virscsi.h" + +typedef enum { + VIR_SP_PCI_HOSTDEV = (1 << 0), /* support pci passthrough */ + VIR_SP_USB_HOSTDEV = (1 << 1), /* support usb passthrough */ + VIR_SP_SCSI_HOSTDEV = (1 << 2), /* support scsi passthrough */ + + VIR_COLD_BOOT = (1 << 8), /* cold boot */ + VIR_STRICT_ACS_CHECK = (1 << 9), /* strict acs check */ +} virHostdevManagerFlag; + +typedef struct _virHostdevManager virHostdevManager; +typedef virHostdevManager *virHostdevManagerPtr; +struct _virHostdevManager{ + char *stateDir; + + virPCIDeviceListPtr activePciHostdevs; + virPCIDeviceListPtr inactivePciHostdevs; + virUSBDeviceListPtr activeUsbHostdevs; + virSCSIDeviceListPtr activeScsiHostdevs; +}; + +virHostdevManagerPtr virHostdevManagerGetDefault(void); + +bool virHostdevHostSupportsPassthroughVFIO(void); +bool virHostdevHostSupportsPassthroughKVM(void); + +/* functions used to prepare/unprepare hostdevs for domain */ +int virHostdevPrepareDomainHostdevs(virHostdevManagerPtr mgr, + const char *driver, + virDomainDefPtr def, + unsigned int flags); +void virHostdevReAttachDomainHostdevs(virHostdevManagerPtr mgr, + const char *driver, + virDomainDefPtr def, + unsigned int flags); +int virHostdevPreparePciHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name, + const unsigned char *uuid, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs, + unsigned int flags); +int virHostdevPrepareUsbHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs, + unsigned int flags); +int virHostdevPrepareScsiHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs); +void virHostdevReAttachPciHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs); +void virHostdevReAttachUsbHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs); +void virHostdevReAttachScsiHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *dom_name, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs); +int +virHostdevUpdateActivePciHostdevs(virHostdevManagerPtr mgr, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs, + const char *drv_name, + const char *dom_name); +int +virHostdevUpdateActiveUsbHostdevs(virHostdevManagerPtr mgr, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs, + const char *drv_name, + const char *dom_name); +int +virHostdevUpdateActiveScsiHostdevs(virHostdevManagerPtr mgr, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs, + const char *drv_name, + const char *dom_name); +int +virHostdevUpdateActiveHostdevs(virHostdevManagerPtr mgr, + const char *driver, + virDomainDefPtr def, + unsigned int flags); + +/* functions used by NodeDevDetach/Reattach/Reset */ +int virHostdevPciNodeDeviceDetach(virHostdevManagerPtr mgr, + virPCIDevicePtr pci); +int virHostdevPciNodeDeviceReAttach(virHostdevManagerPtr mgr, + virPCIDevicePtr pci); +int virHostdevPciNodeDeviceReset(virHostdevManagerPtr mgr, + virPCIDevicePtr pci); + +#endif /* __VIR_HOSTDEV_H__ */

Add unit test for hostdev common library. Current tests are based on virpcimock. Signed-off-by: Chunyan Liu <cyliu@suse.com> --- .gitignore | 1 + tests/Makefile.am | 5 + tests/virhostdevtest.c | 473 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 479 insertions(+), 0 deletions(-) create mode 100644 tests/virhostdevtest.c diff --git a/.gitignore b/.gitignore index 5fe60c6..b52f7b4 100644 --- a/.gitignore +++ b/.gitignore @@ -200,6 +200,7 @@ /tests/virendiantest /tests/virfiletest /tests/virhashtest +/tests/virhostdevtest /tests/viridentitytest /tests/virkeycodetest /tests/virkeyfiletest diff --git a/tests/Makefile.am b/tests/Makefile.am index 1698455..86cdb85 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -145,6 +145,7 @@ test_programs = virshtest sockettest \ virnetdevbandwidthtest \ virkmodtest \ vircapstest \ + virhostdevtest \ $(NULL) if WITH_REMOTE @@ -781,6 +782,10 @@ vircgroupmock_la_CFLAGS = $(AM_CFLAGS) vircgroupmock_la_LDFLAGS = -module -avoid-version \ -rpath /evil/libtool/hack/to/force/shared/lib/creation +virhostdevtest_SOURCES = \ + virhostdevtest.c testutils.h testutils.c +virhostdevtest_LDADD = $(LDADDS) + virpcitest_SOURCES = \ virpcitest.c testutils.h testutils.c virpcitest_LDADD = $(LDADDS) diff --git a/tests/virhostdevtest.c b/tests/virhostdevtest.c new file mode 100644 index 0000000..2dd9370 --- /dev/null +++ b/tests/virhostdevtest.c @@ -0,0 +1,473 @@ +/* + * Copyright (C) 2014 SUSE LINUX Products GmbH, Nuernberg, Germany. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * Author: Chunyan Liu <cyliu@suse.com> + */ + +#include <config.h> + +#include "testutils.h" + +#ifdef __linux__ + +# include <stdlib.h> +# include <stdio.h> +# include <sys/types.h> +# include <sys/stat.h> +# include <fcntl.h> +# include "virlog.h" +# include "virhostdev.h" + +# define VIR_FROM_THIS VIR_FROM_NONE + +# define CHECK_LIST_COUNT(list, cnt) \ + if ((count = virPCIDeviceListCount(list)) != cnt) { \ + virReportError(VIR_ERR_INTERNAL_ERROR, \ + "Unexpected count of items in " #list ": %d, " \ + "expecting %zu", count, (size_t) cnt); \ + goto cleanup; \ + } + +# define TEST_STATE_DIR abs_builddir "/hostdevmgr" +static const char *drv_name = "test_driver"; +static const char *dom_name = "test_domain"; +static const unsigned char *uuid = + (unsigned char *)("f92360b0-2541-8791-fb32-d1f838811541"); +static int nhostdevs = 3; +static virDomainHostdevDefPtr hostdevs[] = {NULL, NULL, NULL}; +static virPCIDevicePtr dev[] = {NULL, NULL, NULL}; +static virHostdevManagerPtr mgr = NULL; + +static void +myCleanup(void) +{ + size_t i; + for (i = 0; i < nhostdevs; i++) { + virPCIDeviceFree(dev[i]); + virDomainHostdevDefFree(hostdevs[i]); + } + + if (mgr) { + virObjectUnref(mgr->activePciHostdevs); + virObjectUnref(mgr->inactivePciHostdevs); + virObjectUnref(mgr->activeUsbHostdevs); + VIR_FREE(mgr->stateDir); + VIR_FREE(mgr); + } +} + +static int +myInit(void) +{ + size_t i; + + for (i = 0; i < nhostdevs; i++) { + virDomainHostdevSubsys subsys; + hostdevs[i] = virDomainHostdevDefAlloc(); + if (!hostdevs[i]) + goto cleanup; + hostdevs[i]->mode = VIR_DOMAIN_HOSTDEV_MODE_SUBSYS; + subsys.type = VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI; + subsys.u.pci.addr.domain = 0; + subsys.u.pci.addr.bus = 0; + subsys.u.pci.addr.slot = i + 1; + subsys.u.pci.addr.function = 0; + subsys.u.pci.backend = VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM; + hostdevs[i]->source.subsys = subsys; + } + + for (i = 0; i < nhostdevs; i++) { + if (!(dev[i] = virPCIDeviceNew(0, 0, i + 1, 0)) || + virPCIDeviceSetStubDriver(dev[i], "pci-stub") < 0) + goto cleanup; + } + + if (VIR_ALLOC(mgr) < 0) + goto cleanup; + if ((mgr->activePciHostdevs = virPCIDeviceListNew()) == NULL) + goto cleanup; + if ((mgr->activeUsbHostdevs = virUSBDeviceListNew()) == NULL) + goto cleanup; + if ((mgr->inactivePciHostdevs = virPCIDeviceListNew()) == NULL) + goto cleanup; + if ((mgr->activeScsiHostdevs = virSCSIDeviceListNew()) == NULL) + goto cleanup; + if (VIR_STRDUP(mgr->stateDir, TEST_STATE_DIR) < 0) + goto cleanup; + if (virFileMakePath(mgr->stateDir) < 0) + goto cleanup; + + return 0; + +cleanup: + myCleanup(); + return -1; +} + +static int +testVirHostdevPreparePciHostdevs_unmanaged(const void *oaque ATTRIBUTE_UNUSED) +{ + int ret = -1; + size_t i; + int count, count1, count2; + + for (i = 0; i < nhostdevs; i++) + hostdevs[i]->managed = false; + + /* Test invalid args */ + VIR_DEBUG("Test hostdev mgr=NULL\n"); + if (!virHostdevPreparePciHostdevs(NULL, drv_name, dom_name, uuid, + hostdevs, nhostdevs, 0)) + goto cleanup; + + count1 = virPCIDeviceListCount(mgr->activePciHostdevs); + count2 = virPCIDeviceListCount(mgr->inactivePciHostdevs); + + /* Test normal functionality */ + VIR_DEBUG("Test 0 hostdevs\n"); + if (virHostdevPreparePciHostdevs(mgr, drv_name, dom_name, uuid, + NULL, 0, 0) < 0) + goto cleanup; + CHECK_LIST_COUNT(mgr->activePciHostdevs, count1); + + /* Test unmanaged hostdevs */ + VIR_DEBUG("Test >=1 unmanaged hostdevs\n"); + if (virHostdevPreparePciHostdevs(mgr, drv_name, dom_name, uuid, + hostdevs, nhostdevs, 0) < 0) + goto cleanup; + CHECK_LIST_COUNT(mgr->activePciHostdevs, count1 + 3); + CHECK_LIST_COUNT(mgr->inactivePciHostdevs, count2 - 3); + + /* Test conflict */ + count1 = virPCIDeviceListCount(mgr->activePciHostdevs); + count2 = virPCIDeviceListCount(mgr->inactivePciHostdevs); + VIR_DEBUG("Test: prepare same hostdevs for same driver/domain again\n"); + if (!virHostdevPreparePciHostdevs(mgr, drv_name, dom_name, uuid, + &hostdevs[0], 1, 0)) + goto cleanup; + CHECK_LIST_COUNT(mgr->activePciHostdevs, count1); + CHECK_LIST_COUNT(mgr->inactivePciHostdevs, count2); + + VIR_DEBUG("Test: prepare same hostdevs for same driver, diff domain again\n"); + if (!virHostdevPreparePciHostdevs(mgr, drv_name, "test_domain1", uuid, + &hostdevs[1], 1, 0)) + goto cleanup; + CHECK_LIST_COUNT(mgr->activePciHostdevs, count1); + CHECK_LIST_COUNT(mgr->inactivePciHostdevs, count2); + + VIR_DEBUG("Test: prepare same hostdevs for diff driver/domain again\n"); + if (!virHostdevPreparePciHostdevs(mgr, "test_driver1", dom_name, uuid, + &hostdevs[2], 1, 0)) + goto cleanup; + CHECK_LIST_COUNT(mgr->activePciHostdevs, count1); + CHECK_LIST_COUNT(mgr->inactivePciHostdevs, count2); + + ret = 0; + +cleanup: + return ret; + +} + +static int +testVirHostdevReAttachPciHostdevs_unmanaged(const void *oaque ATTRIBUTE_UNUSED) +{ + int ret = -1; + size_t i; + int count, count1, count2; + + for (i = 0; i < nhostdevs; i++) { + if (hostdevs[i]->managed != false) { + VIR_DEBUG("invalid test\n"); + return -1; + } + } + + VIR_DEBUG("Test hostdev mgr=NULL\n"); + virHostdevReAttachPciHostdevs(NULL, drv_name, dom_name, + hostdevs, nhostdevs); + + count1 = virPCIDeviceListCount(mgr->activePciHostdevs); + count2 = virPCIDeviceListCount(mgr->inactivePciHostdevs); + + VIR_DEBUG("Test 0 hostdevs\n"); + virHostdevReAttachPciHostdevs(mgr, drv_name, dom_name, NULL, 0); + CHECK_LIST_COUNT(mgr->activePciHostdevs, count1); + + VIR_DEBUG("Test >=1 unmanaged hostdevs\n"); + virHostdevReAttachPciHostdevs(mgr, drv_name, dom_name, hostdevs, nhostdevs); + CHECK_LIST_COUNT(mgr->activePciHostdevs, count1 - 3); + CHECK_LIST_COUNT(mgr->inactivePciHostdevs, count2 + 3); + + ret = 0; + +cleanup: + return ret; + +} + +static int +testVirHostdevPreparePciHostdevs_managed(const void *oaque ATTRIBUTE_UNUSED) +{ + int ret = -1; + size_t i; + int count, count1; + + for (i = 0; i < nhostdevs; i++) + hostdevs[i]->managed = true; + + count1 = virPCIDeviceListCount(mgr->activePciHostdevs); + + /* Test normal functionality */ + VIR_DEBUG("Test >=1 hostdevs\n"); + if (virHostdevPreparePciHostdevs(mgr, drv_name, dom_name, uuid, + hostdevs, nhostdevs, 0) < 0) + goto cleanup; + CHECK_LIST_COUNT(mgr->activePciHostdevs, count1 + 3); + + /* Test conflict */ + count1 = virPCIDeviceListCount(mgr->activePciHostdevs); + VIR_DEBUG("Test: prepare same hostdevs for same driver/domain again\n"); + if (!virHostdevPreparePciHostdevs(mgr, drv_name, dom_name, uuid, + &hostdevs[0], 1, 0)) + goto cleanup; + CHECK_LIST_COUNT(mgr->activePciHostdevs, count1); + + VIR_DEBUG("Test: prepare same hostdevs for same driver, diff domain again\n"); + if (!virHostdevPreparePciHostdevs(mgr, drv_name, "test_domain1", uuid, + &hostdevs[1], 1, 0)) + goto cleanup; + CHECK_LIST_COUNT(mgr->activePciHostdevs, count1); + + VIR_DEBUG("Test: prepare same hostdevs for diff driver/domain again\n"); + if (!virHostdevPreparePciHostdevs(mgr, "test_driver1", dom_name, uuid, + &hostdevs[2], 1, 0)) + goto cleanup; + CHECK_LIST_COUNT(mgr->activePciHostdevs, count1); + + ret = 0; + +cleanup: + return ret; + +} + +static int +testVirHostdevReAttachPciHostdevs_managed(const void *oaque ATTRIBUTE_UNUSED) +{ + int ret = -1; + size_t i; + int count, count1; + + for (i = 0; i < nhostdevs; i++) { + if (hostdevs[i]->managed != true) { + VIR_DEBUG("invalid test\n"); + return -1; + } + } + + VIR_DEBUG("Test hostdev mgr=NULL\n"); + virHostdevReAttachPciHostdevs(NULL, drv_name, dom_name, + hostdevs, nhostdevs); + + count1 = virPCIDeviceListCount(mgr->activePciHostdevs); + + VIR_DEBUG("Test 0 hostdevs\n"); + virHostdevReAttachPciHostdevs(mgr, drv_name, dom_name, NULL, 0); + CHECK_LIST_COUNT(mgr->activePciHostdevs, count1); + + VIR_DEBUG("Test >=1 hostdevs\n"); + virHostdevReAttachPciHostdevs(mgr, drv_name, dom_name, hostdevs, nhostdevs); + CHECK_LIST_COUNT(mgr->activePciHostdevs, count1 - 3); + + ret = 0; + +cleanup: + return ret; + +} + +static int +testVirHostdevDetachPciNodeDevice(const void *oaque ATTRIBUTE_UNUSED) +{ + int ret = -1; + size_t i; + int count, count1; + + if (!virHostdevPciNodeDeviceDetach(NULL, dev[0])) + goto cleanup; + + if (!virHostdevPciNodeDeviceDetach(mgr, NULL)) + goto cleanup; + + for (i = 0; i < nhostdevs; i++) { + count1 = virPCIDeviceListCount(mgr->inactivePciHostdevs); + if (virHostdevPciNodeDeviceDetach(mgr, dev[i]) < 0) + goto cleanup; + CHECK_LIST_COUNT(mgr->inactivePciHostdevs, count1 + 1); + } + + ret = 0; + +cleanup: + return ret; +} +static int +testVirHostdevResetPciNodeDevice(const void *oaque ATTRIBUTE_UNUSED) +{ + int ret = -1; + size_t i; + + if (!virHostdevPciNodeDeviceReset(NULL, dev[0])) + goto cleanup; + + if (!virHostdevPciNodeDeviceReAttach(mgr, NULL)) + goto cleanup; + + for (i = 0; i < nhostdevs; i++) { + if (virHostdevPciNodeDeviceReset(mgr, dev[i]) < 0) + goto cleanup; + } + + ret = 0; + +cleanup: + return ret; + +} + +static int +testVirHostdevReAttachPciNodeDevice(const void *oaque ATTRIBUTE_UNUSED) +{ + int ret = -1; + size_t i; + int count, count1; + + if (!virHostdevPciNodeDeviceReAttach(NULL, dev[0])) + goto cleanup; + + if (!virHostdevPciNodeDeviceReAttach(mgr, NULL)) + goto cleanup; + + for (i = 0; i < nhostdevs; i++) { + count1 = virPCIDeviceListCount(mgr->inactivePciHostdevs); + if (virHostdevPciNodeDeviceReAttach(mgr, dev[i]) < 0) + goto cleanup; + CHECK_LIST_COUNT(mgr->inactivePciHostdevs, count1 - 1); + } + + ret = 0; + +cleanup: + return ret; + +} + +static int +testVirHostdevUpdateActivePciHostdevs(const void *oaque ATTRIBUTE_UNUSED) +{ + int ret = -1; + int count, count1; + + VIR_DEBUG("Test hostdev mgr=NULL\n"); + if (!virHostdevUpdateActivePciHostdevs(NULL, hostdevs, nhostdevs, + drv_name, dom_name)) + goto cleanup; + + count1 = virPCIDeviceListCount(mgr->activePciHostdevs); + + VIR_DEBUG("Test 0 hostdevs\n"); + if (virHostdevUpdateActivePciHostdevs(mgr, NULL, 0, + drv_name, dom_name) < 0) + goto cleanup; + CHECK_LIST_COUNT(mgr->activePciHostdevs, count1); + + VIR_DEBUG("Test >=1 hostdevs\n"); + if (virHostdevUpdateActivePciHostdevs(mgr, hostdevs, nhostdevs, + drv_name, dom_name) < 0) + goto cleanup; + CHECK_LIST_COUNT(mgr->activePciHostdevs, count1 + 3); + + ret = 0; + +cleanup: + return ret; +} + +# define FAKESYSFSDIRTEMPLATE abs_builddir "/fakesysfsdir-XXXXXX" + +static int +mymain(void) +{ + int ret = 0; + char *fakesysfsdir; + + if (VIR_STRDUP_QUIET(fakesysfsdir, FAKESYSFSDIRTEMPLATE) < 0) { + fprintf(stderr, "Out of memory\n"); + abort(); + } + + if (!mkdtemp(fakesysfsdir)) { + fprintf(stderr, "Cannot create fakesysfsdir"); + abort(); + } + + setenv("LIBVIRT_FAKE_SYSFS_DIR", fakesysfsdir, 1); + +# define DO_TEST(fnc) \ + do { \ + VIR_DEBUG("\nTesting: %s", #fnc); \ + if (virtTestRun(#fnc, fnc, NULL) < 0) \ + ret = -1; \ + } while (0) + + if (myInit() < 0) + fprintf(stderr, "Init data structures failed."); + + DO_TEST(testVirHostdevDetachPciNodeDevice); + if (virHostdevHostSupportsPassthroughKVM()) { + /* following tests would check KVM support */ + DO_TEST(testVirHostdevPreparePciHostdevs_unmanaged); + DO_TEST(testVirHostdevReAttachPciHostdevs_unmanaged); + } + DO_TEST(testVirHostdevResetPciNodeDevice); + DO_TEST(testVirHostdevReAttachPciNodeDevice); + if (virHostdevHostSupportsPassthroughKVM()) { + /* following tests would check KVM support */ + DO_TEST(testVirHostdevPreparePciHostdevs_managed); + DO_TEST(testVirHostdevReAttachPciHostdevs_managed); + } + DO_TEST(testVirHostdevUpdateActivePciHostdevs); + + myCleanup(); + + if (getenv("LIBVIRT_SKIP_CLEANUP") == NULL) + virFileDeleteTree(fakesysfsdir); + + VIR_FREE(fakesysfsdir); + + return ret==0 ? EXIT_SUCCESS : EXIT_FAILURE; +} + +VIRT_TEST_MAIN_PRELOAD(mymain, abs_builddir "/.libs/virpcimock.so") +#else +int +main(void) +{ + return EXIT_AM_SKIP; +} +#endif -- 1.6.0.2

Change qemu driver to use hostdev common library instead of APIs in qemu_hostdev.[ch] Improve some test files. Signed-off-by: Chunyan Liu <cyliu@suse.com> --- po/POTFILES.in | 1 - src/Makefile.am | 1 - src/qemu/qemu_command.c | 1 - src/qemu/qemu_conf.h | 9 +--- src/qemu/qemu_domain.c | 4 +- src/qemu/qemu_driver.c | 81 ++++++------------------------- src/qemu/qemu_hostdev.h | 5 -- src/qemu/qemu_hotplug.c | 119 +++++++++++++++++++--------------------------- src/qemu/qemu_process.c | 31 ++++++++---- 9 files changed, 89 insertions(+), 163 deletions(-) diff --git a/po/POTFILES.in b/po/POTFILES.in index 60c226a..9e71db3 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -99,7 +99,6 @@ src/qemu/qemu_command.c src/qemu/qemu_conf.c src/qemu/qemu_domain.c src/qemu/qemu_driver.c -src/qemu/qemu_hostdev.c src/qemu/qemu_hotplug.c src/qemu/qemu_migration.c src/qemu/qemu_monitor.c diff --git a/src/Makefile.am b/src/Makefile.am index 04ea01b..5712055 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -673,7 +673,6 @@ QEMU_DRIVER_SOURCES = \ qemu/qemu_command.c qemu/qemu_command.h \ qemu/qemu_domain.c qemu/qemu_domain.h \ qemu/qemu_cgroup.c qemu/qemu_cgroup.h \ - qemu/qemu_hostdev.c qemu/qemu_hostdev.h \ qemu/qemu_hotplug.c qemu/qemu_hotplug.h \ qemu/qemu_hotplugpriv.h \ qemu/qemu_conf.c qemu/qemu_conf.h \ diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index e499d54..ccdd50f 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -24,7 +24,6 @@ #include <config.h> #include "qemu_command.h" -#include "qemu_hostdev.h" #include "qemu_capabilities.h" #include "qemu_bridge_filter.h" #include "cpu/cpu.h" diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index 158cc1a..3512589 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -55,6 +55,7 @@ # define QEMU_DRIVER_NAME "QEMU" +# define QEMU_DRIVER_NAME "QEMU" typedef struct _virQEMUDriver virQEMUDriver; typedef virQEMUDriver *virQEMUDriverPtr; @@ -215,14 +216,6 @@ struct _virQEMUDriver { /* Immutable pointer. self-locking APIs */ virSecurityManagerPtr securityManager; - /* Immutable pointers. Requires locks to be held before - * calling APIs. activePciHostdevs must be locked before - * inactivePciHostdevs */ - virPCIDeviceListPtr activePciHostdevs; - virPCIDeviceListPtr inactivePciHostdevs; - virUSBDeviceListPtr activeUsbHostdevs; - virSCSIDeviceListPtr activeScsiHostdevs; - /* Immutable pointer. Unsafe APIs. XXX */ virHashTablePtr sharedDevices; diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 55e707e..c9586e5 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -38,7 +38,7 @@ #include "virtime.h" #include "virstoragefile.h" #include "virstring.h" -#include "qemu_hostdev.h" +#include "virhostdev.h" #include <sys/time.h> #include <fcntl.h> @@ -909,7 +909,7 @@ qemuDomainDeviceDefPostParse(virDomainDeviceDefPtr dev, hostdev->source.subsys.u.pci.backend = VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM; if (driver && driver->qemuCapsCache) { - bool supportsPassthroughVFIO = qemuHostdevHostSupportsPassthroughVFIO(); + bool supportsPassthroughVFIO = virHostdevHostSupportsPassthroughVFIO(); qemuCaps = virQEMUCapsCacheLookupCopy(driver->qemuCapsCache, def->emulator); if (supportsPassthroughVFIO && qemuCaps && diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 1fe8992..a812e47 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -50,7 +50,6 @@ #include "qemu_capabilities.h" #include "qemu_command.h" #include "qemu_cgroup.h" -#include "qemu_hostdev.h" #include "qemu_hotplug.h" #include "qemu_monitor.h" #include "qemu_bridge_filter.h" @@ -94,6 +93,7 @@ #include "virstring.h" #include "viraccessapicheck.h" #include "viraccessapicheckqemu.h" +#include "virhostdev.h" #define VIR_FROM_THIS VIR_FROM_QEMU @@ -695,18 +695,6 @@ qemuStateInitialize(bool privileged, if (qemuSecurityInit(qemu_driver) < 0) goto error; - if ((qemu_driver->activePciHostdevs = virPCIDeviceListNew()) == NULL) - goto error; - - if ((qemu_driver->activeUsbHostdevs = virUSBDeviceListNew()) == NULL) - goto error; - - if ((qemu_driver->inactivePciHostdevs = virPCIDeviceListNew()) == NULL) - goto error; - - if ((qemu_driver->activeScsiHostdevs = virSCSIDeviceListNew()) == NULL) - goto error; - if (!(qemu_driver->sharedDevices = virHashCreate(30, qemuSharedDeviceEntryFree))) goto error; @@ -983,10 +971,6 @@ qemuStateCleanup(void) { virNWFilterUnRegisterCallbackDriver(&qemuCallbackDriver); virObjectUnref(qemu_driver->config); - virObjectUnref(qemu_driver->activePciHostdevs); - virObjectUnref(qemu_driver->inactivePciHostdevs); - virObjectUnref(qemu_driver->activeUsbHostdevs); - virObjectUnref(qemu_driver->activeScsiHostdevs); virHashFree(qemu_driver->sharedDevices); virObjectUnref(qemu_driver->caps); virQEMUCapsCacheFree(qemu_driver->qemuCapsCache); @@ -11199,14 +11183,14 @@ qemuNodeDeviceDetachFlags(virNodeDevicePtr dev, const char *driverName, unsigned int flags) { - virQEMUDriverPtr driver = dev->conn->privateData; virPCIDevicePtr pci = NULL; unsigned domain = 0, bus = 0, slot = 0, function = 0; int ret = -1; virNodeDeviceDefPtr def = NULL; char *xml = NULL; - bool legacy = qemuHostdevHostSupportsPassthroughLegacy(); - bool vfio = qemuHostdevHostSupportsPassthroughVFIO(); + bool legacy = virHostdevHostSupportsPassthroughKVM(); + bool vfio = virHostdevHostSupportsPassthroughVFIO(); + virHostdevManagerPtr hostdev_mgr; virCheckFlags(0, -1); @@ -11265,18 +11249,12 @@ qemuNodeDeviceDetachFlags(virNodeDevicePtr dev, goto cleanup; } - virObjectLock(driver->activePciHostdevs); - virObjectLock(driver->inactivePciHostdevs); - - if (virPCIDeviceDetach(pci, driver->activePciHostdevs, - driver->inactivePciHostdevs) < 0) { - goto out; - } + hostdev_mgr = virHostdevManagerGetDefault(); + if (hostdev_mgr == NULL || + virHostdevPciNodeDeviceDetach(hostdev_mgr, pci) < 0) + goto cleanup; ret = 0; -out: - virObjectUnlock(driver->inactivePciHostdevs); - virObjectUnlock(driver->activePciHostdevs); cleanup: virPCIDeviceFree(pci); virNodeDeviceDefFree(def); @@ -11293,13 +11271,12 @@ qemuNodeDeviceDettach(virNodeDevicePtr dev) static int qemuNodeDeviceReAttach(virNodeDevicePtr dev) { - virQEMUDriverPtr driver = dev->conn->privateData; virPCIDevicePtr pci = NULL; - virPCIDevicePtr other; unsigned domain = 0, bus = 0, slot = 0, function = 0; int ret = -1; virNodeDeviceDefPtr def = NULL; char *xml = NULL; + virHostdevManagerPtr hostdev_mgr; xml = virNodeDeviceGetXMLDesc(dev, 0); if (!xml) @@ -11319,35 +11296,13 @@ qemuNodeDeviceReAttach(virNodeDevicePtr dev) if (!pci) goto cleanup; - virObjectLock(driver->activePciHostdevs); - virObjectLock(driver->inactivePciHostdevs); - other = virPCIDeviceListFind(driver->activePciHostdevs, pci); - if (other) { - const char *other_name = NULL; - const char *other_drvname = NULL; - virPCIDeviceGetUsedBy(other, &other_drvname, &other_name); - - if (other_name) - virReportError(VIR_ERR_OPERATION_INVALID, - _("PCI device %s is still in use by domain %s"), - virPCIDeviceGetName(pci), other_name); - else - virReportError(VIR_ERR_OPERATION_INVALID, - _("PCI device %s is still in use"), - virPCIDeviceGetName(pci)); - goto out; - } - - virPCIDeviceReattachInit(pci); - - if (virPCIDeviceReattach(pci, driver->activePciHostdevs, - driver->inactivePciHostdevs) < 0) + hostdev_mgr = virHostdevManagerGetDefault(); + if (hostdev_mgr == NULL || + virHostdevPciNodeDeviceReAttach(hostdev_mgr, pci) < 0) goto out; ret = 0; out: - virObjectUnlock(driver->inactivePciHostdevs); - virObjectUnlock(driver->activePciHostdevs); virPCIDeviceFree(pci); cleanup: virNodeDeviceDefFree(def); @@ -11358,12 +11313,12 @@ cleanup: static int qemuNodeDeviceReset(virNodeDevicePtr dev) { - virQEMUDriverPtr driver = dev->conn->privateData; virPCIDevicePtr pci; unsigned domain = 0, bus = 0, slot = 0, function = 0; int ret = -1; virNodeDeviceDefPtr def = NULL; char *xml = NULL; + virHostdevManagerPtr hostdev_mgr; xml = virNodeDeviceGetXMLDesc(dev, 0); if (!xml) @@ -11383,17 +11338,13 @@ qemuNodeDeviceReset(virNodeDevicePtr dev) if (!pci) goto cleanup; - virObjectLock(driver->activePciHostdevs); - virObjectLock(driver->inactivePciHostdevs); - - if (virPCIDeviceReset(pci, driver->activePciHostdevs, - driver->inactivePciHostdevs) < 0) + hostdev_mgr = virHostdevManagerGetDefault(); + if (hostdev_mgr == NULL || + virHostdevPciNodeDeviceReset(hostdev_mgr, pci) < 0) goto out; ret = 0; out: - virObjectUnlock(driver->inactivePciHostdevs); - virObjectUnlock(driver->activePciHostdevs); virPCIDeviceFree(pci); cleanup: virNodeDeviceDefFree(def); diff --git a/src/qemu/qemu_hostdev.h b/src/qemu/qemu_hostdev.h index 128032d..c4a9226 100644 --- a/src/qemu/qemu_hostdev.h +++ b/src/qemu/qemu_hostdev.h @@ -27,11 +27,6 @@ # include "qemu_conf.h" # include "domain_conf.h" -typedef enum { - VIR_STRICT_ACS_CHECK = (1 << 0), /* strict acs check */ - VIR_COLD_BOOT = (1 << 1), /* cold boot */ -} qemuHostdevFlag; - int qemuUpdateActivePciHostdevs(virQEMUDriverPtr driver, virDomainDefPtr def); int qemuUpdateActiveUsbHostdevs(virQEMUDriverPtr driver, diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index afa92da..bb9b3ff 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -30,7 +30,6 @@ #include "qemu_domain.h" #include "qemu_command.h" #include "qemu_bridge_filter.h" -#include "qemu_hostdev.h" #include "domain_audit.h" #include "domain_nwfilter.h" #include "virlog.h" @@ -50,6 +49,7 @@ #include "virstoragefile.h" #include "virstring.h" #include "virtime.h" +#include "virhostdev.h" #define VIR_FROM_THIS VIR_FROM_QEMU #define CHANGE_MEDIA_RETRIES 10 @@ -1156,13 +1156,17 @@ qemuDomainAttachHostPciDevice(virQEMUDriverPtr driver, unsigned long long memKB; virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); unsigned int flags = 0; + virHostdevManagerPtr hostdev_mgr; if (VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs + 1) < 0) return -1; if (!cfg->relaxedACS) flags |= VIR_STRICT_ACS_CHECK; - if (qemuPrepareHostdevPCIDevices(driver, vm->def->name, vm->def->uuid, + hostdev_mgr = virHostdevManagerGetDefault(); + if (hostdev_mgr == NULL || + virHostdevPreparePciHostdevs(hostdev_mgr, QEMU_DRIVER_NAME, + vm->def->name, vm->def->uuid, &hostdev, 1, flags) < 0) return -1; @@ -1273,7 +1277,8 @@ error: if (releaseaddr) qemuDomainReleaseDeviceAddress(vm, hostdev->info, NULL); - qemuDomainReAttachHostdevDevices(driver, vm->def->name, &hostdev, 1); + virHostdevReAttachPciHostdevs(hostdev_mgr, QEMU_DRIVER_NAME, + vm->def->name, &hostdev, 1); VIR_FREE(devstr); VIR_FREE(configfd_name); @@ -1459,28 +1464,23 @@ qemuDomainAttachHostUsbDevice(virQEMUDriverPtr driver, virDomainHostdevDefPtr hostdev) { qemuDomainObjPrivatePtr priv = vm->privateData; - virUSBDeviceList *list = NULL; - virUSBDevicePtr usb = NULL; char *devstr = NULL; bool added = false; bool teardowncgroup = false; bool teardownlabel = false; int ret = -1; - - if (qemuFindHostdevUSBDevice(hostdev, true, &usb) < 0) + virHostdevManagerPtr hostdev_mgr; + + hostdev_mgr = virHostdevManagerGetDefault(); + if (hostdev_mgr == NULL || + virHostdevPrepareUsbHostdevs(hostdev_mgr, + QEMU_DRIVER_NAME, + vm->def->name, + &hostdev, + 1, 0) < 0) return -1; - if (!(list = virUSBDeviceListNew())) - goto cleanup; - - if (virUSBDeviceListAdd(list, usb) < 0) - goto cleanup; - - if (qemuPrepareHostdevUSBDevices(driver, vm->def->name, list) < 0) - goto cleanup; - added = true; - virUSBDeviceListSteal(list, usb); if (qemuSetupHostdevCGroup(vm, hostdev) < 0) goto cleanup; @@ -1525,13 +1525,9 @@ cleanup: vm->def, hostdev, NULL) < 0) VIR_WARN("Unable to restore host device labelling on hotplug fail"); if (added) - virUSBDeviceListSteal(driver->activeUsbHostdevs, usb); + virHostdevReAttachUsbHostdevs(hostdev_mgr, QEMU_DRIVER_NAME, + vm->def->name, &hostdev, 1); } - if (list && usb && - !virUSBDeviceListFind(list, usb) && - !virUSBDeviceListFind(driver->activeUsbHostdevs, usb)) - virUSBDeviceFree(usb); - virObjectUnref(list); VIR_FREE(devstr); return ret; } @@ -1549,6 +1545,7 @@ qemuDomainAttachHostScsiDevice(virQEMUDriverPtr driver, bool teardowncgroup = false; bool teardownlabel = false; virDomainDeviceDef dev; + virHostdevManagerPtr hostdev_mgr; if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DRIVE) || !virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE) || @@ -1568,8 +1565,12 @@ qemuDomainAttachHostScsiDevice(virQEMUDriverPtr driver, if (qemuAddSharedDevice(driver, &dev, vm->def->name) < 0) return -1; - if (qemuPrepareHostdevSCSIDevices(driver, vm->def->name, - &hostdev, 1)) { + hostdev_mgr = virHostdevManagerGetDefault(); + if (hostdev_mgr == NULL || + virHostdevPrepareScsiHostdevs(hostdev_mgr, + QEMU_DRIVER_NAME, + vm->def->name, + &hostdev, 1) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Unable to prepare scsi hostdev: %s:%d:%d:%d"), hostdev->source.subsys.u.scsi.adapter, @@ -1627,7 +1628,8 @@ qemuDomainAttachHostScsiDevice(virQEMUDriverPtr driver, cleanup: if (ret < 0) { ignore_value(qemuRemoveSharedDevice(driver, &dev, vm->def->name)); - qemuDomainReAttachHostScsiDevices(driver, vm->def->name, &hostdev, 1); + virHostdevReAttachScsiHostdevs(hostdev_mgr, QEMU_DRIVER_NAME, + vm->def->name, &hostdev, 1); if (teardowncgroup && qemuTeardownHostdevCgroup(vm, hostdev) < 0) VIR_WARN("Unable to remove host device cgroup ACL on hotplug fail"); if (teardownlabel && @@ -2540,54 +2542,29 @@ qemuDomainRemoveControllerDevice(virQEMUDriverPtr driver, static void -qemuDomainRemovePCIHostDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm, +qemuDomainRemovePCIHostDevice(virDomainObjPtr vm, virDomainHostdevDefPtr hostdev) { - virDomainHostdevSubsysPtr subsys = &hostdev->source.subsys; - virPCIDevicePtr pci; - virPCIDevicePtr activePci; - - virObjectLock(driver->activePciHostdevs); - virObjectLock(driver->inactivePciHostdevs); - pci = virPCIDeviceNew(subsys->u.pci.addr.domain, subsys->u.pci.addr.bus, - subsys->u.pci.addr.slot, subsys->u.pci.addr.function); - if (pci) { - activePci = virPCIDeviceListSteal(driver->activePciHostdevs, pci); - if (activePci && - virPCIDeviceReset(activePci, driver->activePciHostdevs, - driver->inactivePciHostdevs) == 0) { - qemuReattachPciDevice(activePci, driver); - } else { - /* reset of the device failed, treat it as if it was returned */ - virPCIDeviceFree(activePci); - } - virPCIDeviceFree(pci); - } - virObjectUnlock(driver->activePciHostdevs); - virObjectUnlock(driver->inactivePciHostdevs); + virHostdevManagerPtr hostdev_mgr; + + hostdev_mgr = virHostdevManagerGetDefault(); + if (hostdev_mgr != NULL) + virHostdevReAttachPciHostdevs(hostdev_mgr, QEMU_DRIVER_NAME, + vm->def->name, &hostdev, 1); qemuDomainReleaseDeviceAddress(vm, hostdev->info, NULL); } static void -qemuDomainRemoveUSBHostDevice(virQEMUDriverPtr driver, - virDomainObjPtr vm ATTRIBUTE_UNUSED, +qemuDomainRemoveUSBHostDevice(virDomainObjPtr vm, virDomainHostdevDefPtr hostdev) { - virDomainHostdevSubsysPtr subsys = &hostdev->source.subsys; - virUSBDevicePtr usb; - - usb = virUSBDeviceNew(subsys->u.usb.bus, subsys->u.usb.device, NULL); - if (usb) { - virObjectLock(driver->activeUsbHostdevs); - virUSBDeviceListDel(driver->activeUsbHostdevs, usb); - virObjectUnlock(driver->activeUsbHostdevs); - virUSBDeviceFree(usb); - } else { - VIR_WARN("Unable to find device %03d.%03d in list of used USB devices", - subsys->u.usb.bus, subsys->u.usb.device); - } + virHostdevManagerPtr hostdev_mgr; + + hostdev_mgr = virHostdevManagerGetDefault(); + if (hostdev_mgr != NULL) + virHostdevReAttachUsbHostdevs(hostdev_mgr, QEMU_DRIVER_NAME, + vm->def->name, &hostdev, 1); } static void @@ -2595,13 +2572,17 @@ qemuDomainRemoveSCSIHostDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainHostdevDefPtr hostdev) { + virHostdevManagerPtr hostdev_mgr; virDomainDeviceDef dev; dev.type = VIR_DOMAIN_DEVICE_HOSTDEV; dev.data.hostdev = hostdev; ignore_value(qemuRemoveSharedDevice(driver, &dev, vm->def->name)); - qemuDomainReAttachHostScsiDevices(driver, vm->def->name, &hostdev, 1); + hostdev_mgr = virHostdevManagerGetDefault(); + if (hostdev_mgr != NULL) + virHostdevReAttachScsiHostdevs(hostdev_mgr, QEMU_DRIVER_NAME, + vm->def->name, &hostdev, 1); } static void @@ -2641,14 +2622,12 @@ qemuDomainRemoveHostDevice(virQEMUDriverPtr driver, virDomainAuditHostdev(vm, hostdev, "detach", true); - qemuDomainHostdevNetConfigRestore(hostdev, cfg->stateDir); - switch ((enum virDomainHostdevSubsysType) hostdev->source.subsys.type) { case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: - qemuDomainRemovePCIHostDevice(driver, vm, hostdev); + qemuDomainRemovePCIHostDevice(vm, hostdev); break; case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: - qemuDomainRemoveUSBHostDevice(driver, vm, hostdev); + qemuDomainRemoveUSBHostDevice(vm, hostdev); break; case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI: qemuDomainRemoveSCSIHostDevice(driver, vm, hostdev); diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 5ea42c7..8cabb39 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -39,7 +39,6 @@ #include "qemu_capabilities.h" #include "qemu_monitor.h" #include "qemu_command.h" -#include "qemu_hostdev.h" #include "qemu_hotplug.h" #include "qemu_bridge_filter.h" #include "qemu_migration.h" @@ -66,6 +65,7 @@ #include "viratomic.h" #include "virnuma.h" #include "virstring.h" +#include "virhostdev.h" #define VIR_FROM_THIS VIR_FROM_QEMU @@ -3124,6 +3124,8 @@ qemuProcessReconnect(void *opaque) virQEMUDriverConfigPtr cfg; size_t i; int ret; + virHostdevManagerPtr hostdev_mgr; + unsigned int flags; memcpy(&oldjob, &data->oldjob, sizeof(oldjob)); @@ -3168,14 +3170,12 @@ qemuProcessReconnect(void *opaque) goto error; } - if (qemuUpdateActivePciHostdevs(driver, obj->def) < 0) { - goto error; - } - if (qemuUpdateActiveUsbHostdevs(driver, obj->def) < 0) - goto error; - - if (qemuUpdateActiveScsiHostdevs(driver, obj->def) < 0) + hostdev_mgr = virHostdevManagerGetDefault(); + flags = VIR_SP_PCI_HOSTDEV | VIR_SP_USB_HOSTDEV | VIR_SP_SCSI_HOSTDEV; + if (hostdev_mgr == NULL || + virHostdevUpdateActiveHostdevs(hostdev_mgr, QEMU_DRIVER_NAME, + obj->def, flags) < 0) goto error; if (qemuConnectCgroup(driver, obj) < 0) @@ -3607,6 +3607,7 @@ int qemuProcessStart(virConnectPtr conn, virQEMUDriverConfigPtr cfg; virCapsPtr caps = NULL; unsigned int hostdev_flags = 0; + virHostdevManagerPtr hostdev_mgr; VIR_DEBUG("vm=%p name=%s id=%d pid=%llu", vm, vm->def->name, vm->def->id, @@ -3709,7 +3710,11 @@ int qemuProcessStart(virConnectPtr conn, hostdev_flags |= VIR_STRICT_ACS_CHECK; if (!migrateFrom) hostdev_flags |= VIR_COLD_BOOT; - if (qemuPrepareHostDevices(driver, vm->def, hostdev_flags) < 0) + hostdev_flags |= VIR_SP_PCI_HOSTDEV | VIR_SP_USB_HOSTDEV | VIR_SP_SCSI_HOSTDEV; + hostdev_mgr = virHostdevManagerGetDefault(); + if (!hostdev_mgr || + virHostdevPrepareDomainHostdevs(hostdev_mgr, QEMU_DRIVER_NAME, + vm->def, hostdev_flags) < 0) goto cleanup; VIR_DEBUG("Preparing chr devices"); @@ -4248,6 +4253,8 @@ void qemuProcessStop(virQEMUDriverPtr driver, char *timestamp; char ebuf[1024]; virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); + unsigned int hostdev_flags = 0; + virHostdevManagerPtr hostdev_mgr; VIR_DEBUG("Shutting down vm=%p name=%s id=%d pid=%llu flags=%x", vm, vm->def->name, vm->def->id, @@ -4400,7 +4407,11 @@ void qemuProcessStop(virQEMUDriverPtr driver, ignore_value(qemuRemoveSharedDevice(driver, &dev, vm->def->name)); } - qemuDomainReAttachHostDevices(driver, vm->def); + hostdev_mgr = virHostdevManagerGetDefault(); + hostdev_flags |= VIR_SP_PCI_HOSTDEV | VIR_SP_USB_HOSTDEV | VIR_SP_SCSI_HOSTDEV; + if (hostdev_mgr != NULL) + virHostdevReAttachDomainHostdevs(hostdev_mgr, QEMU_DRIVER_NAME, + vm->def, hostdev_flags); def = vm->def; for (i = 0; i < def->nnets; i++) { -- 1.6.0.2

Change lxc driver to use hostdev common library instead of APIs in lxc_hostdev.[ch] Signed-off-by: Chunyan Liu <cyliu@suse.com> --- po/POTFILES.in | 1 - src/Makefile.am | 1 - src/lxc/lxc_conf.h | 4 - src/lxc/lxc_driver.c | 47 ++++--- src/lxc/lxc_hostdev.c | 416 ------------------------------------------------- src/lxc/lxc_hostdev.h | 43 ----- src/lxc/lxc_process.c | 24 +++- 7 files changed, 48 insertions(+), 488 deletions(-) delete mode 100644 src/lxc/lxc_hostdev.c delete mode 100644 src/lxc/lxc_hostdev.h diff --git a/po/POTFILES.in b/po/POTFILES.in index 9e71db3..94017c6 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -61,7 +61,6 @@ src/locking/lock_manager.c src/locking/sanlock_helper.c src/lxc/lxc_cgroup.c src/lxc/lxc_fuse.c -src/lxc/lxc_hostdev.c src/lxc/lxc_container.c src/lxc/lxc_conf.c src/lxc/lxc_controller.c diff --git a/src/Makefile.am b/src/Makefile.am index 5712055..b5839f3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -616,7 +616,6 @@ LXC_DRIVER_SOURCES = \ lxc/lxc_container.c lxc/lxc_container.h \ lxc/lxc_cgroup.c lxc/lxc_cgroup.h \ lxc/lxc_domain.c lxc/lxc_domain.h \ - lxc/lxc_hostdev.c lxc/lxc_hostdev.h \ lxc/lxc_monitor.c lxc/lxc_monitor.h \ lxc/lxc_process.c lxc/lxc_process.h \ lxc/lxc_fuse.c lxc/lxc_fuse.h \ diff --git a/src/lxc/lxc_conf.h b/src/lxc/lxc_conf.h index e04dcdd..5be159b 100644 --- a/src/lxc/lxc_conf.h +++ b/src/lxc/lxc_conf.h @@ -93,10 +93,6 @@ struct _virLXCDriver { /* Immutable pointer, self-locking APIs */ virDomainObjListPtr domains; - /* Immutable pointer. Requires lock to be held before - * calling APIs. */ - virUSBDeviceListPtr activeUsbHostdevs; - /* Immutable pointer, self-locking APIs */ virObjectEventStatePtr domainEventState; diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index 687046e..6f3a30f 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -70,6 +70,7 @@ #include "virstring.h" #include "viraccessapicheck.h" #include "viraccessapichecklxc.h" +#include "virhostdev.h" #define VIR_FROM_THIS VIR_FROM_LXC @@ -1525,9 +1526,6 @@ static int lxcStateInitialize(bool privileged, if (!(lxc_driver->securityManager = lxcSecurityInit(cfg))) goto cleanup; - if ((lxc_driver->activeUsbHostdevs = virUSBDeviceListNew()) == NULL) - goto cleanup; - if ((virLXCDriverGetCapabilities(lxc_driver, true)) == NULL) goto cleanup; @@ -1642,7 +1640,6 @@ static int lxcStateCleanup(void) virSysinfoDefFree(lxc_driver->hostsysinfo); - virObjectUnref(lxc_driver->activeUsbHostdevs); virObjectUnref(lxc_driver->caps); virObjectUnref(lxc_driver->securityManager); virObjectUnref(lxc_driver->xmlopt); @@ -3892,6 +3889,7 @@ lxcDomainAttachDeviceHostdevSubsysUSBLive(virLXCDriverPtr driver, mode_t mode; bool created = false; virUSBDevicePtr usb = NULL; + virHostdevManagerPtr hostdev_mgr; if (virDomainHostdevFind(vm->def, def, NULL) >= 0) { virReportError(VIR_ERR_OPERATION_FAILED, "%s", @@ -3899,6 +3897,14 @@ lxcDomainAttachDeviceHostdevSubsysUSBLive(virLXCDriverPtr driver, return -1; } + hostdev_mgr = virHostdevManagerGetDefault(); + if (hostdev_mgr == NULL || + virHostdevPrepareUsbHostdevs(hostdev_mgr, + LXC_DRIVER_NAME, + vm->def->name, + &def, 1, 0) < 0) + return -1; + if (virAsprintf(&vroot, "/proc/%llu/root", (unsigned long long)priv->initpid) < 0) goto cleanup; @@ -3974,6 +3980,11 @@ lxcDomainAttachDeviceHostdevSubsysUSBLive(virLXCDriverPtr driver, ret = 0; cleanup: + virHostdevReAttachUsbHostdevs(hostdev_mgr, + LXC_DRIVER_NAME, + vm->def->name, + &def, + 1); virDomainAuditHostdev(vm, def, "attach", ret == 0); if (ret < 0 && created) unlink(dstfile); @@ -4436,8 +4447,7 @@ cleanup: static int -lxcDomainDetachDeviceHostdevUSBLive(virLXCDriverPtr driver, - virDomainObjPtr vm, +lxcDomainDetachDeviceHostdevUSBLive(virDomainObjPtr vm, virDomainDeviceDefPtr dev) { virLXCDomainObjPrivatePtr priv = vm->privateData; @@ -4446,6 +4456,7 @@ lxcDomainDetachDeviceHostdevUSBLive(virLXCDriverPtr driver, char *dst = NULL; char *vroot = NULL; virUSBDevicePtr usb = NULL; + virHostdevManagerPtr hostdev_mgr; if ((idx = virDomainHostdevFind(vm->def, dev->data.hostdev, @@ -4490,9 +4501,10 @@ lxcDomainDetachDeviceHostdevUSBLive(virLXCDriverPtr driver, VIR_WARN("cannot deny device %s for domain %s", dst, vm->def->name); - virObjectLock(driver->activeUsbHostdevs); - virUSBDeviceListDel(driver->activeUsbHostdevs, usb); - virObjectUnlock(driver->activeUsbHostdevs); + hostdev_mgr = virHostdevManagerGetDefault(); + if (hostdev_mgr != NULL) + virHostdevReAttachPciHostdevs(hostdev_mgr, LXC_DRIVER_NAME, + vm->def->name, &def, 1); virDomainHostdevRemove(vm->def, idx); virDomainHostdevDefFree(def); @@ -4626,13 +4638,12 @@ cleanup: static int -lxcDomainDetachDeviceHostdevSubsysLive(virLXCDriverPtr driver, - virDomainObjPtr vm, +lxcDomainDetachDeviceHostdevSubsysLive(virDomainObjPtr vm, virDomainDeviceDefPtr dev) { switch (dev->data.hostdev->source.subsys.type) { case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: - return lxcDomainDetachDeviceHostdevUSBLive(driver, vm, dev); + return lxcDomainDetachDeviceHostdevUSBLive(vm, dev); default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, @@ -4664,8 +4675,7 @@ lxcDomainDetachDeviceHostdevCapsLive(virDomainObjPtr vm, static int -lxcDomainDetachDeviceHostdevLive(virLXCDriverPtr driver, - virDomainObjPtr vm, +lxcDomainDetachDeviceHostdevLive(virDomainObjPtr vm, virDomainDeviceDefPtr dev) { virLXCDomainObjPrivatePtr priv = vm->privateData; @@ -4678,7 +4688,7 @@ lxcDomainDetachDeviceHostdevLive(virLXCDriverPtr driver, switch (dev->data.hostdev->mode) { case VIR_DOMAIN_HOSTDEV_MODE_SUBSYS: - return lxcDomainDetachDeviceHostdevSubsysLive(driver, vm, dev); + return lxcDomainDetachDeviceHostdevSubsysLive(vm, dev); case VIR_DOMAIN_HOSTDEV_MODE_CAPABILITIES: return lxcDomainDetachDeviceHostdevCapsLive(vm, dev); @@ -4693,8 +4703,7 @@ lxcDomainDetachDeviceHostdevLive(virLXCDriverPtr driver, static int -lxcDomainDetachDeviceLive(virLXCDriverPtr driver, - virDomainObjPtr vm, +lxcDomainDetachDeviceLive(virDomainObjPtr vm, virDomainDeviceDefPtr dev) { int ret = -1; @@ -4709,7 +4718,7 @@ lxcDomainDetachDeviceLive(virLXCDriverPtr driver, break; case VIR_DOMAIN_DEVICE_HOSTDEV: - ret = lxcDomainDetachDeviceHostdevLive(driver, vm, dev); + ret = lxcDomainDetachDeviceHostdevLive(vm, dev); break; default: @@ -5042,7 +5051,7 @@ static int lxcDomainDetachDeviceFlags(virDomainPtr dom, if (virDomainDefCompatibleDevice(vm->def, dev_copy) < 0) goto cleanup; - if ((ret = lxcDomainDetachDeviceLive(driver, vm, dev_copy)) < 0) + if ((ret = lxcDomainDetachDeviceLive(vm, dev_copy)) < 0) goto cleanup; /* * update domain status forcibly because the domain status may be diff --git a/src/lxc/lxc_hostdev.c b/src/lxc/lxc_hostdev.c deleted file mode 100644 index 77ce965..0000000 --- a/src/lxc/lxc_hostdev.c +++ /dev/null @@ -1,416 +0,0 @@ -/* - * virLXC_hostdev.c: VIRLXC hostdev management - * - * Copyright (C) 2006-2007, 2009-2012 Red Hat, Inc. - * Copyright (C) 2006 Daniel P. Berrange - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see - * <http://www.gnu.org/licenses/>. - * - * Author: Daniel P. Berrange <berrange@redhat.com> - */ - -#include <config.h> - -#include "lxc_hostdev.h" -#include "viralloc.h" -#include "virlog.h" -#include "virerror.h" - -#define VIR_FROM_THIS VIR_FROM_LXC - -int -virLXCUpdateActiveUsbHostdevs(virLXCDriverPtr driver, - virDomainDefPtr def) -{ - virDomainHostdevDefPtr hostdev = NULL; - size_t i; - - if (!def->nhostdevs) - return 0; - - for (i = 0; i < def->nhostdevs; i++) { - virUSBDevicePtr usb = NULL; - hostdev = def->hostdevs[i]; - - if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) - continue; - if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) - continue; - - usb = virUSBDeviceNew(hostdev->source.subsys.u.usb.bus, - hostdev->source.subsys.u.usb.device, - NULL); - if (!usb) { - VIR_WARN("Unable to reattach USB device %03d.%03d on domain %s", - hostdev->source.subsys.u.usb.bus, - hostdev->source.subsys.u.usb.device, - def->name); - continue; - } - - virUSBDeviceSetUsedBy(usb, "QEMU", def->name); - - virObjectLock(driver->activeUsbHostdevs); - if (virUSBDeviceListAdd(driver->activeUsbHostdevs, usb) < 0) { - virObjectUnlock(driver->activeUsbHostdevs); - virUSBDeviceFree(usb); - return -1; - } - virObjectUnlock(driver->activeUsbHostdevs); - } - - return 0; -} - - -int -virLXCPrepareHostdevUSBDevices(virLXCDriverPtr driver, - const char *name, - virUSBDeviceList *list) -{ - size_t i, j; - unsigned int count; - virUSBDevicePtr tmp; - - count = virUSBDeviceListCount(list); - - virObjectLock(driver->activeUsbHostdevs); - for (i = 0; i < count; i++) { - virUSBDevicePtr usb = virUSBDeviceListGet(list, i); - if ((tmp = virUSBDeviceListFind(driver->activeUsbHostdevs, usb))) { - const char *other_name = NULL; - const char *other_drvname = NULL; - virUSBDeviceGetUsedBy(tmp, &other_drvname, &other_name); - - if (other_name) - virReportError(VIR_ERR_OPERATION_INVALID, - _("USB device %s is in use by domain %s"), - virUSBDeviceGetName(tmp), other_name); - else - virReportError(VIR_ERR_OPERATION_INVALID, - _("USB device %s is already in use"), - virUSBDeviceGetName(tmp)); - goto error; - } - - virUSBDeviceSetUsedBy(usb, "QEMU", name); - VIR_DEBUG("Adding %03d.%03d dom=%s to activeUsbHostdevs", - virUSBDeviceGetBus(usb), virUSBDeviceGetDevno(usb), name); - /* - * The caller is responsible to steal these usb devices - * from the virUSBDeviceList that passed in on success, - * perform rollback on failure. - */ - if (virUSBDeviceListAdd(driver->activeUsbHostdevs, usb) < 0) - goto error; - } - virObjectUnlock(driver->activeUsbHostdevs); - return 0; - -error: - for (j = 0; j < i; j++) { - tmp = virUSBDeviceListGet(list, i); - virUSBDeviceListSteal(driver->activeUsbHostdevs, tmp); - } - virObjectUnlock(driver->activeUsbHostdevs); - return -1; -} - -int -virLXCFindHostdevUSBDevice(virDomainHostdevDefPtr hostdev, - bool mandatory, - virUSBDevicePtr *usb) -{ - unsigned vendor = hostdev->source.subsys.u.usb.vendor; - unsigned product = hostdev->source.subsys.u.usb.product; - unsigned bus = hostdev->source.subsys.u.usb.bus; - unsigned device = hostdev->source.subsys.u.usb.device; - bool autoAddress = hostdev->source.subsys.u.usb.autoAddress; - int rc; - - *usb = NULL; - - if (vendor && bus) { - rc = virUSBDeviceFind(vendor, product, bus, device, - NULL, - autoAddress ? false : mandatory, - usb); - if (rc < 0) { - return -1; - } else if (!autoAddress) { - goto out; - } else { - VIR_INFO("USB device %x:%x could not be found at previous" - " address (bus:%u device:%u)", - vendor, product, bus, device); - } - } - - /* When vendor is specified, its USB address is either unspecified or the - * device could not be found at the USB device where it had been - * automatically found before. - */ - if (vendor) { - virUSBDeviceList *devs; - - rc = virUSBDeviceFindByVendor(vendor, product, - NULL, - mandatory, &devs); - if (rc < 0) - return -1; - - if (rc == 1) { - *usb = virUSBDeviceListGet(devs, 0); - virUSBDeviceListSteal(devs, *usb); - } - virObjectUnref(devs); - - if (rc == 0) { - goto out; - } else if (rc > 1) { - if (autoAddress) { - virReportError(VIR_ERR_OPERATION_FAILED, - _("Multiple USB devices for %x:%x were found," - " but none of them is at bus:%u device:%u"), - vendor, product, bus, device); - } else { - virReportError(VIR_ERR_OPERATION_FAILED, - _("Multiple USB devices for %x:%x, " - "use <address> to specify one"), - vendor, product); - } - return -1; - } - - hostdev->source.subsys.u.usb.bus = virUSBDeviceGetBus(*usb); - hostdev->source.subsys.u.usb.device = virUSBDeviceGetDevno(*usb); - hostdev->source.subsys.u.usb.autoAddress = true; - - if (autoAddress) { - VIR_INFO("USB device %x:%x found at bus:%u device:%u (moved" - " from bus:%u device:%u)", - vendor, product, - hostdev->source.subsys.u.usb.bus, - hostdev->source.subsys.u.usb.device, - bus, device); - } - } else if (!vendor && bus) { - if (virUSBDeviceFindByBus(bus, device, - NULL, - mandatory, usb) < 0) - return -1; - } - -out: - if (!*usb) - hostdev->missing = true; - return 0; -} - -static int -virLXCPrepareHostUSBDevices(virLXCDriverPtr driver, - virDomainDefPtr def) -{ - size_t i; - int ret = -1; - virUSBDeviceList *list; - virUSBDevicePtr tmp; - virDomainHostdevDefPtr *hostdevs = def->hostdevs; - int nhostdevs = def->nhostdevs; - - /* To prevent situation where USB device is assigned to two domains - * we need to keep a list of currently assigned USB devices. - * This is done in several loops which cannot be joined into one big - * loop. See virLXCPrepareHostdevPCIDevices() - */ - if (!(list = virUSBDeviceListNew())) - goto cleanup; - - /* Loop 1: build temporary list - */ - for (i = 0; i < nhostdevs; i++) { - virDomainHostdevDefPtr hostdev = hostdevs[i]; - bool required = true; - virUSBDevicePtr usb; - - if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) - continue; - if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) - continue; - - if (hostdev->startupPolicy == VIR_DOMAIN_STARTUP_POLICY_OPTIONAL) - required = false; - - if (virLXCFindHostdevUSBDevice(hostdev, required, &usb) < 0) - goto cleanup; - - if (usb && virUSBDeviceListAdd(list, usb) < 0) { - virUSBDeviceFree(usb); - goto cleanup; - } - } - - /* Mark devices in temporary list as used by @name - * and add them do driver list. However, if something goes - * wrong, perform rollback. - */ - if (virLXCPrepareHostdevUSBDevices(driver, def->name, list) < 0) - goto cleanup; - - /* Loop 2: Temporary list was successfully merged with - * driver list, so steal all items to avoid freeing them - * in cleanup label. - */ - while (virUSBDeviceListCount(list) > 0) { - tmp = virUSBDeviceListGet(list, 0); - virUSBDeviceListSteal(list, tmp); - } - - ret = 0; - -cleanup: - virObjectUnref(list); - return ret; -} - - -int virLXCPrepareHostDevices(virLXCDriverPtr driver, - virDomainDefPtr def) -{ - size_t i; - - if (!def->nhostdevs) - return 0; - - /* Sanity check for supported configurations only */ - for (i = 0; i < def->nhostdevs; i++) { - virDomainHostdevDefPtr dev = def->hostdevs[i]; - - switch (dev->mode) { - case VIR_DOMAIN_HOSTDEV_MODE_SUBSYS: - switch (dev->source.subsys.type) { - case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: - break; - default: - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("Unsupported hostdev type %s"), - virDomainHostdevSubsysTypeToString(dev->source.subsys.type)); - return -1; - } - break; - - case VIR_DOMAIN_HOSTDEV_MODE_CAPABILITIES: - switch (dev->source.subsys.type) { - case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_STORAGE: - case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_MISC: - case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_NET: - break; - default: - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("Unsupported hostdev type %s"), - virDomainHostdevSubsysTypeToString(dev->source.subsys.type)); - return -1; - } - break; - - - default: - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("Unsupported hostdev mode %s"), - virDomainHostdevModeTypeToString(dev->mode)); - return -1; - } - } - - if (virLXCPrepareHostUSBDevices(driver, def) < 0) - return -1; - - return 0; -} - - -static void -virLXCDomainReAttachHostUsbDevices(virLXCDriverPtr driver, - const char *name, - virDomainHostdevDefPtr *hostdevs, - int nhostdevs) -{ - size_t i; - - virObjectLock(driver->activeUsbHostdevs); - for (i = 0; i < nhostdevs; i++) { - virDomainHostdevDefPtr hostdev = hostdevs[i]; - virUSBDevicePtr usb, tmp; - const char *used_by = NULL; - const char *used_by_drvname = NULL; - - if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) - continue; - if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) - continue; - if (hostdev->missing) - continue; - - usb = virUSBDeviceNew(hostdev->source.subsys.u.usb.bus, - hostdev->source.subsys.u.usb.device, - NULL); - - if (!usb) { - VIR_WARN("Unable to reattach USB device %03d.%03d on domain %s", - hostdev->source.subsys.u.usb.bus, - hostdev->source.subsys.u.usb.device, - name); - continue; - } - - /* Delete only those USB devices which belongs - * to domain @name because virLXCProcessStart() might - * have failed because USB device is already taken. - * Therefore we want to steal only those devices from - * the list which were taken by @name */ - - tmp = virUSBDeviceListFind(driver->activeUsbHostdevs, usb); - virUSBDeviceFree(usb); - - if (!tmp) { - VIR_WARN("Unable to find device %03d.%03d " - "in list of active USB devices", - hostdev->source.subsys.u.usb.bus, - hostdev->source.subsys.u.usb.device); - continue; - } - - virUSBDeviceGetUsedBy(tmp, &used_by_drvname, &used_by); - if (STREQ_NULLABLE(used_by, name)) { - VIR_DEBUG("Removing %03d.%03d dom=%s from activeUsbHostdevs", - hostdev->source.subsys.u.usb.bus, - hostdev->source.subsys.u.usb.device, - name); - - virUSBDeviceListDel(driver->activeUsbHostdevs, tmp); - } - } - virObjectUnlock(driver->activeUsbHostdevs); -} - -void virLXCDomainReAttachHostDevices(virLXCDriverPtr driver, - virDomainDefPtr def) -{ - if (!def->nhostdevs) - return; - - virLXCDomainReAttachHostUsbDevices(driver, def->name, def->hostdevs, - def->nhostdevs); -} diff --git a/src/lxc/lxc_hostdev.h b/src/lxc/lxc_hostdev.h deleted file mode 100644 index 41bb178..0000000 --- a/src/lxc/lxc_hostdev.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * virLXC_hostdev.h: VIRLXC hostdev management - * - * Copyright (C) 2006-2007, 2009-2010 Red Hat, Inc. - * Copyright (C) 2006 Daniel P. Berrange - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see - * <http://www.gnu.org/licenses/>. - * - * Author: Daniel P. Berrange <berrange@redhat.com> - */ - -#ifndef __LXC_HOSTDEV_H__ -# define __LXC_HOSTDEV_H__ - -# include "lxc_conf.h" -# include "domain_conf.h" - -int virLXCUpdateActiveUsbHostdevs(virLXCDriverPtr driver, - virDomainDefPtr def); -int virLXCFindHostdevUSBDevice(virDomainHostdevDefPtr hostdev, - bool mandatory, - virUSBDevicePtr *usb); -int virLXCPrepareHostdevUSBDevices(virLXCDriverPtr driver, - const char *name, - virUSBDeviceListPtr list); -int virLXCPrepareHostDevices(virLXCDriverPtr driver, - virDomainDefPtr def); -void virLXCDomainReAttachHostDevices(virLXCDriverPtr driver, - virDomainDefPtr def); - -#endif /* __LXC_HOSTDEV_H__ */ diff --git a/src/lxc/lxc_process.c b/src/lxc/lxc_process.c index ed729f6..0afbddd 100644 --- a/src/lxc/lxc_process.c +++ b/src/lxc/lxc_process.c @@ -45,7 +45,7 @@ #include "virerror.h" #include "virlog.h" #include "vircommand.h" -#include "lxc_hostdev.h" +#include "virhostdev.h" #include "virhook.h" #include "virstring.h" #include "viratomic.h" @@ -150,6 +150,7 @@ static void virLXCProcessCleanup(virLXCDriverPtr driver, virLXCDomainObjPrivatePtr priv = vm->privateData; virNetDevVPortProfilePtr vport = NULL; virLXCDriverConfigPtr cfg = virLXCDriverGetConfig(driver); + virHostdevManagerPtr hostdev_mgr; VIR_DEBUG("Stopping VM name=%s pid=%d reason=%d", vm->def->name, (int)vm->pid, (int)reason); @@ -185,7 +186,12 @@ static void virLXCProcessCleanup(virLXCDriverPtr driver, if (virAtomicIntDecAndTest(&driver->nactive) && driver->inhibitCallback) driver->inhibitCallback(false, driver->inhibitOpaque); - virLXCDomainReAttachHostDevices(driver, vm->def); + hostdev_mgr = virHostdevManagerGetDefault(); + if (hostdev_mgr != NULL) + virHostdevReAttachDomainHostdevs(hostdev_mgr, + LXC_DRIVER_NAME, + vm->def, + VIR_SP_USB_HOSTDEV); for (i = 0; i < vm->def->nnets; i++) { virDomainNetDefPtr iface = vm->def->nets[i]; @@ -997,6 +1003,7 @@ int virLXCProcessStart(virConnectPtr conn, virLXCDriverConfigPtr cfg = virLXCDriverGetConfig(driver); virCgroupPtr selfcgroup; int status; + virHostdevManagerPtr hostdev_mgr; if (virCgroupNewSelf(&selfcgroup) < 0) return -1; @@ -1082,7 +1089,12 @@ int virLXCProcessStart(virConnectPtr conn, /* Must be run before security labelling */ VIR_DEBUG("Preparing host devices"); - if (virLXCPrepareHostDevices(driver, vm->def) < 0) + hostdev_mgr = virHostdevManagerGetDefault(); + if (hostdev_mgr == NULL || + virHostdevPrepareDomainHostdevs(hostdev_mgr, + LXC_DRIVER_NAME, + vm->def, + VIR_SP_USB_HOSTDEV) < 0) goto cleanup; /* Here we open all the PTYs we need on the host OS side. @@ -1435,6 +1447,7 @@ virLXCProcessReconnectDomain(virDomainObjPtr vm, { virLXCDriverPtr driver = opaque; virLXCDomainObjPrivatePtr priv; + virHostdevManagerPtr hostdev_mgr; int ret = -1; virObjectLock(vm); @@ -1467,7 +1480,10 @@ virLXCProcessReconnectDomain(virDomainObjPtr vm, goto error; } - if (virLXCUpdateActiveUsbHostdevs(driver, vm->def) < 0) + hostdev_mgr = virHostdevManagerGetDefault(); + if (hostdev_mgr == NULL || + virHostdevUpdateActiveHostdevs(hostdev_mgr, LXC_DRIVER_NAME, + vm->def, VIR_SP_USB_HOSTDEV) < 0) goto error; if (virSecurityManagerReserveLabel(driver->securityManager, -- 1.6.0.2

Add VIR_DOMAIN_HOSTDEV_PCI_BACKEND_XEN. For legacy xen, it will use "pciback" as stub driver. Signed-off-by: Chunyan Liu <cyliu@suse.com> --- docs/schemas/domaincommon.rng | 1 + src/conf/domain_conf.c | 3 ++- src/conf/domain_conf.h | 1 + src/libxl/libxl_domain.c | 9 +++++++++ src/qemu/qemu_command.c | 3 +-- src/qemu/qemu_hotplug.c | 4 +--- src/util/virhostdev.c | 12 ++++++++++++ 7 files changed, 27 insertions(+), 6 deletions(-) diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index c1efcd2..ca92271 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -3420,6 +3420,7 @@ <choice> <value>kvm</value> <value>vfio</value> + <value>xen</value> </choice> </attribute> <empty/> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index f6065ed..411ae55 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -624,7 +624,8 @@ VIR_ENUM_IMPL(virDomainHostdevSubsysPciBackend, VIR_DOMAIN_HOSTDEV_PCI_BACKEND_TYPE_LAST, "default", "kvm", - "vfio") + "vfio", + "xen") VIR_ENUM_IMPL(virDomainHostdevCaps, VIR_DOMAIN_HOSTDEV_CAPS_TYPE_LAST, "storage", diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 4895e81..407e6e6 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -414,6 +414,7 @@ typedef enum { VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT, /* detect automaticaly, prefer VFIO */ VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM, /* force legacy kvm style */ VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO, /* force vfio */ + VIR_DOMAIN_HOSTDEV_PCI_BACKEND_XEN, /* force legacy xen style, use pciback */ VIR_DOMAIN_HOSTDEV_PCI_BACKEND_TYPE_LAST } virDomainHostdevSubsysPciBackendType; diff --git a/src/libxl/libxl_domain.c b/src/libxl/libxl_domain.c index eb2e50e..a88bb20 100644 --- a/src/libxl/libxl_domain.c +++ b/src/libxl/libxl_domain.c @@ -350,6 +350,15 @@ libxlDomainDeviceDefPostParse(virDomainDeviceDefPtr dev, STRNEQ(def->os.type, "hvm")) dev->data.chr->targetType = VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_XEN; + if (dev->type == VIR_DOMAIN_DEVICE_HOSTDEV) { + virDomainHostdevDefPtr hostdev = dev->data.hostdev; + + if (hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS && + hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI && + hostdev->source.subsys.u.pci.backend == VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT) + hostdev->source.subsys.u.pci.backend = VIR_DOMAIN_HOSTDEV_PCI_BACKEND_XEN; + } + return 0; } diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index ccdd50f..69c9cb1 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -5537,8 +5537,7 @@ qemuBuildPCIHostdevDevStr(virDomainDefPtr def, virBufferAddLit(&buf, "vfio-pci"); break; - case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT: - case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_TYPE_LAST: + default: virReportError(VIR_ERR_INTERNAL_ERROR, _("invalid PCI passthrough type '%s'"), virDomainHostdevSubsysPciBackendTypeToString(backend)); diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index bb9b3ff..666e98e 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -1196,9 +1196,7 @@ qemuDomainAttachHostPciDevice(virQEMUDriverPtr driver, virProcessSetMaxMemLock(vm->pid, memKB * 1024); break; - case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT: - case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM: - case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_TYPE_LAST: + default: break; } diff --git a/src/util/virhostdev.c b/src/util/virhostdev.c index 8d75a77..6fa32d4 100644 --- a/src/util/virhostdev.c +++ b/src/util/virhostdev.c @@ -373,6 +373,13 @@ virHostdevGetPciHostDeviceList(virDomainHostdevDefPtr *hostdevs, int nhostdevs) } break; + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_XEN : + if (virPCIDeviceSetStubDriver(dev, "pciback") < 0) { + virObjectUnref(list); + return NULL; + } + break; + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM : case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT: if (virPCIDeviceSetStubDriver(dev, "pci-stub") < 0) { @@ -1264,6 +1271,11 @@ virHostdevUpdateActivePciHostdevs(virHostdevManagerPtr mgr, goto cleanup; break; + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_XEN : + if (virPCIDeviceSetStubDriver(dev, "pciback") < 0) + goto cleanup; + break; + case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM : case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT: if (virPCIDeviceSetStubDriver(dev, "pci-stub") < 0) -- 1.6.0.2

Add pci passthrough to libxl driver, support attach-device, detach-device and start a vm with pci hostdev specified. Signed-off-by: Chunyan Liu <cyliu@suse.com> --- src/libxl/libxl_conf.c | 63 +++++++ src/libxl/libxl_conf.h | 4 + src/libxl/libxl_driver.c | 448 +++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 514 insertions(+), 1 deletions(-) diff --git a/src/libxl/libxl_conf.c b/src/libxl/libxl_conf.c index 4cefadf..54f9f9c 100644 --- a/src/libxl/libxl_conf.c +++ b/src/libxl/libxl_conf.c @@ -1141,6 +1141,66 @@ libxlDriverConfigGet(libxlDriverPrivatePtr driver) return cfg; } +int +libxlMakePci(virDomainHostdevDefPtr hostdev, libxl_device_pci *pcidev) +{ + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) + return -1; + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) + return -1; + + pcidev->domain = hostdev->source.subsys.u.pci.addr.domain; + pcidev->bus = hostdev->source.subsys.u.pci.addr.bus; + pcidev->dev = hostdev->source.subsys.u.pci.addr.slot; + pcidev->func = hostdev->source.subsys.u.pci.addr.function; + + return 0; +} + +static int +libxlMakePciList(virDomainDefPtr def, libxl_domain_config *d_config) +{ + virDomainHostdevDefPtr *l_hostdevs = def->hostdevs; + size_t nhostdevs = def->nhostdevs; + size_t npcidevs = 0; + libxl_device_pci *x_pcidevs; + size_t i, j; + + if (nhostdevs == 0) + return 0; + + if (VIR_ALLOC_N(x_pcidevs, nhostdevs) < 0) + return -1; + + for (i = 0, j = 0; i < nhostdevs; i++) { + if (l_hostdevs[i]->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) + continue; + if (l_hostdevs[i]->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) + continue; + + libxl_device_pci_init(&x_pcidevs[j]); + + if (libxlMakePci(l_hostdevs[i], &x_pcidevs[j]) < 0) + goto error; + + npcidevs++; + j++; + } + + VIR_SHRINK_N(x_pcidevs, nhostdevs, nhostdevs - npcidevs); + d_config->pcidevs = x_pcidevs; + d_config->num_pcidevs = npcidevs; + + return 0; + +error: + for (i = 0; i < npcidevs; i++) + libxl_device_pci_dispose(&x_pcidevs[i]); + + VIR_FREE(x_pcidevs); + return -1; +} + virCapsPtr libxlMakeCapabilities(libxl_ctx *ctx) { @@ -1189,6 +1249,9 @@ libxlBuildDomainConfig(libxlDriverPrivatePtr driver, if (libxlMakeVfbList(driver, def, d_config) < 0) return -1; + if (libxlMakePciList(def, d_config) < 0) + return -1; + d_config->on_reboot = def->onReboot; d_config->on_poweroff = def->onPoweroff; d_config->on_crash = def->onCrash; diff --git a/src/libxl/libxl_conf.h b/src/libxl/libxl_conf.h index ca7bc7d..4491859 100644 --- a/src/libxl/libxl_conf.h +++ b/src/libxl/libxl_conf.h @@ -39,6 +39,7 @@ # include "virchrdev.h" +# define LIBXL_DRIVER_NAME "xenlight" # define LIBXL_VNC_PORT_MIN 5900 # define LIBXL_VNC_PORT_MAX 65535 @@ -148,6 +149,9 @@ libxlMakeVfb(libxlDriverPrivatePtr driver, virDomainGraphicsDefPtr l_vfb, libxl_device_vfb *x_vfb); int +libxlMakePci(virDomainHostdevDefPtr hostdev, libxl_device_pci *pcidev); + +int libxlBuildDomainConfig(libxlDriverPrivatePtr driver, virDomainObjPtr vm, libxl_domain_config *d_config); diff --git a/src/libxl/libxl_driver.c b/src/libxl/libxl_driver.c index 8e4242a..999dee2 100644 --- a/src/libxl/libxl_driver.c +++ b/src/libxl/libxl_driver.c @@ -52,6 +52,7 @@ #include "virsysinfo.h" #include "viraccessapicheck.h" #include "viratomic.h" +#include "virhostdev.h" #define VIR_FROM_THIS VIR_FROM_LIBXL @@ -327,6 +328,7 @@ libxlVmReap(libxlDriverPrivatePtr driver, virDomainShutoffReason reason) { libxlDomainObjPrivatePtr priv = vm->privateData; + virHostdevManagerPtr hostdev_mgr; if (libxl_domain_destroy(priv->ctx, vm->def->id, NULL) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, @@ -334,6 +336,11 @@ libxlVmReap(libxlDriverPrivatePtr driver, return -1; } + hostdev_mgr = virHostdevManagerGetDefault(); + if (hostdev_mgr != NULL) + virHostdevReAttachDomainHostdevs(hostdev_mgr, LIBXL_DRIVER_NAME, + vm->def, VIR_SP_PCI_HOSTDEV); + libxlVmCleanup(driver, vm, reason); return 0; } @@ -603,6 +610,7 @@ libxlVmStart(libxlDriverPrivatePtr driver, virDomainObjPtr vm, #ifdef LIBXL_HAVE_DOMAIN_CREATE_RESTORE_PARAMS libxl_domain_restore_params params; #endif + virHostdevManagerPtr hostdev_mgr; if (libxlDomainObjPrivateInitCtx(vm) < 0) goto error; @@ -661,6 +669,13 @@ libxlVmStart(libxlDriverPrivatePtr driver, virDomainObjPtr vm, goto error; } + VIR_DEBUG("Preparing host PCI devices"); + hostdev_mgr = virHostdevManagerGetDefault(); + if (hostdev_mgr == NULL || + virHostdevPrepareDomainHostdevs(hostdev_mgr, LIBXL_DRIVER_NAME, + vm->def, VIR_SP_PCI_HOSTDEV) < 0) + goto error; + /* use as synchronous operations => ao_how = NULL and no intermediate reports => ao_progress = NULL */ if (restore_fd < 0) { @@ -763,6 +778,7 @@ libxlReconnectDomain(virDomainObjPtr vm, libxl_dominfo d_info; int len; uint8_t *data = NULL; + virHostdevManagerPtr hostdev_mgr; virObjectLock(vm); @@ -786,6 +802,14 @@ libxlReconnectDomain(virDomainObjPtr vm, /* Update domid in case it changed (e.g. reboot) while we were gone? */ vm->def->id = d_info.domid; + + /* Update hostdev state */ + hostdev_mgr = virHostdevManagerGetDefault(); + if (hostdev_mgr == NULL || + virHostdevUpdateActiveHostdevs(hostdev_mgr, LIBXL_DRIVER_NAME, + vm->def, VIR_SP_PCI_HOSTDEV) < 0) + goto out; + virDomainObjSetState(vm, VIR_DOMAIN_RUNNING, VIR_DOMAIN_RUNNING_UNKNOWN); if (virAtomicIntInc(&driver->nactive) == 1 && driver->inhibitCallback) @@ -3074,6 +3098,95 @@ cleanup: } static int +libxlDomainAttachHostPciDevice(libxlDomainObjPrivatePtr priv, + virDomainObjPtr vm, + virDomainHostdevDefPtr hostdev) +{ + libxl_device_pci pcidev; + virDomainHostdevDefPtr found; + virHostdevManagerPtr hostdev_mgr; + + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) + return -1; + + if (virDomainHostdevFind(vm->def, hostdev, &found) >= 0) { + virReportError(VIR_ERR_OPERATION_FAILED, + _("target pci device %.4x:%.2x:%.2x.%.1x already exists"), + hostdev->source.subsys.u.pci.addr.domain, + hostdev->source.subsys.u.pci.addr.bus, + hostdev->source.subsys.u.pci.addr.slot, + hostdev->source.subsys.u.pci.addr.function); + return -1; + } + + if (VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs + 1) < 0) + return -1; + + hostdev_mgr = virHostdevManagerGetDefault(); + if (hostdev_mgr == NULL || + virHostdevPreparePciHostdevs(hostdev_mgr, LIBXL_DRIVER_NAME, + vm->def->name, vm->def->uuid, + &hostdev, 1, 0) < 0) + goto cleanup; + + if (libxlMakePci(hostdev, &pcidev) < 0) + goto reattach_hostdev; + + if (libxl_device_pci_add(priv->ctx, vm->def->id, &pcidev, 0) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("libxenlight failed to attach pci device %.4x:%.2x:%.2x.%.1x"), + hostdev->source.subsys.u.pci.addr.domain, + hostdev->source.subsys.u.pci.addr.bus, + hostdev->source.subsys.u.pci.addr.slot, + hostdev->source.subsys.u.pci.addr.function); + goto reattach_hostdev; + } + + vm->def->hostdevs[vm->def->nhostdevs++] = hostdev; + return 0; + +reattach_hostdev: + virHostdevReAttachPciHostdevs(hostdev_mgr, LIBXL_DRIVER_NAME, + vm->def->name, &hostdev, 1); + +cleanup: + return -1; +} + +static int +libxlDomainAttachHostDevice(libxlDomainObjPrivatePtr priv, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev) +{ + virDomainHostdevDefPtr hostdev = dev->data.hostdev; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("hostdev mode '%s' not supported"), + virDomainHostdevModeTypeToString(hostdev->mode)); + return -1; + } + + switch (hostdev->source.subsys.type) { + case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: + if (libxlDomainAttachHostPciDevice(priv, vm, hostdev) < 0) + goto error; + break; + + default: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("hostdev subsys type '%s' not supported"), + virDomainHostdevSubsysTypeToString(hostdev->source.subsys.type)); + goto error; + } + + return 0; + +error: + return -1; +} + +static int libxlDomainDetachDeviceDiskLive(libxlDomainObjPrivatePtr priv, virDomainObjPtr vm, virDomainDeviceDefPtr dev) { @@ -3140,6 +3253,12 @@ libxlDomainAttachDeviceLive(libxlDomainObjPrivatePtr priv, virDomainObjPtr vm, dev->data.disk = NULL; break; + case VIR_DOMAIN_DEVICE_HOSTDEV: + ret = libxlDomainAttachHostDevice(priv, vm, dev); + if (!ret) + dev->data.hostdev = NULL; + break; + default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("device type '%s' cannot be attached"), @@ -3154,6 +3273,8 @@ static int libxlDomainAttachDeviceConfig(virDomainDefPtr vmdef, virDomainDeviceDefPtr dev) { virDomainDiskDefPtr disk; + virDomainHostdevDefPtr hostdev; + virDomainHostdevDefPtr found; switch (dev->type) { case VIR_DOMAIN_DEVICE_DISK: @@ -3168,6 +3289,25 @@ libxlDomainAttachDeviceConfig(virDomainDefPtr vmdef, virDomainDeviceDefPtr dev) /* vmdef has the pointer. Generic codes for vmdef will do all jobs */ dev->data.disk = NULL; break; + case VIR_DOMAIN_DEVICE_HOSTDEV: + hostdev = dev->data.hostdev; + + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) + return -1; + + if (virDomainHostdevFind(vmdef, hostdev, &found) >= 0) { + virReportError(VIR_ERR_OPERATION_FAILED, + _("target pci device %.4x:%.2x:%.2x.%.1x\ + already exists"), + hostdev->source.subsys.u.pci.addr.domain, + hostdev->source.subsys.u.pci.addr.bus, + hostdev->source.subsys.u.pci.addr.slot, + hostdev->source.subsys.u.pci.addr.function); + return -1; + } + + virDomainHostdevInsert(vmdef, hostdev); + break; default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", @@ -3178,6 +3318,125 @@ libxlDomainAttachDeviceConfig(virDomainDefPtr vmdef, virDomainDeviceDefPtr dev) } static int +libxlComparePCIDevice(virDomainDefPtr def ATTRIBUTE_UNUSED, + virDomainDeviceDefPtr device ATTRIBUTE_UNUSED, + virDomainDeviceInfoPtr info1, + void *opaque) +{ + virDomainDeviceInfoPtr info2 = opaque; + + if (info1->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI || + info2->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) + return 0; + + if (info1->addr.pci.domain == info2->addr.pci.domain && + info1->addr.pci.bus == info2->addr.pci.bus && + info1->addr.pci.slot == info2->addr.pci.slot && + info1->addr.pci.function != info2->addr.pci.function) + return -1; + return 0; +} + +static bool +libxlIsMultiFunctionDevice(virDomainDefPtr def, + virDomainDeviceInfoPtr dev) +{ + if (virDomainDeviceInfoIterate(def, libxlComparePCIDevice, dev) < 0) + return true; + return false; +} + +static int +libxlDomainDetachHostPciDevice(libxlDomainObjPrivatePtr priv, + virDomainObjPtr vm, + virDomainHostdevDefPtr hostdev) +{ + virDomainHostdevSubsysPtr subsys = &hostdev->source.subsys; + libxl_device_pci pcidev; + virDomainHostdevDefPtr detach; + int idx; + virHostdevManagerPtr hostdev_mgr; + + if (subsys->type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) + return -1; + + idx = virDomainHostdevFind(vm->def, hostdev, &detach); + if (idx < 0) { + virReportError(VIR_ERR_OPERATION_FAILED, + _("host pci device %.4x:%.2x:%.2x.%.1x not found"), + subsys->u.pci.addr.domain, subsys->u.pci.addr.bus, + subsys->u.pci.addr.slot, subsys->u.pci.addr.function); + return -1; + } + + if (libxlIsMultiFunctionDevice(vm->def, detach->info)) { + virReportError(VIR_ERR_OPERATION_FAILED, + _("cannot hot unplug multifunction PCI device: %.4x:%.2x:%.2x.%.1x"), + subsys->u.pci.addr.domain, subsys->u.pci.addr.bus, + subsys->u.pci.addr.slot, subsys->u.pci.addr.function); + goto cleanup; + } + + + libxl_device_pci_init(&pcidev); + + if (libxlMakePci(detach, &pcidev) < 0) + goto cleanup; + + if (libxl_device_pci_remove(priv->ctx, vm->def->id, &pcidev, 0) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("libxenlight failed to detach pci device\ + %.4x:%.2x:%.2x.%.1x"), + subsys->u.pci.addr.domain, subsys->u.pci.addr.bus, + subsys->u.pci.addr.slot, subsys->u.pci.addr.function); + goto cleanup; + } + + libxl_device_pci_dispose(&pcidev); + + virDomainHostdevRemove(vm->def, idx); + + hostdev_mgr = virHostdevManagerGetDefault(); + if (hostdev_mgr != NULL) + virHostdevReAttachPciHostdevs(hostdev_mgr, LIBXL_DRIVER_NAME, + vm->def->name, &hostdev, 1); + + return 0; + +cleanup: + virDomainHostdevDefFree(detach); + return -1; +} + +static int +libxlDomainDetachHostDevice(libxlDomainObjPrivatePtr priv, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev) +{ + virDomainHostdevDefPtr hostdev = dev->data.hostdev; + virDomainHostdevSubsysPtr subsys = &hostdev->source.subsys; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("hostdev mode '%s' not supported"), + virDomainHostdevModeTypeToString(hostdev->mode)); + return -1; + } + + switch (subsys->type) { + case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: + return libxlDomainDetachHostPciDevice(priv, vm, hostdev); + + default: + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unexpected hostdev type %d"), subsys->type); + break; + } + + return -1; +} + +static int libxlDomainDetachDeviceLive(libxlDomainObjPrivatePtr priv, virDomainObjPtr vm, virDomainDeviceDefPtr dev) { @@ -3188,6 +3447,10 @@ libxlDomainDetachDeviceLive(libxlDomainObjPrivatePtr priv, virDomainObjPtr vm, ret = libxlDomainDetachDeviceDiskLive(priv, vm, dev); break; + case VIR_DOMAIN_DEVICE_HOSTDEV: + ret = libxlDomainDetachHostDevice(priv, vm, dev); + break; + default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("device type '%s' cannot be detached"), @@ -3198,6 +3461,7 @@ libxlDomainDetachDeviceLive(libxlDomainObjPrivatePtr priv, virDomainObjPtr vm, return ret; } + static int libxlDomainDetachDeviceConfig(virDomainDefPtr vmdef, virDomainDeviceDefPtr dev) { @@ -4361,10 +4625,188 @@ libxlConnectSupportsFeature(virConnectPtr conn, int feature) } } +static int +libxlNodeDeviceGetPciInfo(virNodeDeviceDefPtr def, + unsigned *domain, + unsigned *bus, + unsigned *slot, + unsigned *function) +{ + virNodeDevCapsDefPtr cap; + int ret = -1; + + cap = def->caps; + while (cap) { + if (cap->type == VIR_NODE_DEV_CAP_PCI_DEV) { + *domain = cap->data.pci_dev.domain; + *bus = cap->data.pci_dev.bus; + *slot = cap->data.pci_dev.slot; + *function = cap->data.pci_dev.function; + break; + } + + cap = cap->next; + } + + if (!cap) { + virReportError(VIR_ERR_INVALID_ARG, + _("device %s is not a PCI device"), def->name); + goto out; + } + + ret = 0; +out: + return ret; +} + +static int +libxlNodeDeviceDetachFlags(virNodeDevicePtr dev, + const char *driverName, + unsigned int flags) +{ + virPCIDevicePtr pci = NULL; + unsigned domain = 0, bus = 0, slot = 0, function = 0; + int ret = -1; + virNodeDeviceDefPtr def = NULL; + char *xml = NULL; + virHostdevManagerPtr hostdev_mgr; + + virCheckFlags(0, -1); + + xml = virNodeDeviceGetXMLDesc(dev, 0); + if (!xml) + goto cleanup; + + def = virNodeDeviceDefParseString(xml, EXISTING_DEVICE, NULL); + if (!def) + goto cleanup; + + if (virNodeDeviceDetachFlagsEnsureACL(dev->conn, def) < 0) + goto cleanup; + + if (libxlNodeDeviceGetPciInfo(def, &domain, &bus, &slot, &function) < 0) + goto cleanup; + + pci = virPCIDeviceNew(domain, bus, slot, function); + if (!pci) + goto cleanup; + + if (!driverName || STREQ(driverName, "xen")) { + if (virPCIDeviceSetStubDriver(pci, "pciback") < 0) + goto cleanup; + } else { + virReportError(VIR_ERR_INVALID_ARG, + _("unsupported driver name '%s'"), driverName); + goto cleanup; + } + + hostdev_mgr = virHostdevManagerGetDefault(); + if (hostdev_mgr == NULL || + virHostdevPciNodeDeviceDetach(hostdev_mgr, pci) < 0) + goto cleanup; + + ret = 0; +cleanup: + virPCIDeviceFree(pci); + virNodeDeviceDefFree(def); + VIR_FREE(xml); + return ret; +} + +static int +libxlNodeDeviceDettach(virNodeDevicePtr dev) +{ + return libxlNodeDeviceDetachFlags(dev, NULL, 0); +} + +static int +libxlNodeDeviceReAttach(virNodeDevicePtr dev) +{ + virPCIDevicePtr pci = NULL; + unsigned domain = 0, bus = 0, slot = 0, function = 0; + int ret = -1; + virNodeDeviceDefPtr def = NULL; + char *xml = NULL; + virHostdevManagerPtr hostdev_mgr; + + xml = virNodeDeviceGetXMLDesc(dev, 0); + if (!xml) + goto cleanup; + + def = virNodeDeviceDefParseString(xml, EXISTING_DEVICE, NULL); + if (!def) + goto cleanup; + + if (virNodeDeviceReAttachEnsureACL(dev->conn, def) < 0) + goto cleanup; + + if (libxlNodeDeviceGetPciInfo(def, &domain, &bus, &slot, &function) < 0) + goto cleanup; + + pci = virPCIDeviceNew(domain, bus, slot, function); + if (!pci) + goto cleanup; + + hostdev_mgr = virHostdevManagerGetDefault(); + if (hostdev_mgr == NULL || + virHostdevPciNodeDeviceReAttach(hostdev_mgr, pci) < 0) + goto out; + + ret = 0; +out: + virPCIDeviceFree(pci); +cleanup: + virNodeDeviceDefFree(def); + VIR_FREE(xml); + return ret; +} + +static int +libxlNodeDeviceReset(virNodeDevicePtr dev) +{ + virPCIDevicePtr pci; + unsigned domain = 0, bus = 0, slot = 0, function = 0; + int ret = -1; + virNodeDeviceDefPtr def = NULL; + char *xml = NULL; + virHostdevManagerPtr hostdev_mgr; + + xml = virNodeDeviceGetXMLDesc(dev, 0); + if (!xml) + goto cleanup; + + def = virNodeDeviceDefParseString(xml, EXISTING_DEVICE, NULL); + if (!def) + goto cleanup; + + if (virNodeDeviceResetEnsureACL(dev->conn, def) < 0) + goto cleanup; + + if (libxlNodeDeviceGetPciInfo(def, &domain, &bus, &slot, &function) < 0) + goto cleanup; + + pci = virPCIDeviceNew(domain, bus, slot, function); + if (!pci) + goto cleanup; + + hostdev_mgr = virHostdevManagerGetDefault(); + if (hostdev_mgr == NULL || + virHostdevPciNodeDeviceReset(hostdev_mgr, pci) < 0) + goto out; + + ret = 0; +out: + virPCIDeviceFree(pci); +cleanup: + virNodeDeviceDefFree(def); + VIR_FREE(xml); + return ret; +} + static virDriver libxlDriver = { .no = VIR_DRV_LIBXL, - .name = "xenlight", + .name = LIBXL_DRIVER_NAME, .connectOpen = libxlConnectOpen, /* 0.9.0 */ .connectClose = libxlConnectClose, /* 0.9.0 */ .connectGetType = libxlConnectGetType, /* 0.9.0 */ @@ -4447,6 +4889,10 @@ static virDriver libxlDriver = { .connectDomainEventDeregisterAny = libxlConnectDomainEventDeregisterAny, /* 0.9.0 */ .connectIsAlive = libxlConnectIsAlive, /* 0.9.8 */ .connectSupportsFeature = libxlConnectSupportsFeature, /* 1.1.1 */ + .nodeDeviceDettach = libxlNodeDeviceDettach, /* 1.2.2 */ + .nodeDeviceDetachFlags = libxlNodeDeviceDetachFlags, /* 1.2.2 */ + .nodeDeviceReAttach = libxlNodeDeviceReAttach, /* 1.2.2 */ + .nodeDeviceReset = libxlNodeDeviceReset, /* 1.2.2 */ }; static virStateDriver libxlStateDriver = { -- 1.6.0.2
participants (4)
-
Cedric Bosdonnat
-
Chunyan Liu
-
Daniel P. Berrange
-
Laine Stump