Signed-off-by: Chunyan Liu <cyliu@xxxxxxxx> --- src/libvirt_private.syms | 1 + src/qemu/qemu_hostdev.c | 217 ----------------------------------------------- src/util/virhostdev.c | 216 ++++++++++++++++++++++++++++++++++++++++++++++ src/util/virhostdev.h | 7 ++ 4 files changed, 224 insertions(+), 217 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 8aba3cf..04694d8 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1295,6 +1295,7 @@ virHookPresent; #util/virhostdev.h virHostdevManagerGetDefault; virHostdevPreparePCIDevices; +virHostdevPrepareUSBDevices; virHostdevReAttachPCIDevices; virHostdevUpdateActivePciHostdevs; virHostdevUpdateActiveScsiHostdevs; diff --git a/src/qemu/qemu_hostdev.c b/src/qemu/qemu_hostdev.c index 0eb78ac..35494d8 100644 --- a/src/qemu/qemu_hostdev.c +++ b/src/qemu/qemu_hostdev.c @@ -232,223 +232,6 @@ out: return ret; } - -static int -virHostdevMarkUsbHostdevs(virHostdevManagerPtr mgr, - const char *drv_name, - const char *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, 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(mgr->activeUsbHostdevs, usb) < 0) - goto error; - } - - virObjectUnlock(mgr->activeUsbHostdevs); - return 0; - -error: - for (j = 0; j < i; j++) { - tmp = virUSBDeviceListGet(list, i); - virUSBDeviceListSteal(mgr->activeUsbHostdevs, tmp); - } - virObjectUnlock(mgr->activeUsbHostdevs); - return -1; -} - - -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 -virHostdevPrepareUSBDevices(virHostdevManagerPtr hostdev_mgr, - const char *drv_name, - const char *name, - virDomainHostdevDefPtr *hostdevs, - int nhostdevs, - unsigned int flags) -{ - size_t i; - int ret = -1; - virUSBDeviceListPtr list; - virUSBDevicePtr tmp; - bool coldBoot = !!(flags & VIR_HOSTDEV_COLD_BOOT); - - /* 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 virHostdevPreparePCIDevices() - */ - 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 driver list. However, if something goes - * wrong, perform rollback. - */ - if (virHostdevMarkUsbHostdevs(hostdev_mgr, drv_name, 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 qemuPrepareHostUSBDevices(virQEMUDriverPtr driver, const char *name, diff --git a/src/util/virhostdev.c b/src/util/virhostdev.c index 47447fa..a55480c 100644 --- a/src/util/virhostdev.c +++ b/src/util/virhostdev.c @@ -927,3 +927,219 @@ cleanup: virObjectUnlock(mgr->activeScsiHostdevs); return ret; } + +static int +virHostdevMarkUsbHostdevs(virHostdevManagerPtr mgr, + const char *drv_name, + const char *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, 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(mgr->activeUsbHostdevs, usb) < 0) + goto error; + } + + virObjectUnlock(mgr->activeUsbHostdevs); + return 0; + +error: + for (j = 0; j < i; j++) { + tmp = virUSBDeviceListGet(list, i); + virUSBDeviceListSteal(mgr->activeUsbHostdevs, tmp); + } + virObjectUnlock(mgr->activeUsbHostdevs); + return -1; +} + + +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; +} + +int +virHostdevPrepareUSBDevices(virHostdevManagerPtr hostdev_mgr, + const char *drv_name, + const char *name, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs, + unsigned int flags) +{ + size_t i; + int ret = -1; + virUSBDeviceListPtr list; + virUSBDevicePtr tmp; + bool coldBoot = !!(flags & VIR_HOSTDEV_COLD_BOOT); + + /* 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 virHostdevPreparePCIDevices() + */ + 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 driver list. However, if something goes + * wrong, perform rollback. + */ + if (virHostdevMarkUsbHostdevs(hostdev_mgr, drv_name, 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; +} diff --git a/src/util/virhostdev.h b/src/util/virhostdev.h index e6b5e31..83ebefc 100644 --- a/src/util/virhostdev.h +++ b/src/util/virhostdev.h @@ -58,6 +58,13 @@ virHostdevPreparePCIDevices(virHostdevManagerPtr hostdev_mgr, virDomainHostdevDefPtr *hostdevs, int nhostdevs, unsigned int flags); +int +virHostdevPrepareUSBDevices(virHostdevManagerPtr hostdev_mgr, + const char *drv_name, + const char *name, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs, + unsigned int flags); void virHostdevReAttachPCIDevices(virHostdevManagerPtr hostdev_mgr, const char *drv_name, -- 1.9.0 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list