[libvirt] [PATCH 00/17] qemu: Assign addresses to USB devices

Technically a v2, but it's been too long: https://www.redhat.com/archives/libvir-list/2015-August/msg00521.html With apologies to anyone who had read v1. https://bugzilla.redhat.com/show_bug.cgi?id=1215968 Ján Tomko (17): Format CCID controllers after USB hubs Fix USB port in input-usbmouse test Add a USB hub to controller order test Add a test for long USB port paths Split out USB port parsing Use for instead of code duplication when parsing USB port Store USB port path as an array of integers Do not call postParse with ABI_UPDATE when parsing cmdline Add newDomain parameter to qemuDomainAssignAddresses Introduce virDomainUSBAddressSet Add functions for adding USB controllers to addrs Add functions for adding USB hubs to addrs Reserve existing USB addresses Assign addresses to USB devices Auto-add one hub if there are too many USB devices Add USB disk to hotplug-base Assign addresses on USB device hotplug src/conf/device_conf.h | 5 +- src/conf/domain_addr.c | 481 +++++++++++++++++++++ src/conf/domain_addr.h | 55 +++ src/conf/domain_conf.c | 125 +++++- src/conf/domain_conf.h | 6 + src/libvirt_private.syms | 11 + src/qemu/qemu_command.c | 33 +- src/qemu/qemu_domain.c | 3 +- src/qemu/qemu_domain.h | 1 + src/qemu/qemu_domain_address.c | 126 +++++- src/qemu/qemu_domain_address.h | 3 +- src/qemu/qemu_hotplug.c | 27 ++ src/qemu/qemu_parse_command.c | 3 +- src/qemu/qemu_process.c | 7 +- tests/qemuhotplugtest.c | 2 +- ...otplug-console-compat-2-live+console-virtio.xml | 1 + .../qemuhotplug-hotplug-base-live+disk-usb.xml | 1 + .../qemuxml2argvdata/qemuxml2argv-bios-nvram.args | 2 +- tests/qemuxml2argvdata/qemuxml2argv-bios.args | 2 +- .../qemuxml2argv-console-compat-2-live.xml | 1 + .../qemuxml2argv-console-compat-2.xml | 4 +- .../qemuxml2argv-controller-order.args | 7 +- .../qemuxml2argv-controller-order.xml | 1 + .../qemuxml2argv-disk-usb-device-removable.args | 3 +- .../qemuxml2argv-disk-usb-device.args | 2 +- .../qemuxml2argv-graphics-spice-timeout.args | 2 +- ...muxml2argv-hostdev-usb-address-device-boot.args | 2 +- .../qemuxml2argv-hostdev-usb-address-device.args | 2 +- .../qemuxml2argv-hostdev-usb-address.args | 2 +- .../qemuxml2argv-hotplug-base.args | 5 +- .../qemuxml2argvdata/qemuxml2argv-hotplug-base.xml | 8 + .../qemuxml2argv-hugepages-numa.args | 2 +- .../qemuxml2argv-input-usbmouse-addr.args | 2 +- .../qemuxml2argv-input-usbmouse-addr.xml | 2 +- .../qemuxml2argv-input-usbmouse.args | 2 +- .../qemuxml2argv-input-usbtablet.args | 2 +- .../qemuxml2argv-pseries-usb-kbd.args | 2 +- .../qemuxml2argv-serial-spiceport.args | 2 +- .../qemuxml2argv-smartcard-controller.args | 2 +- .../qemuxml2argv-smartcard-host-certificates.args | 2 +- .../qemuxml2argv-smartcard-host.args | 2 +- ...emuxml2argv-smartcard-passthrough-spicevmc.args | 2 +- .../qemuxml2argv-smartcard-passthrough-tcp.args | 2 +- .../qemuxml2argv-sound-device.args | 2 +- .../qemuxml2argv-usb-hub-autoadd.args | 28 ++ .../qemuxml2argv-usb-hub-autoadd.xml | 23 + .../qemuxml2argv-usb-hub-conflict.xml | 22 + .../qemuxml2argv-usb-long-port-path.args | 27 ++ .../qemuxml2argv-usb-long-port-path.xml | 30 ++ .../qemuxml2argv-usb-port-autoassign.args | 28 ++ .../qemuxml2argv-usb-port-autoassign.xml | 27 ++ ...qemuxml2argv-usb-too-long-port-path-invalid.xml | 33 ++ tests/qemuxml2argvtest.c | 20 +- 53 files changed, 1139 insertions(+), 58 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-usb-hub-autoadd.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-usb-hub-autoadd.xml create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-usb-hub-conflict.xml create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-usb-long-port-path.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-usb-long-port-path.xml create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-usb-port-autoassign.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-usb-port-autoassign.xml create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-usb-too-long-port-path-invalid.xml -- 2.7.3

They can be USB devices. --- src/qemu/qemu_command.c | 30 +++++++++++++++++++++- .../qemuxml2argv-smartcard-controller.args | 2 +- .../qemuxml2argv-smartcard-host-certificates.args | 2 +- .../qemuxml2argv-smartcard-host.args | 2 +- ...emuxml2argv-smartcard-passthrough-spicevmc.args | 2 +- .../qemuxml2argv-smartcard-passthrough-tcp.args | 2 +- 6 files changed, 34 insertions(+), 6 deletions(-) diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 4a8def1..63b2672 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -2773,6 +2773,30 @@ qemuBuildControllerDevStr(const virDomainDef *domainDef, static int +qemuBuildCCIDControllerCommandLine(virCommandPtr cmd, + const virDomainDef *def, + virQEMUCapsPtr qemuCaps) +{ + size_t i; + + for (i = 0; i < def->ncontrollers; i++) { + virDomainControllerDefPtr cont = def->controllers[i]; + char *devstr; + + if (cont->type != VIR_DOMAIN_CONTROLLER_TYPE_CCID) + continue; + + virCommandAddArg(cmd, "-device"); + if (!(devstr = qemuBuildControllerDevStr(def, cont, qemuCaps, NULL))) + return -1; + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); + } + return 0; +} + + +static int qemuBuildControllerDevCommandLine(virCommandPtr cmd, const virDomainDef *def, virQEMUCapsPtr qemuCaps) @@ -2795,6 +2819,8 @@ qemuBuildControllerDevCommandLine(virCommandPtr cmd, * one. Likewise, we don't do anything for the primary IDE * controller on an i440fx machine or primary SATA on q35, but * we do add those beyond these two exceptions. + * + * CCID controllers are added after USB hubs. */ VIR_DOMAIN_CONTROLLER_TYPE_PCI, VIR_DOMAIN_CONTROLLER_TYPE_USB, @@ -2802,7 +2828,6 @@ qemuBuildControllerDevCommandLine(virCommandPtr cmd, VIR_DOMAIN_CONTROLLER_TYPE_IDE, VIR_DOMAIN_CONTROLLER_TYPE_SATA, VIR_DOMAIN_CONTROLLER_TYPE_VIRTIO_SERIAL, - VIR_DOMAIN_CONTROLLER_TYPE_CCID, }; for (j = 0; j < ARRAY_CARDINALITY(contOrder); j++) { @@ -9262,6 +9287,9 @@ qemuBuildCommandLine(virQEMUDriverPtr driver, if (qemuBuildHubCommandLine(cmd, def, qemuCaps) < 0) goto error; + if (qemuBuildCCIDControllerCommandLine(cmd, def, qemuCaps) < 0) + goto error; + if (qemuBuildDiskDriveCommandLine(cmd, def, qemuCaps, emitBootindex) < 0) goto error; diff --git a/tests/qemuxml2argvdata/qemuxml2argv-smartcard-controller.args b/tests/qemuxml2argvdata/qemuxml2argv-smartcard-controller.args index d3135c2..8cb0968 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-smartcard-controller.args +++ b/tests/qemuxml2argvdata/qemuxml2argv-smartcard-controller.args @@ -19,7 +19,7 @@ server,nowait \ -mon chardev=charmonitor,id=monitor,mode=readline \ -no-acpi \ -boot c \ --device usb-ccid,id=ccid0 \ -usb \ +-device usb-ccid,id=ccid0 \ -device ccid-card-emulated,backend=nss-emulated,id=smartcard0,bus=ccid0.0 \ -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-smartcard-host-certificates.args b/tests/qemuxml2argvdata/qemuxml2argv-smartcard-host-certificates.args index 09ef26c..d4a4d31 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-smartcard-host-certificates.args +++ b/tests/qemuxml2argvdata/qemuxml2argv-smartcard-host-certificates.args @@ -19,8 +19,8 @@ server,nowait \ -mon chardev=charmonitor,id=monitor,mode=readline \ -no-acpi \ -boot c \ --device usb-ccid,id=ccid0 \ -usb \ +-device usb-ccid,id=ccid0 \ -device ccid-card-emulated,backend=certificates,cert1=cert1,cert2=cert2,\ cert3=cert3,db=/etc/pki/nssdb,id=smartcard0,bus=ccid0.0 \ -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-smartcard-host.args b/tests/qemuxml2argvdata/qemuxml2argv-smartcard-host.args index d3135c2..8cb0968 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-smartcard-host.args +++ b/tests/qemuxml2argvdata/qemuxml2argv-smartcard-host.args @@ -19,7 +19,7 @@ server,nowait \ -mon chardev=charmonitor,id=monitor,mode=readline \ -no-acpi \ -boot c \ --device usb-ccid,id=ccid0 \ -usb \ +-device usb-ccid,id=ccid0 \ -device ccid-card-emulated,backend=nss-emulated,id=smartcard0,bus=ccid0.0 \ -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-smartcard-passthrough-spicevmc.args b/tests/qemuxml2argvdata/qemuxml2argv-smartcard-passthrough-spicevmc.args index b618507..7411f2a 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-smartcard-passthrough-spicevmc.args +++ b/tests/qemuxml2argvdata/qemuxml2argv-smartcard-passthrough-spicevmc.args @@ -19,8 +19,8 @@ server,nowait \ -mon chardev=charmonitor,id=monitor,mode=readline \ -no-acpi \ -boot c \ --device usb-ccid,id=ccid0 \ -usb \ +-device usb-ccid,id=ccid0 \ -chardev spicevmc,id=charsmartcard0,name=smartcard \ -device ccid-card-passthru,chardev=charsmartcard0,id=smartcard0,bus=ccid0.0 \ -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-smartcard-passthrough-tcp.args b/tests/qemuxml2argvdata/qemuxml2argv-smartcard-passthrough-tcp.args index e0fcb49..93bbbc4 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-smartcard-passthrough-tcp.args +++ b/tests/qemuxml2argvdata/qemuxml2argv-smartcard-passthrough-tcp.args @@ -19,8 +19,8 @@ server,nowait \ -mon chardev=charmonitor,id=monitor,mode=readline \ -no-acpi \ -boot c \ --device usb-ccid,id=ccid0 \ -usb \ +-device usb-ccid,id=ccid0 \ -chardev socket,id=charsmartcard0,host=127.0.0.1,port=2001,server,nowait \ -device ccid-card-passthru,chardev=charsmartcard0,id=smartcard0,bus=ccid0.0 \ -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 -- 2.7.3

The default USB controller only has two ports. --- tests/qemuxml2argvdata/qemuxml2argv-input-usbmouse-addr.args | 2 +- tests/qemuxml2argvdata/qemuxml2argv-input-usbmouse-addr.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/qemuxml2argvdata/qemuxml2argv-input-usbmouse-addr.args b/tests/qemuxml2argvdata/qemuxml2argv-input-usbmouse-addr.args index 68147a4..6310585 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-input-usbmouse-addr.args +++ b/tests/qemuxml2argvdata/qemuxml2argv-input-usbmouse-addr.args @@ -20,5 +20,5 @@ QEMU_AUDIO_DRV=none \ -usb \ -drive file=/dev/HostVG/QEMUGuest1,format=raw,if=none,id=drive-ide0-0-0 \ -device ide-drive,bus=ide.0,unit=0,drive=drive-ide0-0-0,id=ide0-0-0 \ --device usb-mouse,id=input0,bus=usb.0,port=4 \ +-device usb-mouse,id=input0,bus=usb.0,port=2 \ -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-input-usbmouse-addr.xml b/tests/qemuxml2argvdata/qemuxml2argv-input-usbmouse-addr.xml index a996838..dde3517 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-input-usbmouse-addr.xml +++ b/tests/qemuxml2argvdata/qemuxml2argv-input-usbmouse-addr.xml @@ -22,7 +22,7 @@ <controller type='usb' index='0'/> <controller type='ide' index='0'/> <input type='mouse' bus='usb'> - <address type='usb' bus='0' port='4'/> + <address type='usb' bus='0' port='2'/> </input> </devices> </domain> -- 2.7.3

On Fri, Jun 17, 2016 at 20:07:04 +0200, Ján Tomko wrote:
The default USB controller only has two ports. --- tests/qemuxml2argvdata/qemuxml2argv-input-usbmouse-addr.args | 2 +- tests/qemuxml2argvdata/qemuxml2argv-input-usbmouse-addr.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
ACK

The test has too many USB devices. --- tests/qemuxml2argvdata/qemuxml2argv-controller-order.args | 1 + tests/qemuxml2argvdata/qemuxml2argv-controller-order.xml | 1 + tests/qemuxml2argvtest.c | 3 ++- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/qemuxml2argvdata/qemuxml2argv-controller-order.args b/tests/qemuxml2argvdata/qemuxml2argv-controller-order.args index 9aaae37..52c1a74 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-controller-order.args +++ b/tests/qemuxml2argvdata/qemuxml2argv-controller-order.args @@ -19,6 +19,7 @@ nowait \ -boot order=cna,menu=off \ -device piix3-usb-uhci,id=usb,bus=pci.0,addr=0x1.0x2 \ -device virtio-serial-pci,id=virtio-serial0,bus=pci.0,addr=0x7 \ +-device usb-hub,id=hub0 \ -device usb-ccid,id=ccid0 \ -drive file=/tmp/fdr.img,format=raw,if=none,id=drive-virtio-disk0,cache=none,\ aio=native \ diff --git a/tests/qemuxml2argvdata/qemuxml2argv-controller-order.xml b/tests/qemuxml2argvdata/qemuxml2argv-controller-order.xml index 07db77e..2ae14f6 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-controller-order.xml +++ b/tests/qemuxml2argvdata/qemuxml2argv-controller-order.xml @@ -81,6 +81,7 @@ <address bus='14' device='6'/> </source> </hostdev> + <hub type='usb'/> <memballoon model='virtio'> <address type='pci' domain='0x0000' bus='0x00' slot='0x06' function='0x0'/> </memballoon> diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index c406b64..d47dc58 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -680,7 +680,8 @@ mymain(void) QEMU_CAPS_BOOT_MENU, QEMU_CAPS_PIIX3_USB_UHCI, QEMU_CAPS_PCI_MULTIFUNCTION, QEMU_CAPS_DRIVE_AIO, QEMU_CAPS_CCID_PASSTHRU, QEMU_CAPS_CHARDEV, - QEMU_CAPS_CHARDEV_SPICEVMC, QEMU_CAPS_SPICE, QEMU_CAPS_HDA_DUPLEX); + QEMU_CAPS_CHARDEV_SPICEVMC, QEMU_CAPS_SPICE, + QEMU_CAPS_HDA_DUPLEX, QEMU_CAPS_USB_HUB); DO_TEST("eoi-disabled", NONE); DO_TEST("eoi-enabled", NONE); DO_TEST("pv-spinlock-disabled", NONE); -- 2.7.3

