static void qemuDomainReAttachHostUsbDevices(struct qemud_driver *driver, const char *name, virDomainHostdevDefPtr *hostdevs, int nhostdevs) { int i; for (i = 0; i < nhostdevs; i++) { virDomainHostdevDefPtr hostdev = hostdevs[i]; usbDevice *usb, *tmp; const char *used_by = 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 = usbGetDevice(hostdev->source.subsys.u.usb.bus, hostdev->source.subsys.u.usb.device); 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 qemuProcessStart() 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 = usbDeviceListFind(driver->activeUsbHostdevs, usb); usbFreeDevice(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; } used_by = usbDeviceGetUsedBy(tmp); 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); usbDeviceListDel(driver->activeUsbHostdevs, tmp); } } }
int qemuPrepareHostdevUSBDevices(struct qemud_driver *driver, const char *name, usbDeviceList *list) { int i, j; unsigned int count; usbDevice *tmp; count = usbDeviceListCount(list); for (i = 0; i < count; i++) { usbDevice *usb = usbDeviceListGet(list, i); if ((tmp = usbDeviceListFind(driver->activeUsbHostdevs, usb))) { const char *other_name = usbDeviceGetUsedBy(tmp); if (other_name) virReportError(VIR_ERR_OPERATION_INVALID, _("USB device %s is in use by domain %s"), usbDeviceGetName(tmp), other_name); else virReportError(VIR_ERR_OPERATION_INVALID, _("USB device %s is already in use"), usbDeviceGetName(tmp)); goto error; } usbDeviceSetUsedBy(usb, name); VIR_DEBUG("Adding %03d.%03d dom=%s to activeUsbHostdevs", usbDeviceGetBus(usb), usbDeviceGetDevno(usb), name); /* * The caller is responsible to steal these usb devices * from the usbDeviceList that passed in on success, * perform rollback on failure. */ if (usbDeviceListAdd(driver->activeUsbHostdevs, usb) < 0) goto error; } return 0; error: for (j = 0; j < i; j++) { tmp = usbDeviceListGet(list, i); usbDeviceListSteal(driver->activeUsbHostdevs, tmp); } return -1; }
int usbDeviceListAdd(usbDeviceList *list, usbDevice *dev) { if (usbDeviceListFind(list, dev)) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Device %s is already in use"), dev->name); return -1; } if (VIR_REALLOC_N(list->devs, list->count+1) < 0) { virReportOOMError(); return -1; } list->devs[list->count++] = dev; return 0; }
int qemuPrepareHostdevUSBDevices(struct qemud_driver *driver, const char *name, virDomainHostdevDefPtr *hostdevs, int nhostdevs) { int ret = -1; int i; usbDeviceList *list; usbDevice *tmp; /* 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 qemuPrepareHostdevPCIDevices() */ if (!(list = usbDeviceListNew())) goto cleanup; /* Loop 1: build temporary list and validate no usb device * is already taken */ 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_USB) continue; /* Resolve a vendor/product to bus/device */ if (hostdev->source.subsys.u.usb.vendor) { usbDevice *usb = usbFindDevice(hostdev->source.subsys.u.usb.vendor, hostdev->source.subsys.u.usb.product); if (!usb) return -1; hostdev->source.subsys.u.usb.bus = usbDeviceGetBus(usb); hostdev->source.subsys.u.usb.device = usbDeviceGetDevno(usb); if ((tmp = usbDeviceListFind(driver->activeUsbHostdevs, usb))) { const char *other_name = usbDeviceGetUsedBy(tmp); if (other_name) qemuReportError(VIR_ERR_OPERATION_INVALID, _("USB device %s is in use by domain %s"), usbDeviceGetName(tmp), other_name); else qemuReportError(VIR_ERR_OPERATION_INVALID, _("USB device %s is already in use"), usbDeviceGetName(tmp)); usbFreeDevice(usb); goto cleanup; } if (usbDeviceListAdd(list, usb) < 0) { usbFreeDevice(usb); goto cleanup; } } } /* Loop 2: Mark devices in temporary list as used by @name * and add them do driver list. However, if something goes * wrong, perform rollback. */ for (i = 0; i < usbDeviceListCount(list); i++) { tmp = usbDeviceListGet(list, i); usbDeviceSetUsedBy(tmp, name); if (usbDeviceListAdd(driver->activeUsbHostdevs, tmp) < 0) { usbFreeDevice(tmp); goto inactivedevs; } } /* Loop 3: Temporary list was successfully merged with * driver list, so steal all items to avoid freeing them * in cleanup label. */ while (usbDeviceListCount(list) > 0) { tmp = usbDeviceListGet(list, 0); usbDeviceListSteal(list, tmp); } ret = 0; goto cleanup; inactivedevs: /* Steal devices from driver->activeUsbHostdevs. * We will free them later. */ for (i = 0; i < usbDeviceListCount(list); i++) { tmp = usbDeviceListGet(list, i); usbDeviceListSteal(driver->activeUsbHostdevs, tmp); } cleanup: usbDeviceListFree(list); return ret; }