From 815d98ac0bb8a4b48a412e026cb6465309e4043c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A1n=20Tomko?= Date: Fri, 17 Jun 2016 09:49:54 +0200 Subject: [PATCH] Auto-add one hub if there are too many USB devices When parsing a command line with USB devices that have no address specified, QEMU automatically adds a USB hub if the device would fill up all the available USB ports. To help most of the users, add one hub if there are more USB devices than available ports. For wilder configurations, expect the user to provide us with more hubs and/or controllers. --- src/conf/domain_addr.c | 20 ++++++++ src/conf/domain_addr.h | 2 + src/libvirt_private.syms | 1 + src/qemu/qemu_domain_address.c | 48 +++++++++++++++++++ .../qemuxml2argv-usb-hub-autoadd.args | 28 +++++++++++ .../qemuxml2argv-usb-hub-autoadd.xml | 23 +++++++++ tests/qemuxml2argvtest.c | 3 ++ 7 files changed, 125 insertions(+) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-usb-hub-autoadd.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-usb-hub-autoadd.xml diff --git a/src/conf/domain_addr.c b/src/conf/domain_addr.c index 365ee40ab6..c3469eec9f 100644 --- a/src/conf/domain_addr.c +++ b/src/conf/domain_addr.c @@ -1604,6 +1604,26 @@ virDomainUSBAddressFindFreePort(virDomainUSBAddressHubPtr hub, } +size_t +virDomainUSBAddressCountAllPorts(virDomainDefPtr def) +{ + size_t i, ret = 0; + + for (i = 0; i < def->ncontrollers; i++) { + virDomainControllerDefPtr cont = def->controllers[i]; + if (cont->type == VIR_DOMAIN_CONTROLLER_TYPE_USB) + ret += virDomainUSBAddressControllerModelToPorts(cont); + } + + for (i = 0; i < def->nhubs; i++) { + virDomainHubDefPtr hub = def->hubs[i]; + if (hub->type == VIR_DOMAIN_HUB_TYPE_USB) + ret += VIR_DOMAIN_USB_HUB_PORTS; + } + return ret; +} + + /* Try to find a free port on bus @bus. * * Returns 0 on success diff --git a/src/conf/domain_addr.h b/src/conf/domain_addr.h index cc36aedd52..ce949812dd 100644 --- a/src/conf/domain_addr.h +++ b/src/conf/domain_addr.h @@ -277,6 +277,8 @@ int virDomainUSBAddressSetAddHub(virDomainUSBAddressSetPtr addrs, virDomainHubDefPtr hub) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); +size_t +virDomainUSBAddressCountAllPorts(virDomainDefPtr def); void virDomainUSBAddressSetFree(virDomainUSBAddressSetPtr addrs); int diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index be753333ac..9396c4eb1b 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -108,6 +108,7 @@ virDomainPCIAddressSlotInUse; virDomainPCIAddressValidate; virDomainPCIControllerModelToConnectType; virDomainUSBAddressAssign; +virDomainUSBAddressCountAllPorts; virDomainUSBAddressEnsure; virDomainUSBAddressPortFormat; virDomainUSBAddressPortFormatBuf; diff --git a/src/qemu/qemu_domain_address.c b/src/qemu/qemu_domain_address.c index 7499026f16..787b357d82 100644 --- a/src/qemu/qemu_domain_address.c +++ b/src/qemu/qemu_domain_address.c @@ -1679,6 +1679,51 @@ qemuDomainAssignUSBPorts(virDomainUSBAddressSetPtr addrs, } +static int +qemuDomainAssignUSBPortsCounter(virDomainDeviceInfoPtr info ATTRIBUTE_UNUSED, + void *opaque) +{ + struct qemuAssignUSBIteratorInfo *data = opaque; + + data->count++; + return 0; +} + + +static int +qemuDomainUSBAddressAddHubs(virDomainDefPtr def) +{ + struct qemuAssignUSBIteratorInfo data = { .count = 0 }; + virDomainHubDefPtr hub = NULL; + size_t available_ports; + int ret = -1; + + available_ports = virDomainUSBAddressCountAllPorts(def); + ignore_value(virDomainUSBDeviceDefForeach(def, + qemuDomainAssignUSBPortsCounter, + &data, + false)); + VIR_DEBUG("Found %zu USB devices and %zu provided USB ports", + data.count, available_ports); + + /* Add one hub if there are more devices than ports + * otherwise it's up to the user to specify more hubs/controllers */ + if (data.count > available_ports) { + if (VIR_ALLOC(hub) < 0) + return -1; + hub->type = VIR_DOMAIN_HUB_TYPE_USB; + + if (VIR_APPEND_ELEMENT(def->hubs, def->nhubs, hub) < 0) + goto cleanup; + } + + ret = 0; + cleanup: + VIR_FREE(hub); + return ret; +} + + static int qemuDomainAssignUSBAddresses(virDomainDefPtr def, virDomainObjPtr obj) @@ -1690,6 +1735,9 @@ qemuDomainAssignUSBAddresses(virDomainDefPtr def, if (!(addrs = virDomainUSBAddressSetCreate())) goto cleanup; + if (qemuDomainUSBAddressAddHubs(def) < 0) + goto cleanup; + if (virDomainUSBAddressSetAddControllers(addrs, def) < 0) goto cleanup; diff --git a/tests/qemuxml2argvdata/qemuxml2argv-usb-hub-autoadd.args b/tests/qemuxml2argvdata/qemuxml2argv-usb-hub-autoadd.args new file mode 100644 index 0000000000..7467893ecb --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-usb-hub-autoadd.args @@ -0,0 +1,28 @@ +LC_ALL=C \ +PATH=/bin \ +HOME=/home/test \ +USER=test \ +LOGNAME=test \ +QEMU_AUDIO_DRV=none \ +/usr/bin/qemu \ +-name QEMUGuest1 \ +-S \ +-M pc \ +-m 214 \ +-smp 1,sockets=1,cores=1,threads=1 \ +-uuid c7a5fdbd-edaf-9455-926a-d65c16db1809 \ +-nographic \ +-nodefconfig \ +-nodefaults \ +-chardev socket,id=charmonitor,path=/tmp/lib/domain--1-QEMUGuest1/monitor.sock,\ +server,nowait \ +-mon chardev=charmonitor,id=monitor,mode=readline \ +-no-acpi \ +-boot c \ +-usb \ +-device usb-hub,id=hub0,bus=usb.0,port=1 \ +-device usb-mouse,id=input0,bus=usb.0,port=2 \ +-device usb-mouse,id=input1,bus=usb.0,port=1.1 \ +-device usb-mouse,id=input2,bus=usb.0,port=1.2 \ +-device usb-tablet,id=input3,bus=usb.0,port=1.3 \ +-device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-usb-hub-autoadd.xml b/tests/qemuxml2argvdata/qemuxml2argv-usb-hub-autoadd.xml new file mode 100644 index 0000000000..43e0f1f35b --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-usb-hub-autoadd.xml @@ -0,0 +1,23 @@ + + QEMUGuest1 + c7a5fdbd-edaf-9455-926a-d65c16db1809 + 219136 + 219136 + 1 + + hvm + + + + /usr/bin/qemu + + + + + + + + + + + diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index c99568b8f6..356f8435f5 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -1184,6 +1184,9 @@ mymain(void) DO_TEST("usb-hub", QEMU_CAPS_CHARDEV, QEMU_CAPS_USB_HUB, QEMU_CAPS_NODEFCONFIG); + DO_TEST("usb-hub-autoadd", + QEMU_CAPS_CHARDEV, QEMU_CAPS_USB_HUB, + QEMU_CAPS_NODEFCONFIG); DO_TEST_PARSE_ERROR("usb-hub-conflict", QEMU_CAPS_CHARDEV, QEMU_CAPS_USB_HUB, QEMU_CAPS_NODEFCONFIG);