On Fri, Jun 17, 2016 at 20:07:05 +0200, Ján Tomko wrote:
The test has too many USB devices. --- tests/qemuxml2argvdata/qemuxml2argv-controller-order.args | 1 + tests/qemuxml2argvdata/qemuxml2argv-controller-order.xml | 1 + tests/qemuxml2argvtest.c | 3 ++- 3 files changed, 4 insertions(+), 1 deletion(-)
ACK

For some reason, we support up to four levels of nested USB devices in the guest. Add a test for a domain using all four and a negative test for a domain using five. --- .../qemuxml2argv-usb-long-port-path.args | 27 ++++++++++++++++++ .../qemuxml2argv-usb-long-port-path.xml | 30 ++++++++++++++++++++ ...qemuxml2argv-usb-too-long-port-path-invalid.xml | 33 ++++++++++++++++++++++ tests/qemuxml2argvtest.c | 5 ++++ 4 files changed, 95 insertions(+) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-usb-long-port-path.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-usb-long-port-path.xml create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-usb-too-long-port-path-invalid.xml diff --git a/tests/qemuxml2argvdata/qemuxml2argv-usb-long-port-path.args b/tests/qemuxml2argvdata/qemuxml2argv-usb-long-port-path.args new file mode 100644 index 0000000..d9100f6 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-usb-long-port-path.args @@ -0,0 +1,27 @@ +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 \ +-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-hub,id=hub1,bus=usb.0,port=1.1 \ +-device usb-hub,id=hub2,bus=usb.0,port=1.1.1 \ +-device usb-hub,id=hub3,bus=usb.0,port=1.1.1.1 \ +-device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-usb-long-port-path.xml b/tests/qemuxml2argvdata/qemuxml2argv-usb-long-port-path.xml new file mode 100644 index 0000000..06967b1 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-usb-long-port-path.xml @@ -0,0 +1,30 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219136</memory> + <currentMemory unit='KiB'>219136</currentMemory> + <vcpu placement='static'>1</vcpu> + <os> + <type arch='i686' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <devices> + <emulator>/usr/bin/qemu</emulator> + <controller type='usb' index='0'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/> + </controller> + <hub type='usb'> + <address type='usb' bus='0' port='1'/> + </hub> + <hub type='usb'> + <address type='usb' bus='0' port='1.1'/> + </hub> + <hub type='usb'> + <address type='usb' bus='0' port='1.1.1'/> + </hub> + <hub type='usb'> + <address type='usb' bus='0' port='1.1.1.1'/> + </hub> + <memballoon model='virtio'/> + </devices> +</domain> diff --git a/tests/qemuxml2argvdata/qemuxml2argv-usb-too-long-port-path-invalid.xml b/tests/qemuxml2argvdata/qemuxml2argv-usb-too-long-port-path-invalid.xml new file mode 100644 index 0000000..789628a --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-usb-too-long-port-path-invalid.xml @@ -0,0 +1,33 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219136</memory> + <currentMemory unit='KiB'>219136</currentMemory> + <vcpu placement='static'>1</vcpu> + <os> + <type arch='i686' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <devices> + <emulator>/usr/bin/qemu</emulator> + <controller type='usb' index='0'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/> + </controller> + <hub type='usb'> + <address type='usb' bus='0' port='1'/> + </hub> + <hub type='usb'> + <address type='usb' bus='0' port='1.1'/> + </hub> + <hub type='usb'> + <address type='usb' bus='0' port='1.1.1'/> + </hub> + <hub type='usb'> + <address type='usb' bus='0' port='1.1.1.1'/> + </hub> + <hub type='usb'> + <address type='usb' bus='0' port='1.1.1.1.1'/> + </hub> + <memballoon model='virtio'/> + </devices> +</domain> diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index d47dc58..2a8f6c4 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -2014,6 +2014,11 @@ mymain(void) DO_TEST("debug-threads", QEMU_CAPS_NAME_DEBUG_THREADS); DO_TEST("master-key", QEMU_CAPS_OBJECT_SECRET); + DO_TEST("usb-long-port-path", QEMU_CAPS_CHARDEV, QEMU_CAPS_NODEFCONFIG, + QEMU_CAPS_USB_HUB); + DO_TEST_PARSE_FLAGS_ERROR("usb-too-long-port-path-invalid", + QEMU_CAPS_CHARDEV, + QEMU_CAPS_NODEFCONFIG, QEMU_CAPS_USB_HUB); DO_TEST("acpi-table", NONE); -- 2.7.3

On Fr, 2016-06-17 at 20:07 +0200, Ján Tomko wrote:
For some reason, we support up to four levels of nested USB devices in the guest.
FYI: The reason is simply that usb is specified that way. You can't chain usb hubs endlessly. cheers, Gerd

On Mon, Jun 20, 2016 at 08:50:31AM +0200, Gerd Hoffmann wrote:
On Fr, 2016-06-17 at 20:07 +0200, Ján Tomko wrote:
For some reason, we support up to four levels of nested USB devices in the guest.
FYI: The reason is simply that usb is specified that way. You can't chain usb hubs endlessly.
I was under the impression that 7 is the maximum depth. I see that QEMU errors out at 6 levels for a USB hub: qemu-git: -device usb-hub,port=1.1.1.1.1.1: usb hub chain too deep So perhaps up to 6 levels could be allowed for regular devices? Jan

On Di, 2016-06-21 at 18:19 +0200, Ján Tomko wrote:
On Mon, Jun 20, 2016 at 08:50:31AM +0200, Gerd Hoffmann wrote:
On Fr, 2016-06-17 at 20:07 +0200, Ján Tomko wrote:
For some reason, we support up to four levels of nested USB devices in the guest.
FYI: The reason is simply that usb is specified that way. You can't chain usb hubs endlessly.
I was under the impression that 7 is the maximum depth.
5 hubs IIRC (not fully sure whenever that includes the root hub or not). So, yes, we can do more than 4. The only use case for that I've seen so far is QA trying to go to the limits. For all practical purposes I'd expect 4 levels is *way* more than you'll ever need. Especially as the qemu usb hub supports usb 1.1 only. cheers, Gerd

On Fri, Jun 17, 2016 at 20:07:06 +0200, Ján Tomko wrote:
For some reason, we support up to four levels of nested USB devices in the guest.
This was explained by Gerd, so please fix it.
Add a test for a domain using all four and a negative test for a domain using five. ---
ACK

Make rewriting it easier. --- src/conf/domain_conf.c | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 69995cc..bcf832d 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -5062,11 +5062,30 @@ virDomainDeviceCcidAddressParseXML(xmlNodePtr node, } static int +virDomainDeviceUSBAddressParsePort(char *port) +{ + unsigned int p; + char *tmp; + + if ((virStrToLong_uip(port, &tmp, 10, &p) < 0 || (*tmp != '\0' && *tmp != '.')) || + (*tmp == '.' && (virStrToLong_ui(tmp + 1, &tmp, 10, &p) < 0 || (*tmp != '\0' && *tmp != '.'))) || + (*tmp == '.' && (virStrToLong_ui(tmp + 1, &tmp, 10, &p) < 0 || (*tmp != '\0' && *tmp != '.'))) || + (*tmp == '.' && (virStrToLong_ui(tmp + 1, &tmp, 10, &p) < 0 || (*tmp != '\0')))) + goto error; + + return 0; + + error: + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cannot parse <address> 'port' attribute")); + return -1; +} + +static int virDomainDeviceUSBAddressParseXML(xmlNodePtr node, virDomainDeviceUSBAddressPtr addr) { - char *port, *bus, *tmp; - unsigned int p; + char *port, *bus; int ret = -1; memset(addr, 0, sizeof(*addr)); @@ -5074,15 +5093,8 @@ virDomainDeviceUSBAddressParseXML(xmlNodePtr node, port = virXMLPropString(node, "port"); bus = virXMLPropString(node, "bus"); - if (port && - ((virStrToLong_uip(port, &tmp, 10, &p) < 0 || (*tmp != '\0' && *tmp != '.')) || - (*tmp == '.' && (virStrToLong_ui(tmp + 1, &tmp, 10, &p) < 0 || (*tmp != '\0' && *tmp != '.'))) || - (*tmp == '.' && (virStrToLong_ui(tmp + 1, &tmp, 10, &p) < 0 || (*tmp != '\0' && *tmp != '.'))) || - (*tmp == '.' && (virStrToLong_ui(tmp + 1, &tmp, 10, &p) < 0 || (*tmp != '\0'))))) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Cannot parse <address> 'port' attribute")); + if (port && virDomainDeviceUSBAddressParsePort(port) < 0) goto cleanup; - } addr->port = port; port = NULL; -- 2.7.3

We are done if the string ends and move to another nesting level if we find a dot. --- src/conf/device_conf.h | 3 +++ src/conf/domain_conf.c | 19 +++++++++++-------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/conf/device_conf.h b/src/conf/device_conf.h index 847564b..4903839 100644 --- a/src/conf/device_conf.h +++ b/src/conf/device_conf.h @@ -118,6 +118,9 @@ typedef struct _virDomainDeviceCcidAddress { unsigned int slot; } virDomainDeviceCcidAddress, *virDomainDeviceCcidAddressPtr; +/* chosen by fair dice roll */ +# define VIR_DOMAIN_DEVICE_USB_MAX_PORT_DEPTH 4 + typedef struct _virDomainDeviceUSBAddress { unsigned int bus; char *port; diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index bcf832d..dbb7d44 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -5065,17 +5065,20 @@ static int virDomainDeviceUSBAddressParsePort(char *port) { unsigned int p; - char *tmp; + char *tmp = port; + size_t i; - if ((virStrToLong_uip(port, &tmp, 10, &p) < 0 || (*tmp != '\0' && *tmp != '.')) || - (*tmp == '.' && (virStrToLong_ui(tmp + 1, &tmp, 10, &p) < 0 || (*tmp != '\0' && *tmp != '.'))) || - (*tmp == '.' && (virStrToLong_ui(tmp + 1, &tmp, 10, &p) < 0 || (*tmp != '\0' && *tmp != '.'))) || - (*tmp == '.' && (virStrToLong_ui(tmp + 1, &tmp, 10, &p) < 0 || (*tmp != '\0')))) - goto error; + for (i = 0; i < VIR_DOMAIN_DEVICE_USB_MAX_PORT_DEPTH; i++) { + if (virStrToLong_uip(tmp, &tmp, 10, &p) < 0) + break; - return 0; + if (*tmp == '\0') + return 0; + + if (*tmp == '.') + tmp++; + } - error: virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Cannot parse <address> 'port' attribute")); return -1; -- 2.7.3

On Fri, Jun 17, 2016 at 20:07:08 +0200, Ján Tomko wrote:
We are done if the string ends and move to another nesting level if we find a dot. --- src/conf/device_conf.h | 3 +++ src/conf/domain_conf.c | 19 +++++++++++-------- 2 files changed, 14 insertions(+), 8 deletions(-)
diff --git a/src/conf/device_conf.h b/src/conf/device_conf.h index 847564b..4903839 100644 --- a/src/conf/device_conf.h +++ b/src/conf/device_conf.h @@ -118,6 +118,9 @@ typedef struct _virDomainDeviceCcidAddress { unsigned int slot; } virDomainDeviceCcidAddress, *virDomainDeviceCcidAddressPtr;
+/* chosen by fair dice roll */
Remove this line.
+# define VIR_DOMAIN_DEVICE_USB_MAX_PORT_DEPTH 4 + typedef struct _virDomainDeviceUSBAddress { unsigned int bus; char *port;
ACK

