[libvirt] [PATCH v2 0/8] Usb hub Cold(un)plug and hot(un)plug support

v1: https://www.redhat.com/archives/libvir-list/2018-October/msg00563.html Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1375423 Han Han (8): qemu: Allow coldplugging of hub device qemu: Allow coldunplugging of hub device qemu_alias: Refactor hub alias assignment for hotplug qemu_command: Make qemuBuildHubDevStr global private_syms: add virDomainHubDefFree to libvirt_private.syms qemu: implement usb hub device hotplug qemu: implement usb hub device hotunplug news: Cold(un)plug and hot(un)plug support for usb hub device docs/news.xml | 5 ++ src/conf/domain_conf.c | 30 +++++++++ src/conf/domain_conf.h | 3 + src/libvirt_private.syms | 2 + src/qemu/qemu_alias.c | 22 +++++-- src/qemu/qemu_alias.h | 4 ++ src/qemu/qemu_command.c | 2 +- src/qemu/qemu_command.h | 4 ++ src/qemu/qemu_driver.c | 30 +++++++-- src/qemu/qemu_hotplug.c | 132 ++++++++++++++++++++++++++++++++++++++- src/qemu/qemu_hotplug.h | 12 ++++ 11 files changed, 236 insertions(+), 10 deletions(-) -- 2.19.1

https://bugzilla.redhat.com/show_bug.cgi?id=1375423 Signed-off-by: Han Han <hhan@redhat.com> --- src/qemu/qemu_driver.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index a52e2495d5..130ce4cbd6 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -8133,6 +8133,11 @@ qemuDomainAttachDeviceConfig(virDomainDefPtr vmdef, return -1; break; + case VIR_DOMAIN_DEVICE_HUB: + if (VIR_APPEND_ELEMENT(vmdef->hubs, vmdef->nhubs, dev->data.hub) < 0) + return -1; + break; + case VIR_DOMAIN_DEVICE_VSOCK: if (vmdef->vsock) { virReportError(VIR_ERR_OPERATION_INVALID, "%s", @@ -8145,7 +8150,6 @@ qemuDomainAttachDeviceConfig(virDomainDefPtr vmdef, case VIR_DOMAIN_DEVICE_SOUND: case VIR_DOMAIN_DEVICE_VIDEO: case VIR_DOMAIN_DEVICE_GRAPHICS: - case VIR_DOMAIN_DEVICE_HUB: case VIR_DOMAIN_DEVICE_SMARTCARD: case VIR_DOMAIN_DEVICE_MEMBALLOON: case VIR_DOMAIN_DEVICE_NVRAM: -- 2.19.1

On 10/12/18 4:50 AM, Han Han wrote:
https://bugzilla.redhat.com/show_bug.cgi?id=1375423
Signed-off-by: Han Han <hhan@redhat.com> --- src/qemu/qemu_driver.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
<sigh> sorry for the delay. Lost in the pre KVM Forum "shuffle" Reviewed-by: John Ferlan <jferlan@redhat.com> John

https://bugzilla.redhat.com/show_bug.cgi?id=1375423 Signed-off-by: Han Han <hhan@redhat.com> --- src/conf/domain_conf.c | 30 ++++++++++++++++++++++++++++++ src/conf/domain_conf.h | 3 +++ src/libvirt_private.syms | 1 + src/qemu/qemu_driver.c | 10 +++++++++- 4 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 9911d56130..cfca5daa02 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -17624,6 +17624,36 @@ virDomainVsockDefEquals(const virDomainVsockDef *a, } +static bool +virDomainHubDefEquals(const virDomainHubDef *a, + const virDomainHubDef *b) +{ + if (a->type != b->type) + return false; + + if (a->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE && + !virDomainDeviceInfoAddressIsEqual(&a->info, &b->info)) + return false; + + return true; +} + + +ssize_t +virDomainHubDefFind(const virDomainDef *def, + const virDomainHubDef *hub) +{ + size_t i; + + for (i = 0; i < def->nhubs; i++) { + if (virDomainHubDefEquals(hub, def->hubs[i])) + return i; + } + + return -1; +} + + char * virDomainDefGetDefaultEmulator(virDomainDefPtr def, virCapsPtr caps) diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index e30a4b2fe7..c2d0877170 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -3365,6 +3365,9 @@ virDomainShmemDefPtr virDomainShmemDefRemove(virDomainDefPtr def, size_t idx) ssize_t virDomainInputDefFind(const virDomainDef *def, const virDomainInputDef *input) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK; +ssize_t virDomainHubDefFind(const virDomainDef *def, + const virDomainHubDef *hub) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK; bool virDomainVsockDefEquals(const virDomainVsockDef *a, const virDomainVsockDef *b) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK; diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 335210c31d..6245927673 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -399,6 +399,7 @@ virDomainHostdevRemove; virDomainHostdevSubsysPCIBackendTypeToString; virDomainHostdevSubsysTypeToString; virDomainHPTResizingTypeToString; +virDomainHubDefFind; virDomainHubTypeFromString; virDomainHubTypeToString; virDomainHypervTypeFromString; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 130ce4cbd6..92a81ff1f5 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -8338,10 +8338,18 @@ qemuDomainDetachDeviceConfig(virDomainDefPtr vmdef, vmdef->vsock = NULL; break; + case VIR_DOMAIN_DEVICE_HUB: + if ((idx = virDomainHubDefFind(vmdef, dev->data.hub)) < 0) { + virReportError(VIR_ERR_DEVICE_MISSING, "%s", + _("matching hub device not found")); + return -1; + } + VIR_DELETE_ELEMENT(vmdef->hubs, idx, vmdef->nhubs); + break; + case VIR_DOMAIN_DEVICE_SOUND: case VIR_DOMAIN_DEVICE_VIDEO: case VIR_DOMAIN_DEVICE_GRAPHICS: - case VIR_DOMAIN_DEVICE_HUB: case VIR_DOMAIN_DEVICE_SMARTCARD: case VIR_DOMAIN_DEVICE_MEMBALLOON: case VIR_DOMAIN_DEVICE_NVRAM: -- 2.19.1

