mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2025-01-21 12:05:17 +00:00
qemu: call usb search function for hostdev initialization and hotplug
src/qemu/qemu_hostdev.c: refactor qemuPrepareHostdevUSBDevices function, make it focus on adding usb device to activeUsbHostdevs after check. After that, the usb hotplug function qemuDomainAttachHostDevice also could use it. expand qemuPrepareHostUSBDevices to perform the usb search, rollback on failure. src/qemu/qemu_hotplug.c: If there are multiple usb devices available with same vendorID and productID, but with different value of "bus, device", we give an error to let user use <address> to specify the desired one. (cherry picked from commit 05abd1507d66aabb6cad12eeafeb4c4d1911c585)
This commit is contained in:
parent
18c1491697
commit
d617c987b7
@ -565,13 +565,53 @@ qemuPrepareHostPCIDevices(struct qemud_driver *driver,
|
|||||||
int
|
int
|
||||||
qemuPrepareHostdevUSBDevices(struct qemud_driver *driver,
|
qemuPrepareHostdevUSBDevices(struct qemud_driver *driver,
|
||||||
const char *name,
|
const char *name,
|
||||||
virDomainHostdevDefPtr *hostdevs,
|
usbDeviceList *list)
|
||||||
int nhostdevs)
|
|
||||||
{
|
{
|
||||||
int ret = -1;
|
|
||||||
int i;
|
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;
|
usbDeviceList *list;
|
||||||
usbDevice *tmp;
|
usbDevice *tmp;
|
||||||
|
virDomainHostdevDefPtr *hostdevs = def->hostdevs;
|
||||||
|
int nhostdevs = def->nhostdevs;
|
||||||
|
|
||||||
/* To prevent situation where USB device is assigned to two domains
|
/* To prevent situation where USB device is assigned to two domains
|
||||||
* we need to keep a list of currently assigned USB devices.
|
* we need to keep a list of currently assigned USB devices.
|
||||||
@ -586,70 +626,61 @@ qemuPrepareHostdevUSBDevices(struct qemud_driver *driver,
|
|||||||
*/
|
*/
|
||||||
for (i = 0 ; i < nhostdevs ; i++) {
|
for (i = 0 ; i < nhostdevs ; i++) {
|
||||||
virDomainHostdevDefPtr hostdev = hostdevs[i];
|
virDomainHostdevDefPtr hostdev = hostdevs[i];
|
||||||
|
usbDevice *usb = NULL;
|
||||||
|
|
||||||
if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
|
if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
|
||||||
continue;
|
continue;
|
||||||
if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB)
|
if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* Resolve a vendor/product to bus/device */
|
unsigned vendor = hostdev->source.subsys.u.usb.vendor;
|
||||||
if (hostdev->source.subsys.u.usb.vendor) {
|
unsigned product = hostdev->source.subsys.u.usb.product;
|
||||||
usbDevice *usb;
|
unsigned bus = hostdev->source.subsys.u.usb.bus;
|
||||||
usbDeviceList *devs;
|
unsigned device = hostdev->source.subsys.u.usb.device;
|
||||||
|
|
||||||
devs = usbFindDeviceByVendor(hostdev->source.subsys.u.usb.vendor,
|
if (vendor && bus) {
|
||||||
hostdev->source.subsys.u.usb.product);
|
usb = usbFindDevice(vendor, product, bus, device);
|
||||||
|
|
||||||
|
} else if (vendor && !bus) {
|
||||||
|
usbDeviceList *devs = usbFindDeviceByVendor(vendor, product);
|
||||||
if (!devs)
|
if (!devs)
|
||||||
goto cleanup;
|
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);
|
usb = usbDeviceListGet(devs, 0);
|
||||||
usbDeviceListSteal(devs, usb);
|
usbDeviceListSteal(devs, usb);
|
||||||
usbDeviceListFree(devs);
|
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.bus = usbDeviceGetBus(usb);
|
||||||
hostdev->source.subsys.u.usb.device = usbDeviceGetDevno(usb);
|
hostdev->source.subsys.u.usb.device = usbDeviceGetDevno(usb);
|
||||||
|
|
||||||
if (usbDeviceListAdd(list, usb) < 0) {
|
} else if (!vendor && bus) {
|
||||||
usbFreeDevice(usb);
|
usb = usbFindDeviceByBus(bus, device);
|
||||||
goto cleanup;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
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
|
* and add them do driver list. However, if something goes
|
||||||
* wrong, perform rollback.
|
* wrong, perform rollback.
|
||||||
*/
|
*/
|
||||||
for (i = 0; i < usbDeviceListCount(list); i++) {
|
if (qemuPrepareHostdevUSBDevices(driver, def->name, list) < 0)
|
||||||
tmp = usbDeviceListGet(list, i);
|
goto inactivedevs;
|
||||||
usbDeviceSetUsedBy(tmp, name);
|
|
||||||
|
|
||||||
VIR_DEBUG("Adding %03d.%03d dom=%s to activeUsbHostdevs",
|
/* Loop 2: Temporary list was successfully merged with
|
||||||
usbDeviceGetBus(tmp), usbDeviceGetDevno(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
|
* driver list, so steal all items to avoid freeing them
|
||||||
* in cleanup label.
|
* in cleanup label.
|
||||||
*/
|
*/
|
||||||
@ -675,13 +706,6 @@ cleanup:
|
|||||||
return ret;
|
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,
|
int qemuPrepareHostDevices(struct qemud_driver *driver,
|
||||||
virDomainDefPtr def)
|
virDomainDefPtr def)
|
||||||
{
|
{
|
||||||
|
@ -38,8 +38,7 @@ int qemuPrepareHostdevPCIDevices(struct qemud_driver *driver,
|
|||||||
int nhostdevs);
|
int nhostdevs);
|
||||||
int qemuPrepareHostdevUSBDevices(struct qemud_driver *driver,
|
int qemuPrepareHostdevUSBDevices(struct qemud_driver *driver,
|
||||||
const char *name,
|
const char *name,
|
||||||
virDomainHostdevDefPtr *hostdevs,
|
usbDeviceList *list);
|
||||||
int nhostdevs);
|
|
||||||
int qemuPrepareHostDevices(struct qemud_driver *driver,
|
int qemuPrepareHostDevices(struct qemud_driver *driver,
|
||||||
virDomainDefPtr def);
|
virDomainDefPtr def);
|
||||||
void qemuReattachPciDevice(pciDevice *dev, struct qemud_driver *driver);
|
void qemuReattachPciDevice(pciDevice *dev, struct qemud_driver *driver);
|
||||||
|
@ -1116,11 +1116,13 @@ error:
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int qemuDomainAttachHostDevice(struct qemud_driver *driver,
|
int qemuDomainAttachHostDevice(struct qemud_driver *driver,
|
||||||
virDomainObjPtr vm,
|
virDomainObjPtr vm,
|
||||||
virDomainHostdevDefPtr hostdev)
|
virDomainHostdevDefPtr hostdev)
|
||||||
{
|
{
|
||||||
|
usbDeviceList *list;
|
||||||
|
usbDevice *usb = NULL;
|
||||||
|
|
||||||
if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) {
|
if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) {
|
||||||
qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
||||||
_("hostdev mode '%s' not supported"),
|
_("hostdev mode '%s' not supported"),
|
||||||
@ -1128,35 +1130,58 @@ int qemuDomainAttachHostDevice(struct qemud_driver *driver,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Resolve USB product/vendor to bus/device */
|
if (!(list = usbDeviceListNew()))
|
||||||
if (hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB &&
|
goto cleanup;
|
||||||
hostdev->source.subsys.u.usb.vendor) {
|
|
||||||
usbDevice *usb;
|
|
||||||
usbDeviceList *list;
|
|
||||||
|
|
||||||
if (qemuPrepareHostdevUSBDevices(driver, vm->def->name, &hostdev, 1) < 0)
|
if (hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) {
|
||||||
goto error;
|
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,
|
if (vendor && bus) {
|
||||||
hostdev->source.subsys.u.usb.product);
|
usb = usbFindDevice(vendor, product, bus, device);
|
||||||
|
|
||||||
if (!list)
|
} else if (vendor && !bus) {
|
||||||
return -1;
|
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);
|
||||||
|
|
||||||
|
hostdev->source.subsys.u.usb.bus = usbDeviceGetBus(usb);
|
||||||
|
hostdev->source.subsys.u.usb.device = usbDeviceGetDevno(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;
|
||||||
|
|
||||||
usb = usbDeviceListGet(list, 0);
|
|
||||||
usbDeviceListSteal(list, usb);
|
usbDeviceListSteal(list, usb);
|
||||||
usbDeviceListFree(list);
|
|
||||||
|
|
||||||
hostdev->source.subsys.u.usb.bus = usbDeviceGetBus(usb);
|
|
||||||
hostdev->source.subsys.u.usb.device = usbDeviceGetDevno(usb);
|
|
||||||
|
|
||||||
usbFreeDevice(usb);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (virSecurityManagerSetHostdevLabel(driver->securityManager,
|
if (virSecurityManagerSetHostdevLabel(driver->securityManager,
|
||||||
vm->def, hostdev) < 0)
|
vm->def, hostdev) < 0)
|
||||||
return -1;
|
goto cleanup;
|
||||||
|
|
||||||
switch (hostdev->source.subsys.type) {
|
switch (hostdev->source.subsys.type) {
|
||||||
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI:
|
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI:
|
||||||
@ -1178,6 +1203,7 @@ int qemuDomainAttachHostDevice(struct qemud_driver *driver,
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
usbDeviceListFree(list);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
@ -1185,6 +1211,9 @@ error:
|
|||||||
vm->def, hostdev) < 0)
|
vm->def, hostdev) < 0)
|
||||||
VIR_WARN("Unable to restore host device labelling on hotplug fail");
|
VIR_WARN("Unable to restore host device labelling on hotplug fail");
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
usbDeviceListFree(list);
|
||||||
|
usbDeviceListSteal(driver->activeUsbHostdevs, usb);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user