In preparation to tracking which USB addresses are occupied. Introduce two helper functions for printing the port path as a string and appending it to a virBuffer. --- src/conf/device_conf.h | 2 +- src/conf/domain_addr.c | 26 ++++++++++++++++++++++++++ src/conf/domain_addr.h | 8 ++++++++ src/conf/domain_conf.c | 21 +++++++++------------ src/libvirt_private.syms | 2 ++ src/qemu/qemu_command.c | 3 ++- 6 files changed, 48 insertions(+), 14 deletions(-) diff --git a/src/conf/device_conf.h b/src/conf/device_conf.h index 4903839..7e1fc00 100644 --- a/src/conf/device_conf.h +++ b/src/conf/device_conf.h @@ -123,7 +123,7 @@ typedef struct _virDomainDeviceCcidAddress { typedef struct _virDomainDeviceUSBAddress { unsigned int bus; - char *port; + unsigned int port[VIR_DOMAIN_DEVICE_USB_MAX_PORT_DEPTH]; } virDomainDeviceUSBAddress, *virDomainDeviceUSBAddressPtr; typedef struct _virDomainDeviceSpaprVioAddress { diff --git a/src/conf/domain_addr.c b/src/conf/domain_addr.c index 794270d..1f5193c 100644 --- a/src/conf/domain_addr.c +++ b/src/conf/domain_addr.c @@ -1251,3 +1251,29 @@ virDomainVirtioSerialAddrRelease(virDomainVirtioSerialAddrSetPtr addrs, VIR_FREE(str); return ret; } + + +void +virDomainUSBAddressPortFormatBuf(virBufferPtr buf, + unsigned int *port) +{ + size_t i; + + for (i = 0; i < VIR_DOMAIN_DEVICE_USB_MAX_PORT_DEPTH; i++) { + if (port[i] == 0) + break; + virBufferAsprintf(buf, "%u.", port[i]); + } + virBufferTrim(buf, ".", -1); +} + + +char * +virDomainUSBAddressPortFormat(unsigned int *port) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + virDomainUSBAddressPortFormatBuf(&buf, port); + if (virBufferCheckError(&buf) < 0) + return NULL; + return virBufferContentAndReset(&buf); +} diff --git a/src/conf/domain_addr.h b/src/conf/domain_addr.h index f3eda89..8efd512 100644 --- a/src/conf/domain_addr.h +++ b/src/conf/domain_addr.h @@ -237,4 +237,12 @@ virDomainVirtioSerialAddrRelease(virDomainVirtioSerialAddrSetPtr addrs, virDomainDeviceInfoPtr info) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); +void +virDomainUSBAddressPortFormatBuf(virBufferPtr buf, + unsigned int *port) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); +char * +virDomainUSBAddressPortFormat(unsigned int *port) + ATTRIBUTE_NONNULL(1); + #endif /* __DOMAIN_ADDR_H__ */ diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index dbb7d44..a2cae44 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -32,6 +32,7 @@ #include "internal.h" #include "virerror.h" #include "datatypes.h" +#include "domain_addr.h" #include "domain_conf.h" #include "snapshot_conf.h" #include "viralloc.h" @@ -3321,8 +3322,6 @@ virDomainDeviceInfoCopy(virDomainDeviceInfoPtr dst, void virDomainDeviceInfoClear(virDomainDeviceInfoPtr info) { VIR_FREE(info->alias); - if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB) - VIR_FREE(info->addr.usb.port); memset(&info->addr, 0, sizeof(info->addr)); info->type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE; VIR_FREE(info->romfile); @@ -4828,9 +4827,10 @@ virDomainDeviceInfoFormat(virBufferPtr buf, break; case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB: - virBufferAsprintf(buf, " bus='%d' port='%s'", - info->addr.usb.bus, - info->addr.usb.port); + virBufferAsprintf(buf, " bus='%d' port='", + info->addr.usb.bus); + virDomainUSBAddressPortFormatBuf(buf, info->addr.usb.port); + virBufferAddLit(buf, "'"); break; case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_SPAPRVIO: @@ -5062,14 +5062,14 @@ virDomainDeviceCcidAddressParseXML(xmlNodePtr node, } static int -virDomainDeviceUSBAddressParsePort(char *port) +virDomainDeviceUSBAddressParsePort(virDomainDeviceUSBAddressPtr addr, + char *port) { - unsigned int p; char *tmp = port; size_t i; for (i = 0; i < VIR_DOMAIN_DEVICE_USB_MAX_PORT_DEPTH; i++) { - if (virStrToLong_uip(tmp, &tmp, 10, &p) < 0) + if (virStrToLong_uip(tmp, &tmp, 10, &addr->port[i]) < 0) break; if (*tmp == '\0') @@ -5096,12 +5096,9 @@ virDomainDeviceUSBAddressParseXML(xmlNodePtr node, port = virXMLPropString(node, "port"); bus = virXMLPropString(node, "bus"); - if (port && virDomainDeviceUSBAddressParsePort(port) < 0) + if (port && virDomainDeviceUSBAddressParsePort(addr, port) < 0) goto cleanup; - addr->port = port; - port = NULL; - if (bus && virStrToLong_uip(bus, NULL, 10, &addr->bus) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index f6a2c1b..6ac9952 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -109,6 +109,8 @@ virDomainPCIAddressSetGrow; virDomainPCIAddressSlotInUse; virDomainPCIAddressValidate; virDomainPCIControllerModelToConnectType; +virDomainUSBAddressPortFormat; +virDomainUSBAddressPortFormatBuf; virDomainVirtioSerialAddrAssign; virDomainVirtioSerialAddrAutoAssign; virDomainVirtioSerialAddrIsComplete; diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 63b2672..c0ab2ea 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -375,7 +375,8 @@ qemuBuildDeviceAddressStr(virBufferPtr buf, VIR_DOMAIN_CONTROLLER_TYPE_USB, info->addr.usb.bus))) goto cleanup; - virBufferAsprintf(buf, ",bus=%s.0,port=%s", contAlias, info->addr.usb.port); + virBufferAsprintf(buf, ",bus=%s.0,port=", contAlias); + virDomainUSBAddressPortFormatBuf(buf, info->addr.usb.port); } else if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_SPAPRVIO) { if (info->addr.spaprvio.has_reg) virBufferAsprintf(buf, ",reg=0x%llx", info->addr.spaprvio.reg); -- 2.7.3

