int
qemuPrepareHostdevUSBDevices(struct qemud_driver *driver,
const char *name,
- virDomainHostdevDefPtr *hostdevs,
- int nhostdevs)
+ usbDeviceList *list)
{
- int ret = -1;
int i;
+ 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)
+ 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));
+ return -1;
+ }
+
+ 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)
+ return -1;
+ }
+ return 0;
+}
+
+static int
+qemuPrepareHostUSBDevices(struct qemud_driver *driver,
+ virDomainDefPtr def)
+{
+ int i, ret = -1;
usbDeviceList *list;
usbDevice *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.
*/
for (i = 0 ; i < nhostdevs ; i++) {
virDomainHostdevDefPtr hostdev = hostdevs[i];
+ usbDevice *usb = NULL;
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;
- usbDeviceList *devs;
+ 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;
- devs = usbFindDeviceByVendor(hostdev->source.subsys.u.usb.vendor,
- hostdev->source.subsys.u.usb.product);
+ if (vendor && bus) {
+ usb = usbFindDevice(vendor, product, bus, device);
+ } else if (vendor && !bus) {
+ usbDeviceList *devs = usbFindDeviceByVendor(vendor, product);
if (!devs)
goto cleanup;
+ if (usbDeviceListCount(devs) > 1) {
+ qemuReportError(VIR_ERR_OPERATION_FAILED,
+ _("multiple USB devices for %x:%x, "
+ "use <address> to specify one"), vendor, product);
+ usbDeviceListFree(devs);
+ goto cleanup;
+ }
usb = usbDeviceListGet(devs, 0);
usbDeviceListSteal(devs, usb);
usbDeviceListFree(devs);
- 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;
- }
-
hostdev->source.subsys.u.usb.bus = usbDeviceGetBus(usb);
hostdev->source.subsys.u.usb.device = usbDeviceGetDevno(usb);
- if (usbDeviceListAdd(list, usb) < 0) {
- usbFreeDevice(usb);
- goto cleanup;
- }
+ } else if (!vendor && bus) {
+ usb = usbFindDeviceByBus(bus, device);
+ }
+
+ if (!usb)
+ goto cleanup;
+ if (usbDeviceListAdd(list, usb) < 0) {
+ usbFreeDevice(usb);
+ goto cleanup;
}
}
- /* Loop 2: Mark devices in temporary list as used by @name
+ /* 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);
-
- VIR_DEBUG("Adding %03d.%03d dom=%s to activeUsbHostdevs",
- usbDeviceGetBus(tmp), usbDeviceGetDevno(tmp), name);
- if (usbDeviceListAdd(driver->activeUsbHostdevs, tmp) < 0) {
- usbFreeDevice(tmp);
- goto inactivedevs;
- }
- }
+ if (qemuPrepareHostdevUSBDevices(driver, def->name, list) < 0)
+ goto inactivedevs;
- /* Loop 3: Temporary list was successfully merged with
+ /* Loop 2: Temporary list was successfully merged with
* driver list, so steal all items to avoid freeing them
* in cleanup label.
*/
return ret;
}
-static int
-qemuPrepareHostUSBDevices(struct qemud_driver *driver,
- virDomainDefPtr def)
-{
- return qemuPrepareHostdevUSBDevices(driver, def->name, def->hostdevs, def->nhostdevs);
-}
-
int qemuPrepareHostDevices(struct qemud_driver *driver,
virDomainDefPtr def)
{
return -1;
}
-
int qemuDomainAttachHostDevice(struct qemud_driver *driver,
virDomainObjPtr vm,
virDomainHostdevDefPtr hostdev)
{
+ usbDeviceList *list;
+ usbDevice *usb = NULL;
+
if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) {
qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("hostdev mode '%s' not supported"),
return -1;
}
- /* Resolve USB product/vendor to bus/device */
- if (hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB &&
- hostdev->source.subsys.u.usb.vendor) {
- usbDevice *usb;
- usbDeviceList *list;
+ if (!(list = usbDeviceListNew()))
+ goto cleanup;
- if (qemuPrepareHostdevUSBDevices(driver, vm->def->name, &hostdev, 1) < 0)
- goto error;
+ if (hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_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;
- list = usbFindDeviceByVendor(hostdev->source.subsys.u.usb.vendor,
- hostdev->source.subsys.u.usb.product);
+ if (vendor && bus) {
+ usb = usbFindDevice(vendor, product, bus, device);
- if (!list)
- return -1;
+ } else if (vendor && !bus) {
+ usbDeviceList *devs = usbFindDeviceByVendor(vendor, product);
+ if (!devs)
+ goto cleanup;
- usb = usbDeviceListGet(list, 0);
- usbDeviceListSteal(list, usb);
- usbDeviceListFree(list);
+ if (usbDeviceListCount(devs) > 1) {
+ qemuReportError(VIR_ERR_OPERATION_FAILED,
+ _("multiple USB devices for %x:%x, "
+ "use <address> to specify one"), vendor, product);
+ usbDeviceListFree(devs);
+ goto cleanup;
+ }
+ usb = usbDeviceListGet(devs, 0);
+ usbDeviceListSteal(devs, usb);
+ usbDeviceListFree(devs);
- hostdev->source.subsys.u.usb.bus = usbDeviceGetBus(usb);
- hostdev->source.subsys.u.usb.device = usbDeviceGetDevno(usb);
+ hostdev->source.subsys.u.usb.bus = usbDeviceGetBus(usb);
+ hostdev->source.subsys.u.usb.device = usbDeviceGetDevno(usb);
- usbFreeDevice(usb);
- }
+ } else if (!vendor && bus) {
+ usb = usbFindDeviceByBus(bus, device);
+ }
+ if (!usb)
+ goto cleanup;
+
+ if (usbDeviceListAdd(list, usb) < 0) {
+ usbFreeDevice(usb);
+ goto cleanup;
+ }
+
+ if (qemuPrepareHostdevUSBDevices(driver, vm->def->name, list) < 0)
+ goto cleanup;
+
+ usbDeviceListSteal(list, usb);
+ }
if (virSecurityManagerSetHostdevLabel(driver->securityManager,
vm->def, hostdev) < 0)
- return -1;
+ goto cleanup;
switch (hostdev->source.subsys.type) {
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI:
goto error;
}
+ usbDeviceListFree(list);
return 0;
error:
vm->def, hostdev) < 0)
VIR_WARN("Unable to restore host device labelling on hotplug fail");
+cleanup:
+ usbDeviceListFree(list);
+ usbDeviceListSteal(driver->activeUsbHostdevs, usb);
return -1;
}