On 10/12/18 4:50 AM, Han Han wrote:
https://bugzilla.redhat.com/show_bug.cgi?id=1375423
Signed-off-by: Han Han <hhan@redhat.com> --- src/conf/domain_conf.c | 30 ++++++++++++++++++++++++++++++ src/conf/domain_conf.h | 3 +++ src/libvirt_private.syms | 1 + src/qemu/qemu_driver.c | 10 +++++++++- 4 files changed, 43 insertions(+), 1 deletion(-)
I assume no concerns over something that could be attached to a port on the hub... I guess for a cold unplug it probably won't matter so much. Reviewed-by: John Ferlan <jferlan@redhat.com> John

On Tue, Nov 6, 2018 at 6:29 AM John Ferlan <jferlan@redhat.com> wrote:
On 10/12/18 4:50 AM, Han Han wrote:
https://bugzilla.redhat.com/show_bug.cgi?id=1375423
Signed-off-by: Han Han <hhan@redhat.com> --- src/conf/domain_conf.c | 30 ++++++++++++++++++++++++++++++ src/conf/domain_conf.h | 3 +++ src/libvirt_private.syms | 1 + src/qemu/qemu_driver.c | 10 +++++++++- 4 files changed, 43 insertions(+), 1 deletion(-)
I assume no concerns over something that could be attached to a port on the hub... I guess for a cold unplug it probably won't matter so much.
For the usb hub coldunplug, I find a concern. If a usb device attatched to a usb-hub, then we coldunplug the hub, how will the hub and usb device look like? All disappear OR all remain? As I know, for scsi and scsi controllers, it is all remaining :)
Reviewed-by: John Ferlan <jferlan@redhat.com>
John
-- Best regards, ----------------------------------- Han Han Quality Engineer Redhat. Email: hhan@redhat.com Phone: +861065339333

On 11/7/18 8:17 AM, Han Han wrote:
On Tue, Nov 6, 2018 at 6:29 AM John Ferlan <jferlan@redhat.com <mailto:jferlan@redhat.com>> wrote:
On 10/12/18 4:50 AM, Han Han wrote: > https://bugzilla.redhat.com/show_bug.cgi?id=1375423 > > Signed-off-by: Han Han <hhan@redhat.com <mailto:hhan@redhat.com>> > --- > src/conf/domain_conf.c | 30 ++++++++++++++++++++++++++++++ > src/conf/domain_conf.h | 3 +++ > src/libvirt_private.syms | 1 + > src/qemu/qemu_driver.c | 10 +++++++++- > 4 files changed, 43 insertions(+), 1 deletion(-) >
I assume no concerns over something that could be attached to a port on the hub... I guess for a cold unplug it probably won't matter so much.
For the usb hub coldunplug, I find a concern. If a usb device attatched to a usb-hub, then we coldunplug the hub, how will the hub and usb device look like? All disappear OR all remain?
Since you've posted the patches, then I would hope you could answer your own question regarding what happens. Still in a way this is different than hot/live unplug. Attempting to start the guest after you've removed a hub that had defined/listed USB address/attachments would I assume fail because the hub is gone. But there is a little self doubt because of the automatic hub add algorithm pointed out in patch 7. If it does expectedly fail, then someone would have to cold plug a new device in using the correct address before booting - which is an expected operation. In a way no different that someone changing a guest config to use 100 vCPUs or 100G on a host that couldn't support those amounts. When cold changing things, as long as the value is valid - we're good. When starting if we don't have the resources, then we're not so good John
As I know, for scsi and scsi controllers, it is all remaining :)
Reviewed-by: John Ferlan <jferlan@redhat.com <mailto:jferlan@redhat.com>>
John
-- Best regards, ----------------------------------- Han Han Quality Engineer Redhat.
Email: hhan@redhat.com <mailto:hhan@redhat.com> Phone: +861065339333