So far this is only useful for recalculating NUMA memory size, which this function cannot parse. This will let us generate USB addresses based on this flag. --- src/qemu/qemu_parse_command.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/qemu/qemu_parse_command.c b/src/qemu/qemu_parse_command.c index 927bd79..1d54a68 100644 --- a/src/qemu/qemu_parse_command.c +++ b/src/qemu/qemu_parse_command.c @@ -2626,8 +2626,7 @@ qemuParseCommandLine(virCapsPtr caps, VIR_FREE(nics); - if (virDomainDefPostParse(def, caps, VIR_DOMAIN_DEF_PARSE_ABI_UPDATE, - xmlopt) < 0) + if (virDomainDefPostParse(def, caps, 0, xmlopt) < 0) goto error; if (cmd->num_args || cmd->num_env) { -- 2.7.3

On Fri, Jun 17, 2016 at 20:07:10 +0200, Ján Tomko wrote:
So far this is only useful for recalculating NUMA memory size, which this function cannot parse.
This will let us generate USB addresses based on this flag. --- src/qemu/qemu_parse_command.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/src/qemu/qemu_parse_command.c b/src/qemu/qemu_parse_command.c index 927bd79..1d54a68 100644 --- a/src/qemu/qemu_parse_command.c +++ b/src/qemu/qemu_parse_command.c @@ -2626,8 +2626,7 @@ qemuParseCommandLine(virCapsPtr caps,
VIR_FREE(nics);
- if (virDomainDefPostParse(def, caps, VIR_DOMAIN_DEF_PARSE_ABI_UPDATE, - xmlopt) < 0) + if (virDomainDefPostParse(def, caps, 0, xmlopt) < 0) goto error;
Additionally ABI update is not desired in case when this function is used in the qemu-attach API. Thankfully that doesn't work in most cases either so I didn't manage to break much. ACK

Pass 'true' if we are not dealing with a migration. --- src/qemu/qemu_domain.c | 3 ++- src/qemu/qemu_domain_address.c | 3 ++- src/qemu/qemu_domain_address.h | 3 ++- src/qemu/qemu_process.c | 7 ++++--- tests/qemuhotplugtest.c | 2 +- 5 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 1eb5644..a4c6606 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -2426,12 +2426,13 @@ qemuDomainDefAssignAddresses(virDomainDef *def, virQEMUDriverPtr driver = opaque; virQEMUCapsPtr qemuCaps = NULL; int ret = -1; + bool newDomain = parseFlags & VIR_DOMAIN_DEF_PARSE_ABI_UPDATE; if (!(qemuCaps = virQEMUCapsCacheLookup(driver->qemuCapsCache, def->emulator))) goto cleanup; - if (qemuDomainAssignAddresses(def, qemuCaps, NULL) < 0) + if (qemuDomainAssignAddresses(def, qemuCaps, NULL, newDomain) < 0) goto cleanup; ret = 0; diff --git a/src/qemu/qemu_domain_address.c b/src/qemu/qemu_domain_address.c index ca3adc0..1382a53 100644 --- a/src/qemu/qemu_domain_address.c +++ b/src/qemu/qemu_domain_address.c @@ -1622,7 +1622,8 @@ qemuDomainAssignPCIAddresses(virDomainDefPtr def, int qemuDomainAssignAddresses(virDomainDefPtr def, virQEMUCapsPtr qemuCaps, - virDomainObjPtr obj) + virDomainObjPtr obj, + bool newDomain ATTRIBUTE_UNUSED) { if (qemuDomainAssignVirtioSerialAddresses(def, obj) < 0) return -1; diff --git a/src/qemu/qemu_domain_address.h b/src/qemu/qemu_domain_address.h index 50019b8..ee326d7 100644 --- a/src/qemu/qemu_domain_address.h +++ b/src/qemu/qemu_domain_address.h @@ -33,7 +33,8 @@ int qemuDomainSetSCSIControllerModel(const virDomainDef *def, int qemuDomainAssignAddresses(virDomainDefPtr def, virQEMUCapsPtr qemuCaps, - virDomainObjPtr obj) + virDomainObjPtr obj, + bool newDomain) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); void qemuDomainReleaseDeviceAddress(virDomainObjPtr vm, diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 373e7a9..ab0be0c 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -3291,7 +3291,7 @@ qemuProcessReconnect(void *opaque) goto cleanup; } - if ((qemuDomainAssignAddresses(obj->def, priv->qemuCaps, obj)) < 0) + if ((qemuDomainAssignAddresses(obj->def, priv->qemuCaps, obj, false)) < 0) goto error; /* if domain requests security driver we haven't loaded, report error, but @@ -4878,7 +4878,8 @@ qemuProcessPrepareDomain(virConnectPtr conn, * use in hotplug */ VIR_DEBUG("Assigning domain PCI addresses"); - if ((qemuDomainAssignAddresses(vm->def, priv->qemuCaps, vm)) < 0) + if ((qemuDomainAssignAddresses(vm->def, priv->qemuCaps, vm, + !!(flags & VIR_QEMU_PROCESS_START_NEW))) < 0) goto cleanup; if (qemuAssignDeviceAliases(vm->def, priv->qemuCaps) < 0) @@ -6066,7 +6067,7 @@ int qemuProcessAttach(virConnectPtr conn ATTRIBUTE_UNUSED, * use in hotplug */ VIR_DEBUG("Assigning domain PCI addresses"); - if ((qemuDomainAssignAddresses(vm->def, priv->qemuCaps, vm)) < 0) + if ((qemuDomainAssignAddresses(vm->def, priv->qemuCaps, vm, false)) < 0) goto error; if ((timestamp = virTimeStringNow()) == NULL) diff --git a/tests/qemuhotplugtest.c b/tests/qemuhotplugtest.c index 13055ab..91bf331 100644 --- a/tests/qemuhotplugtest.c +++ b/tests/qemuhotplugtest.c @@ -86,7 +86,7 @@ qemuHotplugCreateObjects(virDomainXMLOptionPtr xmlopt, VIR_DOMAIN_DEF_PARSE_INACTIVE))) goto cleanup; - if (qemuDomainAssignAddresses((*vm)->def, priv->qemuCaps, *vm) < 0) + if (qemuDomainAssignAddresses((*vm)->def, priv->qemuCaps, *vm, true) < 0) goto cleanup; if (qemuAssignDeviceAliases((*vm)->def, priv->qemuCaps) < 0) -- 2.7.3

On Fri, Jun 17, 2016 at 20:07:11 +0200, Ján Tomko wrote:
Pass 'true' if we are not dealing with a migration. --- src/qemu/qemu_domain.c | 3 ++- src/qemu/qemu_domain_address.c | 3 ++- src/qemu/qemu_domain_address.h | 3 ++- src/qemu/qemu_process.c | 7 ++++--- tests/qemuhotplugtest.c | 2 +- 5 files changed, 11 insertions(+), 7 deletions(-)
ACK

A new type to track USB addresses. Every <controller type='usb' index='i'/> is represented by as a virDomainUSBAddressHub at buses[i]. Each of these hubs has up to 'nports' ports. If a port is occupied, it has the corresponding bit set in the 'ports' bitmap, e.g. port 1 would have the 0th bit set. If there is a hub on this port, then hubs[i] will point to this hub. --- src/conf/domain_addr.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ src/conf/domain_addr.h | 22 ++++++++++++++++++++++ src/libvirt_private.syms | 2 ++ 3 files changed, 70 insertions(+) diff --git a/src/conf/domain_addr.c b/src/conf/domain_addr.c index 1f5193c..a43657b 100644 --- a/src/conf/domain_addr.c +++ b/src/conf/domain_addr.c @@ -1277,3 +1277,49 @@ virDomainUSBAddressPortFormat(unsigned int *port) return NULL; return virBufferContentAndReset(&buf); } + + +virDomainUSBAddressSetPtr +virDomainUSBAddressSetCreate(void) +{ + virDomainUSBAddressSetPtr addrs; + + if (VIR_ALLOC(addrs) < 0) + return NULL; + + return addrs; +} + + +static void +virDomainUSBAddressHubFree(virDomainUSBAddressHubPtr hub) +{ + size_t i; + + if (!hub) + return; + + for (i = 0; i < hub->nports; i++) { + if (hub->hubs[i]) + virDomainUSBAddressHubFree(hub->hubs[i]); + } + virBitmapFree(hub->ports); + VIR_FREE(hub); +} + + +void +virDomainUSBAddressSetFree(virDomainUSBAddressSetPtr addrs) +{ + size_t i; + + if (!addrs) + return; + + for (i = 0; i < addrs->nbuses; i++) { + if (addrs->buses[i]) + virDomainUSBAddressHubFree(addrs->buses[i]); + } + VIR_FREE(addrs->buses); + VIR_FREE(addrs); +} diff --git a/src/conf/domain_addr.h b/src/conf/domain_addr.h index 8efd512..51bbb61 100644 --- a/src/conf/domain_addr.h +++ b/src/conf/domain_addr.h @@ -245,4 +245,26 @@ char * virDomainUSBAddressPortFormat(unsigned int *port) ATTRIBUTE_NONNULL(1); +typedef struct _virDomainUSBAddressHub virDomainUSBAddressHub; +typedef virDomainUSBAddressHub *virDomainUSBAddressHubPtr; +struct _virDomainUSBAddressHub { + /* indexes are shifted by one: + * ports[0] represents port 1, because ports are numbered from 1 */ + virBitmapPtr ports; + size_t nports; + virDomainUSBAddressHubPtr *hubs; +}; + +struct _virDomainUSBAddressSet { + /* every <controller type='usb' index='i'> is represented + * as a hub at buses[i] */ + virDomainUSBAddressHubPtr *buses; + size_t nbuses; +}; +typedef struct _virDomainUSBAddressSet virDomainUSBAddressSet; +typedef virDomainUSBAddressSet *virDomainUSBAddressSetPtr; + +virDomainUSBAddressSetPtr virDomainUSBAddressSetCreate(void); +void virDomainUSBAddressSetFree(virDomainUSBAddressSetPtr addrs); + #endif /* __DOMAIN_ADDR_H__ */ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 6ac9952..58db0ca 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -111,6 +111,8 @@ virDomainPCIAddressValidate; virDomainPCIControllerModelToConnectType; virDomainUSBAddressPortFormat; virDomainUSBAddressPortFormatBuf; +virDomainUSBAddressSetCreate; +virDomainUSBAddressSetFree; virDomainVirtioSerialAddrAssign; virDomainVirtioSerialAddrAutoAssign; virDomainVirtioSerialAddrIsComplete; -- 2.7.3

Walk through all the usb controllers in the domain definition and create the corresponding structures in the virDomainUSBAddressSet. --- src/conf/domain_addr.c | 119 +++++++++++++++++++++++++++++++++++++++++++++++ src/conf/domain_addr.h | 4 ++ src/libvirt_private.syms | 1 + 3 files changed, 124 insertions(+) diff --git a/src/conf/domain_addr.c b/src/conf/domain_addr.c index a43657b..58159ef 100644 --- a/src/conf/domain_addr.c +++ b/src/conf/domain_addr.c @@ -1323,3 +1323,122 @@ virDomainUSBAddressSetFree(virDomainUSBAddressSetPtr addrs) VIR_FREE(addrs->buses); VIR_FREE(addrs); } + + +static size_t +virDomainUSBAddressControllerModelToPorts(virDomainControllerDefPtr cont) +{ + int model = cont->model; + + if (model == -1) + model = VIR_DOMAIN_CONTROLLER_MODEL_USB_PIIX3_UHCI; + + switch ((virDomainControllerModelUSB) model) { + case VIR_DOMAIN_CONTROLLER_MODEL_USB_PIIX3_UHCI: + case VIR_DOMAIN_CONTROLLER_MODEL_USB_PIIX4_UHCI: + case VIR_DOMAIN_CONTROLLER_MODEL_USB_VT82C686B_UHCI: + return 2; + + case VIR_DOMAIN_CONTROLLER_MODEL_USB_EHCI: + case VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_EHCI1: + return 6; + + case VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_UHCI1: + case VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_UHCI2: + case VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_UHCI3: + /* These have two ports each and are used to provide USB1.1 + * ports while ICH9_EHCI1 provides 6 USB2.0 ports. + * Ignore these since we will add the EHCI1 too. */ + return 0; + + case VIR_DOMAIN_CONTROLLER_MODEL_USB_PCI_OHCI: + return 3; + + case VIR_DOMAIN_CONTROLLER_MODEL_USB_NEC_XHCI: + return 4; + + case VIR_DOMAIN_CONTROLLER_MODEL_USB_NONE: + case VIR_DOMAIN_CONTROLLER_MODEL_USB_LAST: + /* yoda */ + break; + } + return 0; +} + + +static virDomainUSBAddressHubPtr +virDomainUSBAddressHubNew(size_t nports) +{ + virDomainUSBAddressHubPtr hub = NULL, ret = NULL; + + if (VIR_ALLOC(hub) < 0) + goto cleanup; + + if (!(hub->ports = virBitmapNew(nports))) + goto cleanup; + + if (VIR_ALLOC_N(hub->hubs, nports) < 0) + goto cleanup; + hub->nports = nports; + + ret = hub; + hub = NULL; + cleanup: + virDomainUSBAddressHubFree(hub); + return ret; +} + + +static int +virDomainUSBAddressSetAddController(virDomainUSBAddressSetPtr addrs, + virDomainControllerDefPtr cont) +{ + size_t nports = virDomainUSBAddressControllerModelToPorts(cont); + virDomainUSBAddressHubPtr hub = NULL; + int ret = -1; + + VIR_DEBUG("Adding a USB controller model=%s with %zu ports", + virDomainControllerModelUSBTypeToString(cont->model), + nports); + + /* Skip UHCI{1,2,3} companions; only add the EHCI1 */ + if (nports == 0) + return 0; + + if (addrs->nbuses <= cont->idx) { + if (VIR_EXPAND_N(addrs->buses, addrs->nbuses, cont->idx - addrs->nbuses + 1) < 0) + goto cleanup; + } else if (addrs->buses[cont->idx]) { + virReportError(VIR_ERR_XML_ERROR, + _("Duplicate USB controllers with index %u"), + cont->idx); + goto cleanup; + } + + if (!(hub = virDomainUSBAddressHubNew(nports))) + goto cleanup; + + addrs->buses[cont->idx] = hub; + hub = NULL; + + ret = 0; + cleanup: + virDomainUSBAddressHubFree(hub); + return ret; +} + + +int virDomainUSBAddressSetAddControllers(virDomainUSBAddressSetPtr addrs, + virDomainDefPtr def) +{ + size_t i; + + for (i = 0; i < def->ncontrollers; i++) { + virDomainControllerDefPtr cont = def->controllers[i]; + if (cont->type == VIR_DOMAIN_CONTROLLER_TYPE_USB) { + if (virDomainUSBAddressSetAddController(addrs, cont) < 0) + return -1; + } + } + return 0; +} diff --git a/src/conf/domain_addr.h b/src/conf/domain_addr.h index 51bbb61..693c5ee 100644 --- a/src/conf/domain_addr.h +++ b/src/conf/domain_addr.h @@ -265,6 +265,10 @@ typedef struct _virDomainUSBAddressSet virDomainUSBAddressSet; typedef virDomainUSBAddressSet *virDomainUSBAddressSetPtr; virDomainUSBAddressSetPtr virDomainUSBAddressSetCreate(void); + +int virDomainUSBAddressSetAddControllers(virDomainUSBAddressSetPtr addrs, + virDomainDefPtr def) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); void virDomainUSBAddressSetFree(virDomainUSBAddressSetPtr addrs); #endif /* __DOMAIN_ADDR_H__ */ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 58db0ca..63d1dd9 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -111,6 +111,7 @@ virDomainPCIAddressValidate; virDomainPCIControllerModelToConnectType; virDomainUSBAddressPortFormat; virDomainUSBAddressPortFormatBuf; +virDomainUSBAddressSetAddControllers; virDomainUSBAddressSetCreate; virDomainUSBAddressSetFree; virDomainVirtioSerialAddrAssign; -- 2.7.3

Hi,
+ case VIR_DOMAIN_CONTROLLER_MODEL_USB_NEC_XHCI: + return 4;
That one is configurable via "ports=...": <controller type='usb' index='3' model='nec-xhci' ports='8'> cheers, Gerd

Walk through all the usb hubs in the domain definition and create the corresponding structures in the virDomainUSBAddressSet. --- src/conf/domain_addr.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/src/conf/domain_addr.c b/src/conf/domain_addr.c index 58159ef..50d23b4 100644 --- a/src/conf/domain_addr.c +++ b/src/conf/domain_addr.c @@ -1428,6 +1428,97 @@ virDomainUSBAddressSetAddController(virDomainUSBAddressSetPtr addrs, } +static ssize_t +virDomainUSBAddressGetLastIdx(virDomainDeviceInfoPtr info) +{ + ssize_t i; + for (i = VIR_DOMAIN_DEVICE_USB_MAX_PORT_DEPTH - 1; i > 0; i--) { + if (info->addr.usb.port[i] != 0) + break; + } + return i; +} + + +/* Find the USBAddressHub structure representing the hub/controller + * that corresponds to the bus/port path specified by info. + * Returns the index of the requested port in targetIdx. + */ +static virDomainUSBAddressHubPtr +virDomainUSBAddressFindPort(virDomainUSBAddressSetPtr addrs, + virDomainDeviceInfoPtr info, + int *targetIdx) +{ + virDomainUSBAddressHubPtr hub = NULL; + ssize_t i, lastIdx; + + if (info->addr.usb.bus >= addrs->nbuses || + !addrs->buses[info->addr.usb.bus]) { + virReportError(VIR_ERR_XML_ERROR, _("Missing USB bus %u"), + info->addr.usb.bus); + return NULL; + } + hub = addrs->buses[info->addr.usb.bus]; + + lastIdx = virDomainUSBAddressGetLastIdx(info); + + for (i = 0; i < lastIdx; i++) { + /* ports are numbered from 1 */ + int portIdx = info->addr.usb.port[i] - 1; + + if (hub->nports <= portIdx) { + virReportError(VIR_ERR_XML_ERROR, + _("port %u out of range"), + info->addr.usb.port[i]); + return NULL; + } + hub = hub->hubs[portIdx]; + } + + *targetIdx = info->addr.usb.port[lastIdx] - 1; + return hub; +} + + +#define VIR_DOMAIN_USB_HUB_PORTS 8 + +static int +virDomainUSBAddressSetAddHub(virDomainUSBAddressSetPtr addrs, + virDomainHubDefPtr hub) +{ + virDomainUSBAddressHubPtr targetHub = NULL, newHub = NULL; + int ret = -1; + int portIdx; + + VIR_DEBUG("Adding a USB hub with 8 ports"); + + if (!(newHub = virDomainUSBAddressHubNew(VIR_DOMAIN_USB_HUB_PORTS))) + goto cleanup; + + if (hub->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("Wrong address type for USB hub")); + goto cleanup; + } + + if (!(targetHub = virDomainUSBAddressFindPort(addrs, &(hub->info), &portIdx))) + goto cleanup; + + if (targetHub->hubs[portIdx]) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("Dupicate USB hub")); + goto cleanup; + } + targetHub->hubs[portIdx] = newHub; + newHub = NULL; + + ret = 0; + cleanup: + virDomainUSBAddressHubFree(newHub); + return ret; +} + + int virDomainUSBAddressSetAddControllers(virDomainUSBAddressSetPtr addrs, virDomainDefPtr def) { @@ -1440,5 +1531,13 @@ int virDomainUSBAddressSetAddControllers(virDomainUSBAddressSetPtr addrs, return -1; } } + + for (i = 0; i < def->nhubs; i++) { + virDomainHubDefPtr hub = def->hubs[i]; + if (hub->type == VIR_DOMAIN_HUB_TYPE_USB) { + if (virDomainUSBAddressSetAddHub(addrs, hub) < 0) + return -1; + } + } return 0; } -- 2.7.3

Check if they fit on the USB controllers the domain has, and error out if two devices try to use the same address. --- src/conf/domain_addr.c | 48 ++++++++++++++++++++++ src/conf/domain_addr.h | 6 +++ src/libvirt_private.syms | 1 + src/qemu/qemu_domain.h | 1 + src/qemu/qemu_domain_address.c | 37 ++++++++++++++++- .../qemuxml2argv-usb-hub-conflict.xml | 22 ++++++++++ tests/qemuxml2argvtest.c | 3 ++ 7 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-usb-hub-conflict.xml diff --git a/src/conf/domain_addr.c b/src/conf/domain_addr.c index 50d23b4..0706e6e 100644 --- a/src/conf/domain_addr.c +++ b/src/conf/domain_addr.c @@ -1495,6 +1495,14 @@ virDomainUSBAddressSetAddHub(virDomainUSBAddressSetPtr addrs, if (!(newHub = virDomainUSBAddressHubNew(VIR_DOMAIN_USB_HUB_PORTS))) goto cleanup; + /* TODO: remove in the next patch. + * Skip hubs with no address to pass 'make check' until we start + * auto-assigning their addresses */ + if (hub->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) { + ret = 0; + goto cleanup; + } + if (hub->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Wrong address type for USB hub")); @@ -1541,3 +1549,43 @@ int virDomainUSBAddressSetAddControllers(virDomainUSBAddressSetPtr addrs, } return 0; } + + +int +virDomainUSBAddressReserve(virDomainDefPtr def ATTRIBUTE_UNUSED, + virDomainDeviceDefPtr dev ATTRIBUTE_UNUSED, + virDomainDeviceInfoPtr info, + void *data) +{ + virDomainUSBAddressSetPtr addrs = data; + virDomainUSBAddressHubPtr targetHub = NULL; + char *portstr = NULL; + int ret = -1; + int targetPort; + + if (info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB) + return 0; + + portstr = virDomainUSBAddressPortFormat(info->addr.usb.port); + if (!portstr) + goto cleanup; + VIR_DEBUG("Reserving USB addr bus=%u port=%s", info->addr.usb.bus, portstr); + + if (!(targetHub = virDomainUSBAddressFindPort(addrs, info, &targetPort))) + goto cleanup; + + if (virBitmapIsBitSet(targetHub->ports, targetPort)) { + virReportError(VIR_ERR_XML_ERROR, + _("Duplicate USB address %s"), + portstr); + goto cleanup; + } + + ignore_value(virBitmapSetBit(targetHub->ports, targetPort)); + + ret = 0; + + cleanup: + VIR_FREE(portstr); + return ret; +} diff --git a/src/conf/domain_addr.h b/src/conf/domain_addr.h index 693c5ee..502b1a2 100644 --- a/src/conf/domain_addr.h +++ b/src/conf/domain_addr.h @@ -271,4 +271,10 @@ int virDomainUSBAddressSetAddControllers(virDomainUSBAddressSetPtr addrs, ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); void virDomainUSBAddressSetFree(virDomainUSBAddressSetPtr addrs); +int +virDomainUSBAddressReserve(virDomainDefPtr def, + virDomainDeviceDefPtr dev, + virDomainDeviceInfoPtr info, + void *data) + ATTRIBUTE_NONNULL(3) ATTRIBUTE_NONNULL(4); #endif /* __DOMAIN_ADDR_H__ */ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 63d1dd9..765ac0f 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -111,6 +111,7 @@ virDomainPCIAddressValidate; virDomainPCIControllerModelToConnectType; virDomainUSBAddressPortFormat; virDomainUSBAddressPortFormatBuf; +virDomainUSBAddressReserve; virDomainUSBAddressSetAddControllers; virDomainUSBAddressSetCreate; virDomainUSBAddressSetFree; diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index 2443e97..e2141ed 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -190,6 +190,7 @@ struct _qemuDomainObjPrivate { virDomainPCIAddressSetPtr pciaddrs; virDomainCCWAddressSetPtr ccwaddrs; virDomainVirtioSerialAddrSetPtr vioserialaddrs; + virDomainUSBAddressSetPtr usbaddrs; virQEMUCapsPtr qemuCaps; char *lockState; diff --git a/src/qemu/qemu_domain_address.c b/src/qemu/qemu_domain_address.c index 1382a53..c5f4150 100644 --- a/src/qemu/qemu_domain_address.c +++ b/src/qemu/qemu_domain_address.c @@ -1619,11 +1619,43 @@ qemuDomainAssignPCIAddresses(virDomainDefPtr def, } +static int +qemuDomainAssignUSBAddresses(virDomainDefPtr def, + virDomainObjPtr obj) +{ + int ret = -1; + virDomainUSBAddressSetPtr addrs = NULL; + qemuDomainObjPrivatePtr priv = NULL; + + if (!(addrs = virDomainUSBAddressSetCreate())) + goto cleanup; + + if (virDomainUSBAddressSetAddControllers(addrs, def) < 0) + goto cleanup; + + if (virDomainDeviceInfoIterate(def, virDomainUSBAddressReserve, addrs) < 0) + goto cleanup; + + VIR_DEBUG("Existing USB addresses have been reserved"); + + if (obj && obj->privateData) { + priv = obj->privateData; + priv->usbaddrs = addrs; + addrs = NULL; + } + ret = 0; + + cleanup: + virDomainUSBAddressSetFree(addrs); + return ret; +} + + int qemuDomainAssignAddresses(virDomainDefPtr def, virQEMUCapsPtr qemuCaps, virDomainObjPtr obj, - bool newDomain ATTRIBUTE_UNUSED) + bool newDomain) { if (qemuDomainAssignVirtioSerialAddresses(def, obj) < 0) return -1; @@ -1639,6 +1671,9 @@ qemuDomainAssignAddresses(virDomainDefPtr def, if (qemuDomainAssignPCIAddresses(def, qemuCaps, obj) < 0) return -1; + if (newDomain && qemuDomainAssignUSBAddresses(def, obj) < 0) + return -1; + return 0; } diff --git a/tests/qemuxml2argvdata/qemuxml2argv-usb-hub-conflict.xml b/tests/qemuxml2argvdata/qemuxml2argv-usb-hub-conflict.xml new file mode 100644 index 0000000..9a48ba0 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-usb-hub-conflict.xml @@ -0,0 +1,22 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219136</memory> + <currentMemory unit='KiB'>219136</currentMemory> + <vcpu placement='static'>1</vcpu> + <os> + <type arch='i686' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <devices> + <emulator>/usr/bin/qemu</emulator> + <controller type='usb' index='0'/> + <memballoon model='virtio'/> + <hub type='usb'> + <address type='usb' bus='0' port='1'/> + </hub> + <input type='mouse' bus='usb'> + <address type='usb' bus='0' port='1'/> + </input> + </devices> +</domain> diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index 2a8f6c4..1a01240 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -1159,6 +1159,9 @@ mymain(void) DO_TEST("usb-hub", 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); DO_TEST("usb-ports", QEMU_CAPS_CHARDEV, QEMU_CAPS_USB_HUB, QEMU_CAPS_NODEFCONFIG); -- 2.7.3

Automatically assign addresses to USB devices. Just like reserving, this is only done for newly defined domains. https://bugzilla.redhat.com/show_bug.cgi?id=1215968 --- src/conf/domain_addr.c | 109 +++++++++++++++++++-- src/conf/domain_addr.h | 9 ++ src/conf/domain_conf.c | 77 +++++++++++++++ src/conf/domain_conf.h | 6 ++ src/libvirt_private.syms | 3 + src/qemu/qemu_domain_address.c | 36 +++++++ ...otplug-console-compat-2-live+console-virtio.xml | 1 + .../qemuxml2argvdata/qemuxml2argv-bios-nvram.args | 2 +- tests/qemuxml2argvdata/qemuxml2argv-bios.args | 2 +- .../qemuxml2argv-console-compat-2-live.xml | 1 + .../qemuxml2argv-console-compat-2.xml | 4 +- .../qemuxml2argv-controller-order.args | 8 +- .../qemuxml2argv-disk-usb-device-removable.args | 3 +- .../qemuxml2argv-disk-usb-device.args | 2 +- .../qemuxml2argv-graphics-spice-timeout.args | 2 +- ...muxml2argv-hostdev-usb-address-device-boot.args | 2 +- .../qemuxml2argv-hostdev-usb-address-device.args | 2 +- .../qemuxml2argv-hostdev-usb-address.args | 2 +- .../qemuxml2argv-hugepages-numa.args | 2 +- .../qemuxml2argv-input-usbmouse.args | 2 +- .../qemuxml2argv-input-usbtablet.args | 2 +- .../qemuxml2argv-pseries-usb-kbd.args | 2 +- .../qemuxml2argv-serial-spiceport.args | 2 +- .../qemuxml2argv-smartcard-controller.args | 2 +- .../qemuxml2argv-smartcard-host-certificates.args | 2 +- .../qemuxml2argv-smartcard-host.args | 2 +- ...emuxml2argv-smartcard-passthrough-spicevmc.args | 2 +- .../qemuxml2argv-smartcard-passthrough-tcp.args | 2 +- .../qemuxml2argv-sound-device.args | 2 +- .../qemuxml2argv-usb-port-autoassign.args | 28 ++++++ .../qemuxml2argv-usb-port-autoassign.xml | 27 +++++ tests/qemuxml2argvtest.c | 3 + 32 files changed, 320 insertions(+), 31 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-usb-port-autoassign.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-usb-port-autoassign.xml diff --git a/src/conf/domain_addr.c b/src/conf/domain_addr.c index 0706e6e..e089763 100644 --- a/src/conf/domain_addr.c +++ b/src/conf/domain_addr.c @@ -1495,13 +1495,8 @@ virDomainUSBAddressSetAddHub(virDomainUSBAddressSetPtr addrs, if (!(newHub = virDomainUSBAddressHubNew(VIR_DOMAIN_USB_HUB_PORTS))) goto cleanup; - /* TODO: remove in the next patch. - * Skip hubs with no address to pass 'make check' until we start - * auto-assigning their addresses */ - if (hub->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) { - ret = 0; + if (virDomainUSBAddressEnsure(addrs, &hub->info) < 0) goto cleanup; - } if (hub->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB) { virReportError(VIR_ERR_XML_ERROR, "%s", @@ -1551,9 +1546,89 @@ int virDomainUSBAddressSetAddControllers(virDomainUSBAddressSetPtr addrs, } +static int +virDomainUSBAddressFindFreePort(virDomainUSBAddressHubPtr hub, + unsigned int *portpath, + unsigned int level) +{ + unsigned int port; + ssize_t portIdx; + size_t i; + + /* Look for free ports on the current hub */ + if ((portIdx = virBitmapNextClearBit(hub->ports, -1)) >= 0) { + port = portIdx + 1; + VIR_DEBUG("Found a free port %u at level %u", port, level); + portpath[level] = port; + return 0; + } + + VIR_DEBUG("No ports found on hub %p, trying the hubs on it", hub); + + if (level >= VIR_DOMAIN_DEVICE_USB_MAX_PORT_DEPTH - 1) + return -1; + + /* Recursively search through the ports that contain another hub */ + for (i = 0; i < hub->nports; i++) { + if (!hub->hubs[i]) + continue; + + port = i + 1; + VIR_DEBUG("Looking at USB hub at level: %u port: %u", level, port); + if (virDomainUSBAddressFindFreePort(hub->hubs[i], portpath, + level + 1) < 0) + continue; + + portpath[level] = port; + return 0; + } + return -1; +} + + +int +virDomainUSBAddressAssign(virDomainUSBAddressSetPtr addrs, + virDomainDeviceInfoPtr info) +{ + unsigned int portpath[VIR_DOMAIN_DEVICE_USB_MAX_PORT_DEPTH] = { 0 }; + char *portstr = NULL; + size_t i; + int ret = -1; + + for (i = 0; i < addrs->nbuses; i++) { + virDomainUSBAddressHubPtr hub = addrs->buses[i]; + if (!hub) + continue; + + if (virDomainUSBAddressFindFreePort(hub, portpath, 0) < 0) + continue; + + /* we found a free port */ + if (!(portstr = virDomainUSBAddressPortFormat(portpath))) + goto cleanup; + + info->type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB; + info->addr.usb.bus = i; + memcpy(info->addr.usb.port, portpath, sizeof(portpath)); + VIR_DEBUG("Assigning USB addr bus=%u port=%s", + info->addr.usb.bus, portstr); + VIR_FREE(portstr); + if (virDomainUSBAddressReserve(NULL, NULL, info, addrs) < 0) + goto cleanup; + + return 0; + } + + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("No free USB ports")); + cleanup: + VIR_FREE(portstr); + return ret; +} + + int virDomainUSBAddressReserve(virDomainDefPtr def ATTRIBUTE_UNUSED, - virDomainDeviceDefPtr dev ATTRIBUTE_UNUSED, + virDomainDeviceDefPtr dev, virDomainDeviceInfoPtr info, void *data) { @@ -1563,6 +1638,10 @@ virDomainUSBAddressReserve(virDomainDefPtr def ATTRIBUTE_UNUSED, int ret = -1; int targetPort; + /* USB hubs are dealt with in virDomainUSBAddressSetAddHub */ + if (dev && dev->type == VIR_DOMAIN_DEVICE_HUB) + return 0; + if (info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB) return 0; @@ -1589,3 +1668,19 @@ virDomainUSBAddressReserve(virDomainDefPtr def ATTRIBUTE_UNUSED, VIR_FREE(portstr); return ret; } + + +int +virDomainUSBAddressEnsure(virDomainUSBAddressSetPtr addrs, + virDomainDeviceInfoPtr info) +{ + if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) { + if (virDomainUSBAddressAssign(addrs, info) < 0) + return -1; + } else if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB) { + if (virDomainUSBAddressReserve(NULL, NULL, info, addrs) < 0) + return -1; + } + + return 0; +} diff --git a/src/conf/domain_addr.h b/src/conf/domain_addr.h index 502b1a2..ce640fb 100644 --- a/src/conf/domain_addr.h +++ b/src/conf/domain_addr.h @@ -272,9 +272,18 @@ int virDomainUSBAddressSetAddControllers(virDomainUSBAddressSetPtr addrs, void virDomainUSBAddressSetFree(virDomainUSBAddressSetPtr addrs); int +virDomainUSBAddressAssign(virDomainUSBAddressSetPtr addrs, + virDomainDeviceInfoPtr info) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); + +int virDomainUSBAddressReserve(virDomainDefPtr def, virDomainDeviceDefPtr dev, virDomainDeviceInfoPtr info, void *data) ATTRIBUTE_NONNULL(3) ATTRIBUTE_NONNULL(4); +int +virDomainUSBAddressEnsure(virDomainUSBAddressSetPtr addrs, + virDomainDeviceInfoPtr info) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); #endif /* __DOMAIN_ADDR_H__ */ diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index a2cae44..2f46c72 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -24010,6 +24010,83 @@ virDomainSmartcardDefForeach(virDomainDefPtr def, } +int +virDomainUSBDeviceDefForeach(virDomainDefPtr def, + virDomainUSBDeviceDefIterator iter, + void *opaque) +{ + size_t i; + + /* usb-hub */ + for (i = 0; i < def->nhubs; i++) { + virDomainHubDefPtr hub = def->hubs[i]; + if (hub->type == VIR_DOMAIN_HUB_TYPE_USB) { + if (iter(&hub->info, opaque) < 0) + return -1; + } + } + + /* usb-host */ + for (i = 0; i < def->nhostdevs; i++) { + virDomainHostdevDefPtr hostdev = def->hostdevs[i]; + if (hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) { + if (iter(hostdev->info, opaque) < 0) + return -1; + } + } + + /* usb-storage */ + for (i = 0; i < def->ndisks; i++) { + virDomainDiskDefPtr disk = def->disks[i]; + if (disk->bus == VIR_DOMAIN_DISK_BUS_USB) { + if (iter(&disk->info, opaque) < 0) + return -1; + } + } + + /* TODO: add def->nets here when libvirt starts supporting usb-net */ + + /* usb-ccid */ + for (i = 0; i < def->ncontrollers; i++) { + virDomainControllerDefPtr cont = def->controllers[i]; + if (cont->type == VIR_DOMAIN_CONTROLLER_TYPE_CCID) { + if (iter(&cont->info, opaque) < 0) + return -1; + } + } + + /* usb-kbd, usb-mouse, usb-tablet */ + for (i = 0; i < def->ninputs; i++) { + virDomainInputDefPtr input = def->inputs[i]; + + if (input->bus == VIR_DOMAIN_INPUT_BUS_USB) { + if (iter(&input->info, opaque) < 0) + return -1; + } + } + + /* usb-serial */ + for (i = 0; i < def->nserials; i++) { + virDomainChrDefPtr serial = def->serials[i]; + if (serial->targetType == VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_USB) { + if (iter(&serial->info, opaque) < 0) + return -1; + } + } + + /* usb-audio model=usb */ + for (i = 0; i < def->nsounds; i++) { + virDomainSoundDefPtr sound = def->sounds[i]; + if (sound->model == VIR_DOMAIN_SOUND_MODEL_USB) { + if (iter(&sound->info, opaque) < 0) + return -1; + } + } + + return 0; +} + + /* Call iter(disk, name, depth, opaque) for each element of disk and * its backing chain in the pre-populated disk->src.backingStore. * ignoreOpenFailure determines whether to warn about a chain that diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 428f85d..2aa519c 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -2894,6 +2894,12 @@ typedef int (*virDomainDiskDefPathIterator)(virDomainDiskDefPtr disk, size_t depth, void *opaque); +typedef int (*virDomainUSBDeviceDefIterator)(virDomainDeviceInfoPtr info, + void *opaque); +int virDomainUSBDeviceDefForeach(virDomainDefPtr def, + virDomainUSBDeviceDefIterator iter, + void *opaque); + int virDomainDiskDefForeachPath(virDomainDiskDefPtr disk, bool ignoreOpenFailure, virDomainDiskDefPathIterator iter, diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 765ac0f..4b3de7b 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -109,6 +109,8 @@ virDomainPCIAddressSetGrow; virDomainPCIAddressSlotInUse; virDomainPCIAddressValidate; virDomainPCIControllerModelToConnectType; +virDomainUSBAddressAssign; +virDomainUSBAddressEnsure; virDomainUSBAddressPortFormat; virDomainUSBAddressPortFormatBuf; virDomainUSBAddressReserve; @@ -478,6 +480,7 @@ virDomainTPMBackendTypeToString; virDomainTPMDefFree; virDomainTPMModelTypeFromString; virDomainTPMModelTypeToString; +virDomainUSBDeviceDefForeach; virDomainVideoDefaultRAM; virDomainVideoDefaultType; virDomainVideoDefFree; diff --git a/src/qemu/qemu_domain_address.c b/src/qemu/qemu_domain_address.c index c5f4150..ac07c46 100644 --- a/src/qemu/qemu_domain_address.c +++ b/src/qemu/qemu_domain_address.c @@ -1619,6 +1619,37 @@ qemuDomainAssignPCIAddresses(virDomainDefPtr def, } +struct qemuAssignUSBIteratorInfo { + virDomainUSBAddressSetPtr addrs; + size_t count; +}; + + +static int +qemuDomainAssignUSBPortsIterator(virDomainDeviceInfoPtr info, + void *opaque) +{ + struct qemuAssignUSBIteratorInfo *data = opaque; + + if (info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) + return 0; + + return virDomainUSBAddressAssign(data->addrs, info); +} + + +static int +qemuDomainAssignUSBPorts(virDomainUSBAddressSetPtr addrs, + virDomainDefPtr def) +{ + struct qemuAssignUSBIteratorInfo data = { .addrs = addrs }; + + return virDomainUSBDeviceDefForeach(def, + qemuDomainAssignUSBPortsIterator, + &data); +} + + static int qemuDomainAssignUSBAddresses(virDomainDefPtr def, virDomainObjPtr obj) @@ -1638,6 +1669,11 @@ qemuDomainAssignUSBAddresses(virDomainDefPtr def, VIR_DEBUG("Existing USB addresses have been reserved"); + if (qemuDomainAssignUSBPorts(addrs, def) < 0) + goto cleanup; + + VIR_DEBUG("Finished assigning USB ports"); + if (obj && obj->privateData) { priv = obj->privateData; priv->usbaddrs = addrs; diff --git a/tests/qemuhotplugtestdata/qemuhotplug-console-compat-2-live+console-virtio.xml b/tests/qemuhotplugtestdata/qemuhotplug-console-compat-2-live+console-virtio.xml index 3495ee6..7ca36d5 100644 --- a/tests/qemuhotplugtestdata/qemuhotplug-console-compat-2-live+console-virtio.xml +++ b/tests/qemuhotplugtestdata/qemuhotplug-console-compat-2-live+console-virtio.xml @@ -100,6 +100,7 @@ </channel> <input type='tablet' bus='usb'> <alias name='input0'/> + <address type='usb' bus='0' port='1'/> </input> <input type='mouse' bus='ps2'> <alias name='input1'/> diff --git a/tests/qemuxml2argvdata/qemuxml2argv-bios-nvram.args b/tests/qemuxml2argvdata/qemuxml2argv-bios-nvram.args index fe4e419..848a029 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-bios-nvram.args +++ b/tests/qemuxml2argvdata/qemuxml2argv-bios-nvram.args @@ -21,5 +21,5 @@ QEMU_AUDIO_DRV=none \ -drive file=/dev/HostVG/QEMUGuest1,format=raw,if=none,id=drive-ide0-0-0 \ -device ide-drive,bus=ide.0,unit=0,drive=drive-ide0-0-0,id=ide0-0-0 \ -serial pty \ --device usb-tablet,id=input0 \ +-device usb-tablet,id=input0,bus=usb.0,port=1 \ -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-bios.args b/tests/qemuxml2argvdata/qemuxml2argv-bios.args index 012af85..604b871 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-bios.args +++ b/tests/qemuxml2argvdata/qemuxml2argv-bios.args @@ -22,5 +22,5 @@ QEMU_AUDIO_DRV=none \ -drive file=/dev/HostVG/QEMUGuest1,format=raw,if=none,id=drive-ide0-0-0 \ -device ide-drive,bus=ide.0,unit=0,drive=drive-ide0-0-0,id=ide0-0-0 \ -serial pty \ --device usb-tablet,id=input0 \ +-device usb-tablet,id=input0,bus=usb.0,port=1 \ -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-console-compat-2-live.xml b/tests/qemuxml2argvdata/qemuxml2argv-console-compat-2-live.xml index b36af27..f300940 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-console-compat-2-live.xml +++ b/tests/qemuxml2argvdata/qemuxml2argv-console-compat-2-live.xml @@ -95,6 +95,7 @@ </channel> <input type='tablet' bus='usb'> <alias name='input0'/> + <address type='usb' bus='0' port='1'/> </input> <input type='mouse' bus='ps2'> <alias name='input1'/> diff --git a/tests/qemuxml2argvdata/qemuxml2argv-console-compat-2.xml b/tests/qemuxml2argvdata/qemuxml2argv-console-compat-2.xml index 2ae104e..7b35709 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-console-compat-2.xml +++ b/tests/qemuxml2argvdata/qemuxml2argv-console-compat-2.xml @@ -78,7 +78,9 @@ <target type='virtio' name='org.qemu.guest_agent.0'/> <address type='virtio-serial' controller='0' bus='0' port='1'/> </channel> - <input type='tablet' bus='usb'/> + <input type='tablet' bus='usb'> + <address type='usb' bus='0' port='1'/> + </input> <input type='mouse' bus='ps2'/> <input type='keyboard' bus='ps2'/> <graphics type='vnc' port='-1' autoport='yes' listen='0.0.0.0'> diff --git a/tests/qemuxml2argvdata/qemuxml2argv-controller-order.args b/tests/qemuxml2argvdata/qemuxml2argv-controller-order.args index 52c1a74..ac6243e 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-controller-order.args +++ b/tests/qemuxml2argvdata/qemuxml2argv-controller-order.args @@ -19,8 +19,8 @@ nowait \ -boot order=cna,menu=off \ -device piix3-usb-uhci,id=usb,bus=pci.0,addr=0x1.0x2 \ -device virtio-serial-pci,id=virtio-serial0,bus=pci.0,addr=0x7 \ --device usb-hub,id=hub0 \ --device usb-ccid,id=ccid0 \ +-device usb-hub,id=hub0,bus=usb.0,port=1 \ +-device usb-ccid,id=ccid0,bus=usb.0,port=1.1 \ -drive file=/tmp/fdr.img,format=raw,if=none,id=drive-virtio-disk0,cache=none,\ aio=native \ -device virtio-blk-pci,bus=pci.0,addr=0x5,drive=drive-virtio-disk0,\ @@ -37,10 +37,10 @@ media=cdrom,id=drive-ide0-1-0,readonly=on \ -chardev spicevmc,id=charchannel0,name=vdagent \ -device virtserialport,bus=virtio-serial0.0,nr=1,chardev=charchannel0,\ id=channel0,name=com.redhat.spice.0 \ --device usb-tablet,id=input0 \ +-device usb-tablet,id=input0,bus=usb.0,port=1.2 \ -spice port=5901,tls-port=5902,addr=0.0.0.0,x509-dir=/etc/pki/libvirt-spice \ -vga cirrus \ -device intel-hda,id=sound0,bus=pci.0,addr=0x4 \ -device hda-duplex,id=sound0-codec0,bus=sound0.0,cad=0 \ --device usb-host,hostbus=14,hostaddr=6,id=hostdev0 \ +-device usb-host,hostbus=14,hostaddr=6,id=hostdev0,bus=usb.0,port=2 \ -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x6 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-disk-usb-device-removable.args b/tests/qemuxml2argvdata/qemuxml2argv-disk-usb-device-removable.args index 63e2bb2..7cda592 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-disk-usb-device-removable.args +++ b/tests/qemuxml2argvdata/qemuxml2argv-disk-usb-device-removable.args @@ -21,5 +21,6 @@ QEMU_AUDIO_DRV=none \ -drive file=/dev/HostVG/QEMUGuest1,format=raw,if=none,id=drive-ide0-0-0 \ -device ide-drive,bus=ide.0,unit=0,drive=drive-ide0-0-0,id=ide0-0-0 \ -drive file=/tmp/usbdisk.img,format=raw,if=none,id=drive-usb-disk0 \ --device usb-storage,drive=drive-usb-disk0,id=usb-disk0,removable=on \ +-device usb-storage,bus=usb.0,port=1,drive=drive-usb-disk0,id=usb-disk0,\ +removable=on \ -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-disk-usb-device.args b/tests/qemuxml2argvdata/qemuxml2argv-disk-usb-device.args index 5d1ea98..03ef44f 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-disk-usb-device.args +++ b/tests/qemuxml2argvdata/qemuxml2argv-disk-usb-device.args @@ -21,5 +21,5 @@ QEMU_AUDIO_DRV=none \ -drive file=/dev/HostVG/QEMUGuest1,format=raw,if=none,id=drive-ide0-0-0 \ -device ide-drive,bus=ide.0,unit=0,drive=drive-ide0-0-0,id=ide0-0-0 \ -drive file=/tmp/usbdisk.img,format=raw,if=none,id=drive-usb-disk0 \ --device usb-storage,drive=drive-usb-disk0,id=usb-disk0 \ +-device usb-storage,bus=usb.0,port=1,drive=drive-usb-disk0,id=usb-disk0 \ -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-timeout.args b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-timeout.args index c0be4ee..cead7d6 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-timeout.args +++ b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-timeout.args @@ -28,7 +28,7 @@ media=cdrom,id=drive-ide0-1-0,readonly=on \ -device rtl8139,vlan=0,id=net0,mac=52:54:00:71:70:89,bus=pci.0,addr=0x7 \ -net tap,fd=3,vlan=0,name=hostnet0 \ -serial pty \ --device usb-tablet,id=input0 \ +-device usb-tablet,id=input0,bus=usb.0,port=1 \ -spice port=5900,addr=127.0.0.1 \ -vga std \ -device AC97,id=sound0,bus=pci.0,addr=0x3 \ diff --git a/tests/qemuxml2argvdata/qemuxml2argv-hostdev-usb-address-device-boot.args b/tests/qemuxml2argvdata/qemuxml2argv-hostdev-usb-address-device-boot.args index 8c00055..86f394b 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-hostdev-usb-address-device-boot.args +++ b/tests/qemuxml2argvdata/qemuxml2argv-hostdev-usb-address-device-boot.args @@ -19,5 +19,5 @@ QEMU_AUDIO_DRV=none \ -usb \ -drive file=/dev/HostVG/QEMUGuest1,format=raw,if=none,id=drive-ide0-0-0 \ -device ide-drive,bus=ide.0,unit=0,drive=drive-ide0-0-0,id=ide0-0-0 \ --device usb-host,hostbus=14,hostaddr=6,id=hostdev0,bootindex=1 \ +-device usb-host,hostbus=14,hostaddr=6,id=hostdev0,bootindex=1,bus=usb.0,port=1 \ -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-hostdev-usb-address-device.args b/tests/qemuxml2argvdata/qemuxml2argv-hostdev-usb-address-device.args index b5e6834..7883c61 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-hostdev-usb-address-device.args +++ b/tests/qemuxml2argvdata/qemuxml2argv-hostdev-usb-address-device.args @@ -20,5 +20,5 @@ QEMU_AUDIO_DRV=none \ -usb \ -drive file=/dev/HostVG/QEMUGuest1,format=raw,if=none,id=drive-ide0-0-0 \ -device ide-drive,bus=ide.0,unit=0,drive=drive-ide0-0-0,id=ide0-0-0 \ --device usb-host,hostbus=14,hostaddr=6,id=hostdev0 \ +-device usb-host,hostbus=14,hostaddr=6,id=hostdev0,bus=usb.0,port=1 \ -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-hostdev-usb-address.args b/tests/qemuxml2argvdata/qemuxml2argv-hostdev-usb-address.args index bb5d55a..d1c3e8f 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-hostdev-usb-address.args +++ b/tests/qemuxml2argvdata/qemuxml2argv-hostdev-usb-address.args @@ -19,4 +19,4 @@ QEMU_AUDIO_DRV=none \ -usb \ -drive file=/dev/HostVG/QEMUGuest1,format=raw,if=none,id=drive-ide0-0-0 \ -device ide-drive,bus=ide.0,unit=0,drive=drive-ide0-0-0,id=ide0-0-0 \ --device usb-host,hostbus=14,hostaddr=6,id=hostdev0 +-device usb-host,hostbus=14,hostaddr=6,id=hostdev0,bus=usb.0,port=1 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-hugepages-numa.args b/tests/qemuxml2argvdata/qemuxml2argv-hugepages-numa.args index c5a9e53..08ed54c 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-hugepages-numa.args +++ b/tests/qemuxml2argvdata/qemuxml2argv-hugepages-numa.args @@ -46,7 +46,7 @@ id=channel0,name=org.qemu.guest_agent.0 \ -chardev spicevmc,id=charchannel1,name=vdagent \ -device virtserialport,bus=virtio-serial0.0,nr=2,chardev=charchannel1,\ id=channel1,name=com.redhat.spice.0 \ --device usb-tablet,id=input0 \ +-device usb-tablet,id=input0,bus=usb.0,port=1 \ -spice port=5901,tls-port=5902,addr=127.0.0.1,x509-dir=/etc/pki/libvirt-spice \ -vga qxl \ -global qxl-vga.ram_size=67108864 \ diff --git a/tests/qemuxml2argvdata/qemuxml2argv-input-usbmouse.args b/tests/qemuxml2argvdata/qemuxml2argv-input-usbmouse.args index bd0e5c6..df96e6a 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-input-usbmouse.args +++ b/tests/qemuxml2argvdata/qemuxml2argv-input-usbmouse.args @@ -19,4 +19,4 @@ QEMU_AUDIO_DRV=none \ -usb \ -drive file=/dev/HostVG/QEMUGuest1,format=raw,if=none,id=drive-ide0-0-0 \ -device ide-drive,bus=ide.0,unit=0,drive=drive-ide0-0-0,id=ide0-0-0 \ --device usb-mouse,id=input0 +-device usb-mouse,id=input0,bus=usb.0,port=1 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-input-usbtablet.args b/tests/qemuxml2argvdata/qemuxml2argv-input-usbtablet.args index 294515f..faf21d5 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-input-usbtablet.args +++ b/tests/qemuxml2argvdata/qemuxml2argv-input-usbtablet.args @@ -19,4 +19,4 @@ QEMU_AUDIO_DRV=none \ -usb \ -drive file=/dev/HostVG/QEMUGuest1,format=raw,if=none,id=drive-ide0-0-0 \ -device ide-drive,bus=ide.0,unit=0,drive=drive-ide0-0-0,id=ide0-0-0 \ --device usb-tablet,id=input0 +-device usb-tablet,id=input0,bus=usb.0,port=1 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-pseries-usb-kbd.args b/tests/qemuxml2argvdata/qemuxml2argv-pseries-usb-kbd.args index 25c16cb..5887616 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-pseries-usb-kbd.args +++ b/tests/qemuxml2argvdata/qemuxml2argv-pseries-usb-kbd.args @@ -22,4 +22,4 @@ server,nowait \ -device pci-ohci,id=usb,bus=pci,addr=0x1 \ -chardev pty,id=charserial0 \ -device spapr-vty,chardev=charserial0,reg=0x30000000 \ --device usb-kbd,id=input0 +-device usb-kbd,id=input0,bus=usb.0,port=1 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-serial-spiceport.args b/tests/qemuxml2argvdata/qemuxml2argv-serial-spiceport.args index 246e854..f05c3f2 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-serial-spiceport.args +++ b/tests/qemuxml2argvdata/qemuxml2argv-serial-spiceport.args @@ -23,7 +23,7 @@ server,nowait \ -device ide-drive,bus=ide.0,unit=0,drive=drive-ide0-0-0,id=ide0-0-0 \ -chardev spiceport,id=charserial0,name=org.qemu.console.serial.0 \ -device isa-serial,chardev=charserial0,id=serial0 \ --device usb-tablet,id=input0 \ +-device usb-tablet,id=input0,bus=usb.0,port=1 \ -spice port=5903,tls-port=5904,addr=127.0.0.1,x509-dir=/etc/pki/libvirt-spice \ -device qxl-vga,id=video0,ram_size=67108864,vram_size=67108864,bus=pci.0,\ addr=0x2 \ diff --git a/tests/qemuxml2argvdata/qemuxml2argv-smartcard-controller.args b/tests/qemuxml2argvdata/qemuxml2argv-smartcard-controller.args index 8cb0968..c75054b 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-smartcard-controller.args +++ b/tests/qemuxml2argvdata/qemuxml2argv-smartcard-controller.args @@ -20,6 +20,6 @@ server,nowait \ -no-acpi \ -boot c \ -usb \ --device usb-ccid,id=ccid0 \ +-device usb-ccid,id=ccid0,bus=usb.0,port=1 \ -device ccid-card-emulated,backend=nss-emulated,id=smartcard0,bus=ccid0.0 \ -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-smartcard-host-certificates.args b/tests/qemuxml2argvdata/qemuxml2argv-smartcard-host-certificates.args index d4a4d31..f6f5076 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-smartcard-host-certificates.args +++ b/tests/qemuxml2argvdata/qemuxml2argv-smartcard-host-certificates.args @@ -20,7 +20,7 @@ server,nowait \ -no-acpi \ -boot c \ -usb \ --device usb-ccid,id=ccid0 \ +-device usb-ccid,id=ccid0,bus=usb.0,port=1 \ -device ccid-card-emulated,backend=certificates,cert1=cert1,cert2=cert2,\ cert3=cert3,db=/etc/pki/nssdb,id=smartcard0,bus=ccid0.0 \ -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-smartcard-host.args b/tests/qemuxml2argvdata/qemuxml2argv-smartcard-host.args index 8cb0968..c75054b 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-smartcard-host.args +++ b/tests/qemuxml2argvdata/qemuxml2argv-smartcard-host.args @@ -20,6 +20,6 @@ server,nowait \ -no-acpi \ -boot c \ -usb \ --device usb-ccid,id=ccid0 \ +-device usb-ccid,id=ccid0,bus=usb.0,port=1 \ -device ccid-card-emulated,backend=nss-emulated,id=smartcard0,bus=ccid0.0 \ -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-smartcard-passthrough-spicevmc.args b/tests/qemuxml2argvdata/qemuxml2argv-smartcard-passthrough-spicevmc.args index 7411f2a..819098e 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-smartcard-passthrough-spicevmc.args +++ b/tests/qemuxml2argvdata/qemuxml2argv-smartcard-passthrough-spicevmc.args @@ -20,7 +20,7 @@ server,nowait \ -no-acpi \ -boot c \ -usb \ --device usb-ccid,id=ccid0 \ +-device usb-ccid,id=ccid0,bus=usb.0,port=1 \ -chardev spicevmc,id=charsmartcard0,name=smartcard \ -device ccid-card-passthru,chardev=charsmartcard0,id=smartcard0,bus=ccid0.0 \ -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-smartcard-passthrough-tcp.args b/tests/qemuxml2argvdata/qemuxml2argv-smartcard-passthrough-tcp.args index 93bbbc4..1563f1a 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-smartcard-passthrough-tcp.args +++ b/tests/qemuxml2argvdata/qemuxml2argv-smartcard-passthrough-tcp.args @@ -20,7 +20,7 @@ server,nowait \ -no-acpi \ -boot c \ -usb \ --device usb-ccid,id=ccid0 \ +-device usb-ccid,id=ccid0,bus=usb.0,port=1 \ -chardev socket,id=charsmartcard0,host=127.0.0.1,port=2001,server,nowait \ -device ccid-card-passthru,chardev=charsmartcard0,id=smartcard0,bus=ccid0.0 \ -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-sound-device.args b/tests/qemuxml2argvdata/qemuxml2argv-sound-device.args index 8d846a0..b084f4e 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-sound-device.args +++ b/tests/qemuxml2argvdata/qemuxml2argv-sound-device.args @@ -34,5 +34,5 @@ QEMU_AUDIO_DRV=none \ -device ich9-intel-hda,id=sound7,bus=pci.0,addr=0x8 \ -device hda-micro,id=sound7-codec0,bus=sound7.0,cad=0 \ -device hda-duplex,id=sound7-codec1,bus=sound7.0,cad=1 \ --device usb-audio,id=sound8 \ +-device usb-audio,id=sound8,bus=usb.0,port=1 \ -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x9 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-usb-port-autoassign.args b/tests/qemuxml2argvdata/qemuxml2argv-usb-port-autoassign.args new file mode 100644 index 0000000..ac5cfdd --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-usb-port-autoassign.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 \ +-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-hub,id=hub1,bus=usb.0,port=2 \ +-device usb-mouse,id=input0,bus=usb.0,port=1.1 \ +-device usb-mouse,id=input1,bus=usb.0,port=1.2 \ +-device usb-mouse,id=input2,bus=usb.0,port=1.3 \ +-device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-usb-port-autoassign.xml b/tests/qemuxml2argvdata/qemuxml2argv-usb-port-autoassign.xml new file mode 100644 index 0000000..a2fe34e --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-usb-port-autoassign.xml @@ -0,0 +1,27 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219136</memory> + <currentMemory unit='KiB'>219136</currentMemory> + <vcpu placement='static'>1</vcpu> + <os> + <type arch='i686' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <devices> + <emulator>/usr/bin/qemu</emulator> + <controller type='usb' index='0'/> + <memballoon model='virtio'/> + <hub type='usb'> + <address type='usb' bus='0' port='1'/> + </hub> + <input type='mouse' bus='usb'> + </input> + <hub type='usb'> + </hub> + <input type='mouse' bus='usb'> + </input> + <input type='mouse' bus='usb'> + </input> + </devices> +</domain> diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index 1a01240..1c97d16 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -1165,6 +1165,9 @@ mymain(void) DO_TEST("usb-ports", QEMU_CAPS_CHARDEV, QEMU_CAPS_USB_HUB, QEMU_CAPS_NODEFCONFIG); + DO_TEST("usb-port-autoassign", + QEMU_CAPS_CHARDEV, QEMU_CAPS_USB_HUB, + QEMU_CAPS_NODEFCONFIG); DO_TEST("usb-redir", QEMU_CAPS_CHARDEV, QEMU_CAPS_NODEFCONFIG, QEMU_CAPS_PCI_MULTIFUNCTION, QEMU_CAPS_USB_HUB, -- 2.7.3

On Fr, 2016-06-17 at 20:07 +0200, Ján Tomko wrote:
Automatically assign addresses to USB devices.
Just like reserving, this is only done for newly defined domains.
What happens if I add a device to a guest (using virsh edit)? cheers, Gerd

On Mon, Jun 20, 2016 at 09:02:54AM +0200, Gerd Hoffmann wrote:
On Fr, 2016-06-17 at 20:07 +0200, Ján Tomko wrote:
Automatically assign addresses to USB devices.
Just like reserving, this is only done for newly defined domains.
What happens if I add a device to a guest (using virsh edit)?
From libvirt's point of view, 'virsh edit' is a define, so it will assing an address for the new device, as well as the existing ones.
Jan

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 | 47 ++++++++++++++++++++++ .../qemuxml2argv-usb-hub-autoadd.args | 28 +++++++++++++ .../qemuxml2argv-usb-hub-autoadd.xml | 23 +++++++++++ tests/qemuxml2argvtest.c | 3 ++ 7 files changed, 124 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 e089763..b0cbcfc 100644 --- a/src/conf/domain_addr.c +++ b/src/conf/domain_addr.c @@ -1586,6 +1586,26 @@ virDomainUSBAddressFindFreePort(virDomainUSBAddressHubPtr hub, } +size_t +virDomainUSBAddressCountAvailablePorts(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; +} + + int virDomainUSBAddressAssign(virDomainUSBAddressSetPtr addrs, virDomainDeviceInfoPtr info) diff --git a/src/conf/domain_addr.h b/src/conf/domain_addr.h index ce640fb..331b597 100644 --- a/src/conf/domain_addr.h +++ b/src/conf/domain_addr.h @@ -269,6 +269,8 @@ virDomainUSBAddressSetPtr virDomainUSBAddressSetCreate(void); int virDomainUSBAddressSetAddControllers(virDomainUSBAddressSetPtr addrs, virDomainDefPtr def) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); +size_t +virDomainUSBAddressCountAvailablePorts(virDomainDefPtr def); void virDomainUSBAddressSetFree(virDomainUSBAddressSetPtr addrs); int diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 4b3de7b..a58a643 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -110,6 +110,7 @@ virDomainPCIAddressSlotInUse; virDomainPCIAddressValidate; virDomainPCIControllerModelToConnectType; virDomainUSBAddressAssign; +virDomainUSBAddressCountAvailablePorts; virDomainUSBAddressEnsure; virDomainUSBAddressPortFormat; virDomainUSBAddressPortFormatBuf; diff --git a/src/qemu/qemu_domain_address.c b/src/qemu/qemu_domain_address.c index ac07c46..ea96a33 100644 --- a/src/qemu/qemu_domain_address.c +++ b/src/qemu/qemu_domain_address.c @@ -1651,6 +1651,50 @@ 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 = virDomainUSBAddressCountAvailablePorts(def); + ignore_value(virDomainUSBDeviceDefForeach(def, + qemuDomainAssignUSBPortsCounter, + &data)); + 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) { @@ -1661,6 +1705,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 0000000..12c9691 --- /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 \ +-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 0000000..43e0f1f --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-usb-hub-autoadd.xml @@ -0,0 +1,23 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219136</memory> + <currentMemory unit='KiB'>219136</currentMemory> + <vcpu placement='static'>1</vcpu> + <os> + <type arch='i686' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <devices> + <emulator>/usr/bin/qemu</emulator> + <controller type='usb' index='0'/> + <memballoon model='virtio'/> + <input type='mouse' bus='usb'> + </input> + <input type='mouse' bus='usb'> + </input> + <input type='mouse' bus='usb'> + </input> + <input type='tablet' bus='usb'/> + </devices> +</domain> diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index 1c97d16..a53af53 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -1159,6 +1159,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); -- 2.7.3