Make qemuAssignDeviceHubAlias global and allow alias generating for reuse on hotplug. Signed-off-by: Han Han <hhan@redhat.com> --- src/qemu/qemu_alias.c | 22 ++++++++++++++++++---- src/qemu/qemu_alias.h | 4 ++++ 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/qemu/qemu_alias.c b/src/qemu/qemu_alias.c index 815caec465..116480eaee 100644 --- a/src/qemu/qemu_alias.c +++ b/src/qemu/qemu_alias.c @@ -364,14 +364,28 @@ qemuAssignDeviceVideoAlias(virDomainVideoDefPtr video, } -static int -qemuAssignDeviceHubAlias(virDomainHubDefPtr hub, +int +qemuAssignDeviceHubAlias(virDomainDefPtr def, + virDomainHubDefPtr hub, int idx) { if (hub->info.alias) return 0; - return virAsprintf(&hub->info.alias, "hub%d", idx); + if (idx == -1) { + int thisidx; + size_t i; + + for (i = 0; i < def->nhubs; i++) { + if ((thisidx = qemuDomainDeviceAliasIndex(&def->hubs[i]->info, "hub")) >= idx) + idx = thisidx + 1; + } + } + + if (virAsprintf(&hub->info.alias, "hub%d", idx) < 0) + return -1; + + return 0; } @@ -647,7 +661,7 @@ qemuAssignDeviceAliases(virDomainDefPtr def, virQEMUCapsPtr qemuCaps) return -1; } for (i = 0; i < def->nhubs; i++) { - if (qemuAssignDeviceHubAlias(def->hubs[i], i) < 0) + if (qemuAssignDeviceHubAlias(def, def->hubs[i], i) < 0) return -1; } for (i = 0; i < def->nshmems; i++) { diff --git a/src/qemu/qemu_alias.h b/src/qemu/qemu_alias.h index 33b9937ea4..ea30c1c8a3 100644 --- a/src/qemu/qemu_alias.h +++ b/src/qemu/qemu_alias.h @@ -71,6 +71,10 @@ int qemuAssignDeviceInputAlias(virDomainDefPtr def, virDomainInputDefPtr input, int idx); +int qemuAssignDeviceHubAlias(virDomainDefPtr def, + virDomainHubDefPtr hub, + int idx); + int qemuAssignDeviceVsockAlias(virDomainVsockDefPtr vsock); int qemuAssignDeviceAliases(virDomainDefPtr def, virQEMUCapsPtr qemuCaps); -- 2.19.1

$SUBJ: s/qemu_alias/qemu/ On 10/12/18 4:50 AM, Han Han wrote:
Make qemuAssignDeviceHubAlias global and allow alias generating for reuse on hotplug.
Following the model of Input devices I see...
Signed-off-by: Han Han <hhan@redhat.com> --- src/qemu/qemu_alias.c | 22 ++++++++++++++++++---- src/qemu/qemu_alias.h | 4 ++++ 2 files changed, 22 insertions(+), 4 deletions(-)
Reviewed-by: John Ferlan <jferlan@redhat.com> John

Make function qemuBuildHubDevStr global for reuse on hub hotplug. Signed-off-by: Han Han <hhan@redhat.com> --- src/qemu/qemu_command.c | 2 +- src/qemu/qemu_command.h | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 269276f2f9..e27f84b59b 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -4675,7 +4675,7 @@ qemuBuildUSBHostdevDevStr(const virDomainDef *def, } -static char * +char * qemuBuildHubDevStr(const virDomainDef *def, virDomainHubDefPtr dev, virQEMUCapsPtr qemuCaps) diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h index 98d4ac90b5..e1e4e56bc3 100644 --- a/src/qemu/qemu_command.h +++ b/src/qemu/qemu_command.h @@ -174,6 +174,10 @@ char *qemuBuildRedirdevDevStr(const virDomainDef *def, virDomainRedirdevDefPtr dev, virQEMUCapsPtr qemuCaps); +char *qemuBuildHubDevStr(const virDomainDef *def, + virDomainHubDefPtr dev, + virQEMUCapsPtr qemuCaps); + int qemuNetworkPrepareDevices(virDomainDefPtr def); int qemuGetDriveSourceString(virStorageSourcePtr src, -- 2.19.1

$SUBJ: s/qemu_command/qemu/ On 10/12/18 4:50 AM, Han Han wrote:
Make function qemuBuildHubDevStr global for reuse on hub hotplug.
Signed-off-by: Han Han <hhan@redhat.com> --- src/qemu/qemu_command.c | 2 +- src/qemu/qemu_command.h | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-)
Reviewed-by: John Ferlan <jferlan@redhat.com> John

Add virDomainHubDefFree to for the preparation of usb hub hotplug. Signed-off-by: Han Han <hhan@redhat.com> --- src/libvirt_private.syms | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 6245927673..b29c2bf62b 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -400,6 +400,7 @@ virDomainHostdevSubsysPCIBackendTypeToString; virDomainHostdevSubsysTypeToString; virDomainHPTResizingTypeToString; virDomainHubDefFind; +virDomainHubDefFree; virDomainHubTypeFromString; virDomainHubTypeToString; virDomainHypervTypeFromString; -- 2.19.1

$SUBJ: s/private_syms: a/conf: A/ On 10/12/18 4:50 AM, Han Han wrote:
Add virDomainHubDefFree to for the preparation of usb hub hotplug.
Signed-off-by: Han Han <hhan@redhat.com> --- src/libvirt_private.syms | 1 + 1 file changed, 1 insertion(+)
Reviewed-by: John Ferlan <jferlan@redhat.com> John

https://bugzilla.redhat.com/show_bug.cgi?id=1375423 Signed-off-by: Han Han <hhan@redhat.com> --- src/qemu/qemu_driver.c | 9 ++++++- src/qemu/qemu_hotplug.c | 58 +++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_hotplug.h | 8 ++++++ 3 files changed, 74 insertions(+), 1 deletion(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 92a81ff1f5..de764a7f1c 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -7710,12 +7710,19 @@ qemuDomainAttachDeviceLive(virDomainObjPtr vm, } break; + case VIR_DOMAIN_DEVICE_HUB: + ret = qemuDomainAttachHubDevice(driver, vm, dev->data.hub); + if (ret == 0) { + alias = dev->data.hub->info.alias; + dev->data.hub = NULL; + } + break; + case VIR_DOMAIN_DEVICE_NONE: case VIR_DOMAIN_DEVICE_FS: case VIR_DOMAIN_DEVICE_SOUND: case VIR_DOMAIN_DEVICE_VIDEO: case VIR_DOMAIN_DEVICE_GRAPHICS: - case VIR_DOMAIN_DEVICE_HUB: case VIR_DOMAIN_DEVICE_SMARTCARD: case VIR_DOMAIN_DEVICE_MEMBALLOON: case VIR_DOMAIN_DEVICE_NVRAM: diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 0a63741b9e..1b6cc36bc8 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -3303,6 +3303,64 @@ qemuDomainAttachInputDevice(virQEMUDriverPtr driver, } +int +qemuDomainAttachHubDevice(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainHubDefPtr hub) +{ + int ret = -1; + char *devstr = NULL; + qemuDomainObjPrivatePtr priv = vm->privateData; + virErrorPtr originalError = NULL; + bool releaseaddr = false; + + if (priv->usbaddrs) { + if (virDomainUSBAddressEnsure(priv->usbaddrs, &hub->info) < 0) + goto cleanup; + releaseaddr = true; + } + + if (qemuAssignDeviceHubAlias(vm->def, hub, -1) < 0) + goto cleanup; + + if (!(devstr = qemuBuildHubDevStr(vm->def, hub, priv->qemuCaps))) + goto cleanup; + + if (VIR_REALLOC_N(vm->def->hubs, vm->def->nhubs + 1) < 0) + goto cleanup; + + qemuDomainObjEnterMonitor(driver, vm); + if (qemuMonitorAddDevice(priv->mon, devstr) < 0) + goto exit_monitor; + + if (qemuDomainObjExitMonitor(driver, vm) < 0) { + releaseaddr = false; + goto cleanup; + } + + VIR_APPEND_ELEMENT_COPY_INPLACE(vm->def->hubs, vm->def->nhubs, hub); + + ret = 0; + releaseaddr = false; + + cleanup: + if (ret < 0) { + virErrorPreserveLast(&originalError); + if (releaseaddr) + qemuDomainReleaseDeviceAddress(vm, &hub->info, NULL); + virErrorRestore(&originalError); + } + + VIR_FREE(devstr); + return ret; + + exit_monitor: + if (qemuDomainObjExitMonitor(driver, vm) < 0) + releaseaddr = false; + goto cleanup; +} + + int qemuDomainAttachVsockDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, diff --git a/src/qemu/qemu_hotplug.h b/src/qemu/qemu_hotplug.h index 0297e42a98..444333c4df 100644 --- a/src/qemu/qemu_hotplug.h +++ b/src/qemu/qemu_hotplug.h @@ -135,10 +135,18 @@ int qemuDomainAttachInputDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainInputDefPtr input); +int qemuDomainAttachHubDevice(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainHubDefPtr hub); + int qemuDomainAttachVsockDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainVsockDefPtr vsock); +int qemuDomainAttachInputDevice(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainInputDefPtr input); + int qemuDomainAttachLease(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainLeaseDefPtr lease); -- 2.19.1

$SUBJ: s/implement/Implement On 10/12/18 4:50 AM, Han Han wrote:
Add the infrastructure to allow a USB Hub device to be hotplugged.
Signed-off-by: Han Han <hhan@redhat.com> --- src/qemu/qemu_driver.c | 9 ++++++- src/qemu/qemu_hotplug.c | 58 +++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_hotplug.h | 8 ++++++ 3 files changed, 74 insertions(+), 1 deletion(-)
[...]
diff --git a/src/qemu/qemu_hotplug.h b/src/qemu/qemu_hotplug.h index 0297e42a98..444333c4df 100644 --- a/src/qemu/qemu_hotplug.h +++ b/src/qemu/qemu_hotplug.h @@ -135,10 +135,18 @@ int qemuDomainAttachInputDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainInputDefPtr input);
+int qemuDomainAttachHubDevice(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainHubDefPtr hub); + int qemuDomainAttachVsockDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainVsockDefPtr vsock);
+int qemuDomainAttachInputDevice(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainInputDefPtr input); +
cut-n-paste error perhaps? I'll remove before pushing though... Reviewed-by: John Ferlan <jferlan@redhat.com> John
int qemuDomainAttachLease(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainLeaseDefPtr lease);