--- tests/qemuxml2argvdata/qemuxml2argv-hotplug-base.args | 5 ++++- tests/qemuxml2argvdata/qemuxml2argv-hotplug-base.xml | 8 ++++++++ tests/qemuxml2argvtest.c | 3 ++- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/tests/qemuxml2argvdata/qemuxml2argv-hotplug-base.args b/tests/qemuxml2argvdata/qemuxml2argv-hotplug-base.args index 79aa270..1acbf0d 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-hotplug-base.args +++ b/tests/qemuxml2argvdata/qemuxml2argv-hotplug-base.args @@ -17,4 +17,7 @@ QEMU_AUDIO_DRV=none \ -boot c \ -device virtio-scsi-pci,id=scsi0,bus=pci.0,addr=0x3 \ -device virtio-serial-pci,id=virtio-serial0,bus=pci.0,addr=0x4 \ --usb +-usb \ +-drive file=/dev/null,format=raw,if=none,id=drive-usb-disk16,readonly=on,\ +cache=none \ +-device usb-storage,bus=usb.0,port=1,drive=drive-usb-disk16,id=usb-disk16 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-hotplug-base.xml b/tests/qemuxml2argvdata/qemuxml2argv-hotplug-base.xml index 20ad0a5..aa93487 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-hotplug-base.xml +++ b/tests/qemuxml2argvdata/qemuxml2argv-hotplug-base.xml @@ -19,6 +19,14 @@ <on_crash>restart</on_crash> <devices> <emulator>/usr/libexec/qemu-kvm</emulator> + <disk type='file' device='disk'> + <driver name='qemu' type='raw' cache='none'/> + <source file='/dev/null'/> + <target dev='sdq' bus='usb'/> + <readonly/> + <shareable/> + <address type='usb' bus='0' port='1'/> + </disk> <controller type='usb' index='0'> <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/> </controller> diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index a53af53..43bbb15 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -1726,7 +1726,8 @@ mymain(void) QEMU_CAPS_DEVICE_PCI_BRIDGE); DO_TEST("hotplug-base", - QEMU_CAPS_KVM, QEMU_CAPS_VIRTIO_SCSI); + QEMU_CAPS_KVM, QEMU_CAPS_VIRTIO_SCSI, + QEMU_CAPS_DEVICE_USB_STORAGE); DO_TEST("pcihole64", QEMU_CAPS_I440FX_PCI_HOLE64_SIZE); DO_TEST_FAILURE("pcihole64-none", NONE); -- 2.7.3