On Tue, Nov 6, 2018 at 6:34 AM John Ferlan <jferlan@redhat.com> wrote:
$SUBJ:
s/implement/Implement
On 10/12/18 4:50 AM, Han Han wrote:
Add the infrastructure to allow a USB Hub device to be hotplugged.
Signed-off-by: Han Han <hhan@redhat.com> --- src/qemu/qemu_driver.c | 9 ++++++- src/qemu/qemu_hotplug.c | 58 +++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_hotplug.h | 8 ++++++ 3 files changed, 74 insertions(+), 1 deletion(-)
[...]
diff --git a/src/qemu/qemu_hotplug.h b/src/qemu/qemu_hotplug.h index 0297e42a98..444333c4df 100644 --- a/src/qemu/qemu_hotplug.h +++ b/src/qemu/qemu_hotplug.h @@ -135,10 +135,18 @@ int qemuDomainAttachInputDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainInputDefPtr input);
+int qemuDomainAttachHubDevice(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainHubDefPtr hub); + int qemuDomainAttachVsockDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainVsockDefPtr vsock);
+int qemuDomainAttachInputDevice(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainInputDefPtr input); +
cut-n-paste error perhaps? I'll remove before pushing though...
Sorry for the silly mistake... I will remove it in new patch series.
Reviewed-by: John Ferlan <jferlan@redhat.com>
John
int qemuDomainAttachLease(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainLeaseDefPtr lease);
-- Best regards, ----------------------------------- Han Han Quality Engineer Redhat. Email: hhan@redhat.com Phone: +861065339333

https://bugzilla.redhat.com/show_bug.cgi?id=1375423 Signed-off-by: Han Han <hhan@redhat.com> --- src/qemu/qemu_driver.c | 5 ++- src/qemu/qemu_hotplug.c | 74 ++++++++++++++++++++++++++++++++++++++++- src/qemu/qemu_hotplug.h | 4 +++ 3 files changed, 81 insertions(+), 2 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index de764a7f1c..c8a6d98dc0 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -7822,11 +7822,14 @@ qemuDomainDetachDeviceLive(virDomainObjPtr vm, ret = qemuDomainDetachVsockDevice(vm, dev->data.vsock, async); break; + case VIR_DOMAIN_DEVICE_HUB: + ret = qemuDomainDetachHubDevice(vm, dev->data.hub, async); + break; + case VIR_DOMAIN_DEVICE_FS: case VIR_DOMAIN_DEVICE_SOUND: case VIR_DOMAIN_DEVICE_VIDEO: case VIR_DOMAIN_DEVICE_GRAPHICS: - case VIR_DOMAIN_DEVICE_HUB: case VIR_DOMAIN_DEVICE_SMARTCARD: case VIR_DOMAIN_DEVICE_MEMBALLOON: case VIR_DOMAIN_DEVICE_NVRAM: diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 1b6cc36bc8..87749ec011 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -4965,6 +4965,32 @@ qemuDomainRemoveRedirdevDevice(virQEMUDriverPtr driver, } +static int +qemuDomainRemoveHubDevice(virDomainObjPtr vm, + virDomainHubDefPtr dev) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + virQEMUDriverPtr driver = priv->driver; + virObjectEventPtr event = NULL; + size_t i; + + VIR_DEBUG("Removing hub device %s from domain %p %s", + dev->info.alias, vm, vm->def->name); + + event = virDomainEventDeviceRemovedNewFromObj(vm, dev->info.alias); + virObjectEventStateQueue(driver->domainEventState, event); + for (i = 0; i < vm->def->nhubs; i++) { + if (vm->def->hubs[i] == dev) + break; + } + qemuDomainReleaseDeviceAddress(vm, &dev->info, NULL); + + virDomainHubDefFree(vm->def->hubs[i]); + VIR_DELETE_ELEMENT(vm->def->hubs, i, vm->def->nhubs); + return 0; +} + + int qemuDomainRemoveDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, @@ -5016,13 +5042,16 @@ qemuDomainRemoveDevice(virQEMUDriverPtr driver, ret = qemuDomainRemoveVsockDevice(vm, dev->data.vsock); break; + case VIR_DOMAIN_DEVICE_HUB: + ret = qemuDomainRemoveHubDevice(vm, dev->data.hub); + break; + case VIR_DOMAIN_DEVICE_NONE: case VIR_DOMAIN_DEVICE_LEASE: case VIR_DOMAIN_DEVICE_FS: case VIR_DOMAIN_DEVICE_SOUND: case VIR_DOMAIN_DEVICE_VIDEO: case VIR_DOMAIN_DEVICE_GRAPHICS: - case VIR_DOMAIN_DEVICE_HUB: case VIR_DOMAIN_DEVICE_SMARTCARD: case VIR_DOMAIN_DEVICE_MEMBALLOON: case VIR_DOMAIN_DEVICE_NVRAM: @@ -6955,3 +6984,46 @@ qemuDomainDetachVsockDevice(virDomainObjPtr vm, qemuDomainResetDeviceRemoval(vm); return ret; } + + +int +qemuDomainDetachHubDevice(virDomainObjPtr vm, + virDomainHubDefPtr def, + bool async) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + virQEMUDriverPtr driver = priv->driver; + virDomainHubDefPtr hub; + int ret = -1; + int idx; + + if ((idx = virDomainHubDefFind(vm->def, def)) < 0) { + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("matching hub device not found")); + return -1; + } + hub = vm->def->hubs[idx]; + + if (!async) + qemuDomainMarkDeviceForRemoval(vm, &hub->info); + + qemuDomainObjEnterMonitor(driver, vm); + if (qemuMonitorDelDevice(priv->mon, hub->info.alias)) { + ignore_value(qemuDomainObjExitMonitor(driver, vm)); + goto cleanup; + } + if (qemuDomainObjExitMonitor(driver, vm) < 0) + goto cleanup; + + if (async) { + ret = 0; + } else { + if ((ret = qemuDomainWaitForDeviceRemoval(vm)) == 1) + ret = qemuDomainRemoveHubDevice(vm, hub); + } + + cleanup: + if (!async) + qemuDomainResetDeviceRemoval(vm); + return ret; +} diff --git a/src/qemu/qemu_hotplug.h b/src/qemu/qemu_hotplug.h index 444333c4df..0f205ff54b 100644 --- a/src/qemu/qemu_hotplug.h +++ b/src/qemu/qemu_hotplug.h @@ -208,4 +208,8 @@ int qemuDomainDetachInputDevice(virDomainObjPtr vm, int qemuDomainDetachVsockDevice(virDomainObjPtr vm, virDomainVsockDefPtr dev, bool async); + +int qemuDomainDetachHubDevice(virDomainObjPtr vm, + virDomainHubDefPtr def, + bool async); #endif /* __QEMU_HOTPLUG_H__ */ -- 2.19.1