USB disks, redirected devices, host devices and serial devices are supported. --- src/conf/domain_addr.c | 28 ++++++++++++++++++++++ src/conf/domain_addr.h | 4 ++++ src/libvirt_private.syms | 1 + src/qemu/qemu_domain_address.c | 5 ++++ src/qemu/qemu_hotplug.c | 27 +++++++++++++++++++++ .../qemuhotplug-hotplug-base-live+disk-usb.xml | 1 + 6 files changed, 66 insertions(+) diff --git a/src/conf/domain_addr.c b/src/conf/domain_addr.c index b0cbcfc..b500ddc 100644 --- a/src/conf/domain_addr.c +++ b/src/conf/domain_addr.c @@ -1704,3 +1704,31 @@ virDomainUSBAddressEnsure(virDomainUSBAddressSetPtr addrs, return 0; } + + +int +virDomainUSBAddressRelease(virDomainUSBAddressSetPtr addrs, + virDomainDeviceInfoPtr info) +{ + virDomainUSBAddressHubPtr targetHub = NULL; + char *portstr = NULL; + int targetPort; + int ret = -1; + + if (info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB) + return 0; + + portstr = virDomainUSBAddressPortFormat(info->addr.usb.port); + VIR_DEBUG("Releasing USB addr bus=%u port=%s", info->addr.usb.bus, portstr); + + if (!(targetHub = virDomainUSBAddressFindPort(addrs, info, &targetPort))) + goto cleanup; + + ignore_value(virBitmapClearBit(targetHub->ports, targetPort)); + + ret = 0; + + cleanup: + VIR_FREE(portstr); + return ret; +} diff --git a/src/conf/domain_addr.h b/src/conf/domain_addr.h index 331b597..c9ea287 100644 --- a/src/conf/domain_addr.h +++ b/src/conf/domain_addr.h @@ -288,4 +288,8 @@ int virDomainUSBAddressEnsure(virDomainUSBAddressSetPtr addrs, virDomainDeviceInfoPtr info) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); +int +virDomainUSBAddressRelease(virDomainUSBAddressSetPtr addrs, + virDomainDeviceInfoPtr info) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); #endif /* __DOMAIN_ADDR_H__ */ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index a58a643..b5dca75 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -114,6 +114,7 @@ virDomainUSBAddressCountAvailablePorts; virDomainUSBAddressEnsure; virDomainUSBAddressPortFormat; virDomainUSBAddressPortFormatBuf; +virDomainUSBAddressRelease; virDomainUSBAddressReserve; virDomainUSBAddressSetAddControllers; virDomainUSBAddressSetCreate; diff --git a/src/qemu/qemu_domain_address.c b/src/qemu/qemu_domain_address.c index ea96a33..9590e29 100644 --- a/src/qemu/qemu_domain_address.c +++ b/src/qemu/qemu_domain_address.c @@ -1786,4 +1786,9 @@ qemuDomainReleaseDeviceAddress(virDomainObjPtr vm, virDomainVirtioSerialAddrRelease(priv->vioserialaddrs, info) < 0) VIR_WARN("Unable to release virtio-serial address on %s", NULLSTR(devstr)); + if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB && + priv->usbaddrs && + virDomainUSBAddressRelease(priv->usbaddrs, info) < 0) + VIR_WARN("Unable to release usb address on %s", + NULLSTR(devstr)); } diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 8f2795d..593d8e5 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -627,6 +627,13 @@ qemuDomainAttachUSBMassStorageDevice(virQEMUDriverPtr driver, char *devstr = NULL; virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); const char *src = virDomainDiskGetSource(disk); + bool releaseaddr; + + if (priv->usbaddrs) { + if (virDomainUSBAddressEnsure(priv->usbaddrs, &disk->info) < 0) + goto cleanup; + releaseaddr = true; + } if (qemuDomainPrepareDisk(driver, vm, disk, NULL, false) < 0) goto cleanup; @@ -672,6 +679,8 @@ qemuDomainAttachUSBMassStorageDevice(virQEMUDriverPtr driver, virDomainDiskInsertPreAlloced(vm->def, disk); cleanup: + if (ret < 0 && releaseaddr) + virDomainUSBAddressRelease(priv->usbaddrs, &disk->info); VIR_FREE(devstr); VIR_FREE(drivestr); virObjectUnref(cfg); @@ -1473,6 +1482,12 @@ qemuDomainAttachChrDeviceAssignAddr(qemuDomainObjPrivatePtr priv, return -1; return 1; + } else if (chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL && + chr->targetType == VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_USB) { + if (virDomainUSBAddressEnsure(priv->usbaddrs, &chr->info) < 0) + return -1; + return 1; + } else if (chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_CHANNEL && chr->targetType == VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO) { if (virDomainVirtioSerialAddrAutoAssign(NULL, priv->vioserialaddrs, @@ -1791,11 +1806,18 @@ qemuDomainAttachHostUSBDevice(virQEMUDriverPtr driver, { qemuDomainObjPrivatePtr priv = vm->privateData; char *devstr = NULL; + bool releaseaddr = false; bool added = false; bool teardowncgroup = false; bool teardownlabel = false; int ret = -1; + if (priv->usbaddrs) { + if (virDomainUSBAddressEnsure(priv->usbaddrs, hostdev->info) < 0) + goto cleanup; + releaseaddr = true; + } + if (qemuHostdevPrepareUSBDevices(driver, vm->def->name, &hostdev, 1, 0) < 0) goto cleanup; @@ -1841,6 +1863,8 @@ qemuDomainAttachHostUSBDevice(virQEMUDriverPtr driver, VIR_WARN("Unable to restore host device labelling on hotplug fail"); if (added) qemuHostdevReAttachUSBDevices(driver, vm->def->name, &hostdev, 1); + if (releaseaddr) + virDomainUSBAddressRelease(priv->usbaddrs, hostdev->info); } VIR_FREE(devstr); return ret; @@ -2834,6 +2858,8 @@ qemuDomainRemoveDiskDevice(virQEMUDriverPtr driver, dev.type = VIR_DOMAIN_DEVICE_DISK; dev.data.disk = disk; ignore_value(qemuRemoveSharedDevice(driver, &dev, vm->def->name)); + if (priv->usbaddrs) + virDomainUSBAddressRelease(priv->usbaddrs, &disk->info); virDomainDiskDefFree(disk); return 0; @@ -2930,6 +2956,7 @@ qemuDomainRemoveUSBHostDevice(virQEMUDriverPtr driver, virDomainHostdevDefPtr hostdev) { qemuHostdevReAttachUSBDevices(driver, vm->def->name, &hostdev, 1); + qemuDomainReleaseDeviceAddress(vm, hostdev->info, NULL); } static void diff --git a/tests/qemuhotplugtestdata/qemuhotplug-hotplug-base-live+disk-usb.xml b/tests/qemuhotplugtestdata/qemuhotplug-hotplug-base-live+disk-usb.xml index 41039a4..cd686e6 100644 --- a/tests/qemuhotplugtestdata/qemuhotplug-hotplug-base-live+disk-usb.xml +++ b/tests/qemuhotplugtestdata/qemuhotplug-hotplug-base-live+disk-usb.xml @@ -27,6 +27,7 @@ <readonly/> <shareable/> <alias name='usb-disk16'/> + <address type='usb' bus='0' port='1'/> </disk> <controller type='usb' index='0'> <alias name='usb'/> -- 2.7.3
participants (3)
-
Gerd Hoffmann
-
Ján Tomko
-
Peter Krempa