$SUBJ: s/implement/Implement On 10/12/18 4:50 AM, Han Han wrote:
Add the infrastructure to allow a USB Hub device to be hot unplugged.
Signed-off-by: Han Han <hhan@redhat.com> --- src/qemu/qemu_driver.c | 5 ++- src/qemu/qemu_hotplug.c | 74 ++++++++++++++++++++++++++++++++++++++++- src/qemu/qemu_hotplug.h | 4 +++ 3 files changed, 81 insertions(+), 2 deletions(-)
This is where things get a bit dicey. Are you sure we can allow this given that we automagically create hub devices when there's too many USB devices, see: tests/qemuxml2argvdata/usb-hub-autoadd-deluxe.args tests/qemuxml2argvdata/usb-hub-autoadd-deluxe.xml and in the code qemuDomainUSBAddressAddHubs? Note that the test XML doesn't have a hub device defined, but yet some are created. If someone decides to hot unplug one that has something plugged into it (e.g. in the case of that test XML, some USB Input device), then what happens? Can you test that and ensure the results that you get? I see you've essentially copied the steps that an Input device would take; however, I'd suspect a Hub device is a bit more special and we may need to process the various USB devices to make sure there isn't one using the Hub device by port... Even worse if it's port=1 and there's a port=1.1 around that equates to more ports (like from the test). The question becomes - can we determine which port a USB device is using via the guest status/live XML? Would the qemu del device error out or happily accept deleting a hub with attached ports? Or would it just drop all those attached devices? Logically what's here would appear to work and is essentially similar to the Input devices code, so from that aspect things look OK - it's this one (hah) minor detail. Tks - John
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index de764a7f1c..c8a6d98dc0 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -7822,11 +7822,14 @@ qemuDomainDetachDeviceLive(virDomainObjPtr vm, ret = qemuDomainDetachVsockDevice(vm, dev->data.vsock, async); break;
+ case VIR_DOMAIN_DEVICE_HUB: + ret = qemuDomainDetachHubDevice(vm, dev->data.hub, async); + break; + case VIR_DOMAIN_DEVICE_FS: case VIR_DOMAIN_DEVICE_SOUND: case VIR_DOMAIN_DEVICE_VIDEO: case VIR_DOMAIN_DEVICE_GRAPHICS: - case VIR_DOMAIN_DEVICE_HUB: case VIR_DOMAIN_DEVICE_SMARTCARD: case VIR_DOMAIN_DEVICE_MEMBALLOON: case VIR_DOMAIN_DEVICE_NVRAM: diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 1b6cc36bc8..87749ec011 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -4965,6 +4965,32 @@ qemuDomainRemoveRedirdevDevice(virQEMUDriverPtr driver, }
+static int +qemuDomainRemoveHubDevice(virDomainObjPtr vm, + virDomainHubDefPtr dev) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + virQEMUDriverPtr driver = priv->driver; + virObjectEventPtr event = NULL; + size_t i; + + VIR_DEBUG("Removing hub device %s from domain %p %s", + dev->info.alias, vm, vm->def->name); + + event = virDomainEventDeviceRemovedNewFromObj(vm, dev->info.alias); + virObjectEventStateQueue(driver->domainEventState, event); + for (i = 0; i < vm->def->nhubs; i++) { + if (vm->def->hubs[i] == dev) + break; + } + qemuDomainReleaseDeviceAddress(vm, &dev->info, NULL); + + virDomainHubDefFree(vm->def->hubs[i]); + VIR_DELETE_ELEMENT(vm->def->hubs, i, vm->def->nhubs); + return 0; +} + + int qemuDomainRemoveDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, @@ -5016,13 +5042,16 @@ qemuDomainRemoveDevice(virQEMUDriverPtr driver, ret = qemuDomainRemoveVsockDevice(vm, dev->data.vsock); break;
+ case VIR_DOMAIN_DEVICE_HUB: + ret = qemuDomainRemoveHubDevice(vm, dev->data.hub); + break; + case VIR_DOMAIN_DEVICE_NONE: case VIR_DOMAIN_DEVICE_LEASE: case VIR_DOMAIN_DEVICE_FS: case VIR_DOMAIN_DEVICE_SOUND: case VIR_DOMAIN_DEVICE_VIDEO: case VIR_DOMAIN_DEVICE_GRAPHICS: - case VIR_DOMAIN_DEVICE_HUB: case VIR_DOMAIN_DEVICE_SMARTCARD: case VIR_DOMAIN_DEVICE_MEMBALLOON: case VIR_DOMAIN_DEVICE_NVRAM: @@ -6955,3 +6984,46 @@ qemuDomainDetachVsockDevice(virDomainObjPtr vm, qemuDomainResetDeviceRemoval(vm); return ret; } + + +int +qemuDomainDetachHubDevice(virDomainObjPtr vm, + virDomainHubDefPtr def, + bool async) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + virQEMUDriverPtr driver = priv->driver; + virDomainHubDefPtr hub; + int ret = -1; + int idx; + + if ((idx = virDomainHubDefFind(vm->def, def)) < 0) { + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("matching hub device not found")); + return -1; + } + hub = vm->def->hubs[idx]; + + if (!async) + qemuDomainMarkDeviceForRemoval(vm, &hub->info); + + qemuDomainObjEnterMonitor(driver, vm); + if (qemuMonitorDelDevice(priv->mon, hub->info.alias)) { + ignore_value(qemuDomainObjExitMonitor(driver, vm)); + goto cleanup; + } + if (qemuDomainObjExitMonitor(driver, vm) < 0) + goto cleanup; + + if (async) { + ret = 0; + } else { + if ((ret = qemuDomainWaitForDeviceRemoval(vm)) == 1) + ret = qemuDomainRemoveHubDevice(vm, hub); + } + + cleanup: + if (!async) + qemuDomainResetDeviceRemoval(vm); + return ret; +} diff --git a/src/qemu/qemu_hotplug.h b/src/qemu/qemu_hotplug.h index 444333c4df..0f205ff54b 100644 --- a/src/qemu/qemu_hotplug.h +++ b/src/qemu/qemu_hotplug.h @@ -208,4 +208,8 @@ int qemuDomainDetachInputDevice(virDomainObjPtr vm, int qemuDomainDetachVsockDevice(virDomainObjPtr vm, virDomainVsockDefPtr dev, bool async); + +int qemuDomainDetachHubDevice(virDomainObjPtr vm, + virDomainHubDefPtr def, + bool async); #endif /* __QEMU_HOTPLUG_H__ */

On Tue, Nov 6, 2018 at 6:47 AM John Ferlan <jferlan@redhat.com> wrote:
$SUBJ:
s/implement/Implement
On 10/12/18 4:50 AM, Han Han wrote:
Add the infrastructure to allow a USB Hub device to be hot unplugged.
Signed-off-by: Han Han <hhan@redhat.com> --- src/qemu/qemu_driver.c | 5 ++- src/qemu/qemu_hotplug.c | 74 ++++++++++++++++++++++++++++++++++++++++- src/qemu/qemu_hotplug.h | 4 +++ 3 files changed, 81 insertions(+), 2 deletions(-)
This is where things get a bit dicey. Are you sure we can allow this given that we automagically create hub devices when there's too many USB devices, see:
tests/qemuxml2argvdata/usb-hub-autoadd-deluxe.args tests/qemuxml2argvdata/usb-hub-autoadd-deluxe.xml
and in the code qemuDomainUSBAddressAddHubs?
Note that the test XML doesn't have a hub device defined, but yet some are created. If someone decides to hot unplug one that has something plugged into it (e.g. in the case of that test XML, some USB Input device), then what happens?
Can you test that and ensure the results that you get?
Currently, if you hot-unplug a hub with usb devices attached, these attached usb device will be removed, too. e.g: Start a VM with a hub. 8 usb mouse attached to the hub(Port 3.1~3.8): # virsh qemu-monitor-command rhel7 --hmp info usb Device 0.2, Port 3, Speed 12 Mb/s, Product QEMU USB Hub, ID: hub0 Device 0.2, Port 1, Speed 480 Mb/s, Product QEMU USB Tablet, ID: input0 Device 0.3, Port 3.1, Speed 12 Mb/s, Product QEMU USB Mouse, ID: input3 Device 0.4, Port 3.2, Speed 12 Mb/s, Product QEMU USB Mouse, ID: input4 Device 0.5, Port 3.3, Speed 12 Mb/s, Product QEMU USB Mouse, ID: input5 Device 0.6, Port 3.4, Speed 12 Mb/s, Product QEMU USB Mouse, ID: input6 Device 0.7, Port 3.5, Speed 12 Mb/s, Product QEMU USB Mouse, ID: input7 Device 0.8, Port 3.6, Speed 12 Mb/s, Product QEMU USB Mouse, ID: input8 Device 0.9, Port 3.7, Speed 12 Mb/s, Product QEMU USB Mouse, ID: input9 Device 0.10, Port 3.8, Speed 12 Mb/s, Product QEMU USB Mouse, ID: input10 Device 0.0, Port 2, Speed 1.5 Mb/s, Product USB Redirection Device, ID: redir0 Then hot-unplug the hub by hmp command: # virsh qemu-monitor-command rhel7 --hmp device_del hub0 All usb mouse devices attached to hub disappeared in the live xml. And no error in the qemu VM log. However, I am not sure if other usb devices attached to hub will be removed without error... I see you've essentially copied the steps that an Input device would
take; however, I'd suspect a Hub device is a bit more special and we may need to process the various USB devices to make sure there isn't one using the Hub device by port... Even worse if it's port=1 and there's a port=1.1 around that equates to more ports (like from the test).
The question becomes - can we determine which port a USB device is using via the guest status/live XML? Would the qemu del device error out or happily accept deleting a hub with attached ports? Or would it just drop all those attached devices?
I think that is not the expected result above. It better to refer to the scsi controller in libvirt. When you hot-unplug a scsi controller with scsi disk attached, you will get: # virsh detach-device rhel7 /tmp/scsi.xml error: Failed to detach device from /tmp/scsi.xml error: operation failed: device cannot be detached: device is busy
Thanks for your review and valuable advice.
Logically what's here would appear to work and is essentially similar to the Input devices code, so from that aspect things look OK - it's this one (hah) minor detail.
Tks -
John
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index de764a7f1c..c8a6d98dc0 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -7822,11 +7822,14 @@ qemuDomainDetachDeviceLive(virDomainObjPtr vm, ret = qemuDomainDetachVsockDevice(vm, dev->data.vsock, async); break;
+ case VIR_DOMAIN_DEVICE_HUB: + ret = qemuDomainDetachHubDevice(vm, dev->data.hub, async); + break; + case VIR_DOMAIN_DEVICE_FS: case VIR_DOMAIN_DEVICE_SOUND: case VIR_DOMAIN_DEVICE_VIDEO: case VIR_DOMAIN_DEVICE_GRAPHICS: - case VIR_DOMAIN_DEVICE_HUB: case VIR_DOMAIN_DEVICE_SMARTCARD: case VIR_DOMAIN_DEVICE_MEMBALLOON: case VIR_DOMAIN_DEVICE_NVRAM: diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 1b6cc36bc8..87749ec011 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -4965,6 +4965,32 @@ qemuDomainRemoveRedirdevDevice(virQEMUDriverPtr driver, }
+static int +qemuDomainRemoveHubDevice(virDomainObjPtr vm, + virDomainHubDefPtr dev) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + virQEMUDriverPtr driver = priv->driver; + virObjectEventPtr event = NULL; + size_t i; + + VIR_DEBUG("Removing hub device %s from domain %p %s", + dev->info.alias, vm, vm->def->name); + + event = virDomainEventDeviceRemovedNewFromObj(vm, dev->info.alias); + virObjectEventStateQueue(driver->domainEventState, event); + for (i = 0; i < vm->def->nhubs; i++) { + if (vm->def->hubs[i] == dev) + break; + } + qemuDomainReleaseDeviceAddress(vm, &dev->info, NULL); + + virDomainHubDefFree(vm->def->hubs[i]); + VIR_DELETE_ELEMENT(vm->def->hubs, i, vm->def->nhubs); + return 0; +} + + int qemuDomainRemoveDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, @@ -5016,13 +5042,16 @@ qemuDomainRemoveDevice(virQEMUDriverPtr driver, ret = qemuDomainRemoveVsockDevice(vm, dev->data.vsock); break;
+ case VIR_DOMAIN_DEVICE_HUB: + ret = qemuDomainRemoveHubDevice(vm, dev->data.hub); + break; + case VIR_DOMAIN_DEVICE_NONE: case VIR_DOMAIN_DEVICE_LEASE: case VIR_DOMAIN_DEVICE_FS: case VIR_DOMAIN_DEVICE_SOUND: case VIR_DOMAIN_DEVICE_VIDEO: case VIR_DOMAIN_DEVICE_GRAPHICS: - case VIR_DOMAIN_DEVICE_HUB: case VIR_DOMAIN_DEVICE_SMARTCARD: case VIR_DOMAIN_DEVICE_MEMBALLOON: case VIR_DOMAIN_DEVICE_NVRAM: @@ -6955,3 +6984,46 @@ qemuDomainDetachVsockDevice(virDomainObjPtr vm, qemuDomainResetDeviceRemoval(vm); return ret; } + + +int +qemuDomainDetachHubDevice(virDomainObjPtr vm, + virDomainHubDefPtr def, + bool async) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + virQEMUDriverPtr driver = priv->driver; + virDomainHubDefPtr hub; + int ret = -1; + int idx; + + if ((idx = virDomainHubDefFind(vm->def, def)) < 0) { + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("matching hub device not found")); + return -1; + } + hub = vm->def->hubs[idx]; + + if (!async) + qemuDomainMarkDeviceForRemoval(vm, &hub->info); + + qemuDomainObjEnterMonitor(driver, vm); + if (qemuMonitorDelDevice(priv->mon, hub->info.alias)) { + ignore_value(qemuDomainObjExitMonitor(driver, vm)); + goto cleanup; + } + if (qemuDomainObjExitMonitor(driver, vm) < 0) + goto cleanup; + + if (async) { + ret = 0; + } else { + if ((ret = qemuDomainWaitForDeviceRemoval(vm)) == 1) + ret = qemuDomainRemoveHubDevice(vm, hub); + } + + cleanup: + if (!async) + qemuDomainResetDeviceRemoval(vm); + return ret; +} diff --git a/src/qemu/qemu_hotplug.h b/src/qemu/qemu_hotplug.h index 444333c4df..0f205ff54b 100644 --- a/src/qemu/qemu_hotplug.h +++ b/src/qemu/qemu_hotplug.h @@ -208,4 +208,8 @@ int qemuDomainDetachInputDevice(virDomainObjPtr vm, int qemuDomainDetachVsockDevice(virDomainObjPtr vm, virDomainVsockDefPtr dev, bool async); + +int qemuDomainDetachHubDevice(virDomainObjPtr vm, + virDomainHubDefPtr def, + bool async); #endif /* __QEMU_HOTPLUG_H__ */
-- Best regards, ----------------------------------- Han Han Quality Engineer Redhat. Email: hhan@redhat.com Phone: +861065339333

Signed-off-by: Han Han <hhan@redhat.com> --- docs/news.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/news.xml b/docs/news.xml index dc08c96352..0efad21758 100644 --- a/docs/news.xml +++ b/docs/news.xml @@ -35,6 +35,11 @@ <libvirt> <release version="v4.9.0" date="unreleased"> <section title="New features"> + <change> + <summary> + qemu: Add support for cold(un)plugging and hot(un)plugging usb hub device + </summary> + </change> </section> <section title="Improvements"> </section> -- 2.19.1

On 10/12/18 4:50 AM, Han Han wrote:
Signed-off-by: Han Han <hhan@redhat.com> --- docs/news.xml | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/docs/news.xml b/docs/news.xml index dc08c96352..0efad21758 100644 --- a/docs/news.xml +++ b/docs/news.xml @@ -35,6 +35,11 @@ <libvirt> <release version="v4.9.0" date="unreleased"> <section title="New features"> + <change> + <summary> + qemu: Add support for cold(un)plugging and hot(un)plugging usb hub device + </summary> + </change>
How about: <summary> Add active and inactive device add or remove for QEMU USB Hub devices </summary> <description> Add the ability to attach or detach a USB Hub device either for active or inactive guests. </description> ? John
</section> <section title="Improvements"> </section>

On Tue, Nov 6, 2018 at 6:52 AM John Ferlan <jferlan@redhat.com> wrote:
On 10/12/18 4:50 AM, Han Han wrote:
Signed-off-by: Han Han <hhan@redhat.com> --- docs/news.xml | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/docs/news.xml b/docs/news.xml index dc08c96352..0efad21758 100644 --- a/docs/news.xml +++ b/docs/news.xml @@ -35,6 +35,11 @@ <libvirt> <release version="v4.9.0" date="unreleased"> <section title="New features"> + <change> + <summary> + qemu: Add support for cold(un)plugging and hot(un)plugging usb hub device + </summary> + </change>
How about:
<summary> Add active and inactive device add or remove for QEMU USB Hub devices </summary> <description> Add the ability to attach or detach a USB Hub device either for active or inactive guests. </description>
Well. Your advice is nice. 'active' or 'inactive' is to describe the guests not the devices.
?
John
</section> <section title="Improvements"> </section>
-- Best regards, ----------------------------------- Han Han Quality Engineer Redhat. Email: hhan@redhat.com Phone: +861065339333
participants (2)
-
Han Han
-
John Ferlan