On 02/27/2016 04:50 AM, Osier Yang wrote: > Attaching redirdev device has been supported for a while, but detaching > is not never implemented. > > Simple procedure to test: > > % lsusb > Bus 001 Device 014: ID 0781:5567 SanDisk Corp. Cruzer Blade > > % usbredirserver -p 4000 0781:5567 > > % virsh attach-device test usb.xml > > % cat usb.xml > <redirdev bus='usb' type='tcp'> > <source mode='connect' host='192.168.84.6' service='4000'/> > </redirdev> > > % virsh detach-device test usb.xml > > % virsh qemu-monitor-command test --pretty '{"execute": "query-chardev"}' | grep 4000 > > On success, the chardev should not seen in output of above command. > --- > src/conf/domain_conf.c | 67 +++++++++++++++++++++++++++++++++ > src/conf/domain_conf.h | 4 ++ > src/libvirt_private.syms | 3 ++ > src/qemu/qemu_driver.c | 4 +- > src/qemu/qemu_hotplug.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++- > src/qemu/qemu_hotplug.h | 3 ++ > 6 files changed, 176 insertions(+), 2 deletions(-) > > diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c > index 3b15cb4..d304232 100644 > --- a/src/conf/domain_conf.c > +++ b/src/conf/domain_conf.c > @@ -13825,6 +13825,73 @@ virDomainMemoryRemove(virDomainDefPtr def, > return ret; > } > A little intro - inputs and what it returns (-1 or the index into the redirdevs for the found redirdev. > +ssize_t > +virDomainRedirdevFind(virDomainDefPtr def, > + virDomainRedirdevDefPtr redirdev) I see you're essentially copying the virDomainRNGFind... and friends... > +{ > + size_t i; > + > + for (i = 0; i < def->nredirdevs; i++) { > + virDomainRedirdevDefPtr tmp = def->redirdevs[i]; > + > + if (redirdev->bus != tmp->bus) > + continue; > + > + virDomainChrSourceDef source_chr = redirdev->source.chr; > + virDomainChrSourceDef tmp_chr = tmp->source.chr; > + > + if (source_chr.type != tmp_chr.type) > + continue; Does it matter if the <boot order='#'/> was set in the XML? If it was the boot device, then should it be allowed to be removed? Found yes, but removed? I guess that decision is below us though. > + > + switch (source_chr.type) { > + case VIR_DOMAIN_CHR_TYPE_TCP: > + if (STRNEQ_NULLABLE(source_chr.data.tcp.host, > + tmp_chr.data.tcp.host)) > + continue; > + if (STRNEQ_NULLABLE(source_chr.data.tcp.service, > + tmp_chr.data.tcp.service)) > + continue; > + if (source_chr.data.tcp.listen != tmp_chr.data.tcp.listen) > + continue; > + if (source_chr.data.tcp.protocol != tmp_chr.data.tcp.protocol) > + continue; > + break; > + > + case VIR_DOMAIN_CHR_TYPE_SPICEVMC: > + if (source_chr.data.spicevmc != tmp_chr.data.spicevmc) > + continue; > + break; > + > + default: > + /* Unlikely, currently redirdev only supports character device of > + * type "tcp" and "spicevmc". > + */ Shouldn't this then be a continue; here? IOW: For anything not being supported we don't want to take the next step, right? I know you're following the RNG code... > + break; > + } > + > + if (redirdev->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE && Don't think it matters for checking against TYPE_NONE since the following function does check that... Again more RNG-alike... > + !virDomainDeviceInfoAddressIsEqual(&redirdev->info, &tmp->info)) > + continue; Should I assume if we get this far then this is *the* device to be removed? And there's only one, right? Hence just "return i;" here (yes, different than rng, more obvious (at least to me) and thus removes the need for "if (i < def->nredirdevs)" > + > + break; > + } > + > + if (i < def->nredirdevs) > + return i; > + > + return -1; > +} > + > +virDomainRedirdevDefPtr > +virDomainRedirdevRemove(virDomainDefPtr def, > + size_t idx) I see this code is just a copy of virDomainRNGRemove; however, I'm not convinced that's the best mechanism... The only current caller doesn't check the return value either, although I do note that the RNG code paths to virDomainRNGRemove have a path that would use the returned ret value... > +{ > + virDomainRedirdevDefPtr ret = def->redirdevs[idx]; > + > + VIR_DELETE_ELEMENT(def->redirdevs, idx, def->nredirdevs); > + > + return ret; > +} > > char * > virDomainDefGetDefaultEmulator(virDomainDefPtr def, > diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h > index 1de3be3..03c0155 100644 > --- a/src/conf/domain_conf.h > +++ b/src/conf/domain_conf.h > @@ -2538,6 +2538,10 @@ void virDomainHostdevDefClear(virDomainHostdevDefPtr def); > void virDomainHostdevDefFree(virDomainHostdevDefPtr def); > void virDomainHubDefFree(virDomainHubDefPtr def); > void virDomainRedirdevDefFree(virDomainRedirdevDefPtr def); > +ssize_t virDomainRedirdevFind(virDomainDefPtr def, > + virDomainRedirdevDefPtr redirdev); > +virDomainRedirdevDefPtr virDomainRedirdevRemove(virDomainDefPtr def, > + size_t idx); > void virDomainRedirFilterDefFree(virDomainRedirFilterDefPtr def); > void virDomainShmemDefFree(virDomainShmemDefPtr def); > void virDomainDeviceDefFree(virDomainDeviceDefPtr def); > diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms > index 4b40612..ad7d82c 100644 > --- a/src/libvirt_private.syms > +++ b/src/libvirt_private.syms > @@ -423,6 +423,9 @@ virDomainPMSuspendedReasonTypeFromString; > virDomainPMSuspendedReasonTypeToString; > virDomainRedirdevBusTypeFromString; > virDomainRedirdevBusTypeToString; > +virDomainRedirdevDefFree; > +virDomainRedirdevFind; > +virDomainRedirdevRemove; > virDomainRNGBackendTypeToString; > virDomainRNGDefFree; > virDomainRNGFind; > diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c > index 45ff3c0..8905af6 100644 > --- a/src/qemu/qemu_driver.c > +++ b/src/qemu/qemu_driver.c > @@ -7736,6 +7736,9 @@ qemuDomainDetachDeviceLive(virDomainObjPtr vm, So you're removing from Live, but not Config? Is there a reason? You've followed the RNG code so far... > case VIR_DOMAIN_DEVICE_MEMORY: > ret = qemuDomainDetachMemoryDevice(driver, vm, dev->data.memory); > break; > + case VIR_DOMAIN_DEVICE_REDIRDEV: > + ret = qemuDomainDetachRedirdevDevice(driver, vm, dev->data.redirdev); > + break; > > case VIR_DOMAIN_DEVICE_FS: > case VIR_DOMAIN_DEVICE_INPUT: > @@ -7748,7 +7751,6 @@ qemuDomainDetachDeviceLive(virDomainObjPtr vm, > case VIR_DOMAIN_DEVICE_MEMBALLOON: > case VIR_DOMAIN_DEVICE_NVRAM: > case VIR_DOMAIN_DEVICE_SHMEM: > - case VIR_DOMAIN_DEVICE_REDIRDEV: > case VIR_DOMAIN_DEVICE_NONE: > case VIR_DOMAIN_DEVICE_TPM: > case VIR_DOMAIN_DEVICE_PANIC: > diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c > index dc76268..bbe8aa7 100644 > --- a/src/qemu/qemu_hotplug.c > +++ b/src/qemu/qemu_hotplug.c > @@ -3287,6 +3287,49 @@ qemuDomainRemoveRNGDevice(virQEMUDriverPtr driver, > } > > > +static int > +qemuDomainRemoveRedirdevDevice(virQEMUDriverPtr driver, > + virDomainObjPtr vm, > + virDomainRedirdevDefPtr redirdev) > +{ > + qemuDomainObjPrivatePtr priv = vm->privateData; > + virObjectEventPtr event; > + char *charAlias = NULL; > + ssize_t idx; > + int rc; > + int ret = -1; > + > + VIR_DEBUG("Removing redirdev device %s from domain %p %s", > + redirdev->info.alias, vm, vm->def->name); > + > + if (virAsprintf(&charAlias, "char%s", redirdev->info.alias) < 0) > + return -1; > + > + qemuDomainObjEnterMonitor(driver, vm); > + rc = qemuMonitorDetachCharDev(priv->mon, charAlias); > + if (qemuDomainObjExitMonitor(driver, vm) < 0) > + goto cleanup; > + > + virDomainAuditRedirdev(vm, redirdev, "detach", rc == 0); > + > + if (rc < 0) > + goto cleanup; > + > + event = virDomainEventDeviceRemovedNewFromObj(vm, redirdev->info.alias); > + qemuDomainEventQueue(driver, event); > + > + if ((idx = virDomainRedirdevFind(vm->def, redirdev)) >= 0) > + virDomainRedirdevRemove(vm->def, idx); > + qemuDomainReleaseDeviceAddress(vm, &redirdev->info, NULL); > + virDomainRedirdevDefFree(redirdev); There's something inefficient about this... The only reason to call the Find routine is to get the 'idx' value in order to pass to the Remove function which can return a pointer to the redirdev that we already have (and in one path have already gone through the Find logic). I know you're copying RNG, but this device isn't the same as that. Perhaps it'd be better to have a void qemuDomainRedirdevRemove(vm->def, redirdev) to handle all the logic. Also w/r/t qemuDomainReleaseDeviceAddress - I find it interesting that the *Chr* processing handles that in the DetachChrDevice API, while the RNG handles it in RemoveRNG. Additionally, both of those Attach*Device paths have error paths which will call the ReleaseDeviceAddress, but the AttachRedirdevDevice doesn't have similar logic. So the question becomes - is it a required call for this path? > + > + ret = 0; > + > + cleanup: > + VIR_FREE(charAlias); > + return ret; > +} > + > int > qemuDomainRemoveDevice(virQEMUDriverPtr driver, > virDomainObjPtr vm, > @@ -3318,6 +3361,10 @@ qemuDomainRemoveDevice(virQEMUDriverPtr driver, > ret = qemuDomainRemoveMemoryDevice(driver, vm, dev->data.memory); > break; > > + case VIR_DOMAIN_DEVICE_REDIRDEV: > + ret = qemuDomainRemoveRedirdevDevice(driver, vm, dev->data.redirdev); > + break; > + > case VIR_DOMAIN_DEVICE_NONE: > case VIR_DOMAIN_DEVICE_LEASE: > case VIR_DOMAIN_DEVICE_FS: > @@ -3327,7 +3374,6 @@ qemuDomainRemoveDevice(virQEMUDriverPtr driver, > case VIR_DOMAIN_DEVICE_WATCHDOG: > case VIR_DOMAIN_DEVICE_GRAPHICS: > case VIR_DOMAIN_DEVICE_HUB: > - case VIR_DOMAIN_DEVICE_REDIRDEV: > case VIR_DOMAIN_DEVICE_SMARTCARD: > case VIR_DOMAIN_DEVICE_MEMBALLOON: > case VIR_DOMAIN_DEVICE_NVRAM: > @@ -4318,3 +4364,52 @@ qemuDomainDetachMemoryDevice(virQEMUDriverPtr driver, > qemuDomainResetDeviceRemoval(vm); > return ret; > } > + > +int > +qemuDomainDetachRedirdevDevice(virQEMUDriverPtr driver, > + virDomainObjPtr vm, > + virDomainRedirdevDefPtr redirdev) > +{ > + qemuDomainObjPrivatePtr priv = vm->privateData; > + virDomainRedirdevDefPtr tmp; > + ssize_t idx; > + int rc; > + int ret = -1; > + > + if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE)) { > + virReportError(VIR_ERR_OPERATION_INVALID, "%s", > + _("qemu does not support -device")); > + return -1; > + } > + > + if ((idx = virDomainRedirdevFind(vm->def, redirdev)) < 0) { > + virReportError(VIR_ERR_OPERATION_INVALID, "%s", > + _("device not present in domain configuration")); > + return -1; > + } > + > + tmp = vm->def->redirdevs[idx]; > + > + if (!tmp->info.alias) { > + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", > + _("alias not set for redirdev device")); > + return -1; > + } > + > + qemuDomainMarkDeviceForRemoval(vm, &tmp->info); > + > + qemuDomainObjEnterMonitor(driver, vm); > + rc = qemuMonitorDelDevice(priv->mon, tmp->info.alias); > + if (qemuDomainObjExitMonitor(driver, vm) || rc < 0) s/) ||/) < 0 ||/ That is the ExitMonitor needs to check error status... Although I see the RNG device has the same issue <sigh> - need a separate patch for that. > + goto cleanup; > + > + rc = qemuDomainWaitForDeviceRemoval(vm); > + if (rc == 0 || rc == 1) > + ret = qemuDomainRemoveRedirdevDevice(driver, vm, tmp); So interestingly the DetachChrDevice will: qemuDomainReleaseDeviceAddress(vm, &tmpChr->info, NULL); ret = qemuDomainRemoveChrDevice(driver, vm, tmpChr); But DetachRNG takes a different option; however, I'm still left wondering if it's necessary in this path. John > + else > + ret = 0; > + > + cleanup: > + qemuDomainResetDeviceRemoval(vm); > + return ret; > +} > diff --git a/src/qemu/qemu_hotplug.h b/src/qemu/qemu_hotplug.h > index 4140da3..4ef42e9 100644 > --- a/src/qemu/qemu_hotplug.h > +++ b/src/qemu/qemu_hotplug.h > @@ -51,6 +51,9 @@ int qemuDomainAttachNetDevice(virConnectPtr conn, > int qemuDomainAttachRedirdevDevice(virQEMUDriverPtr driver, > virDomainObjPtr vm, > virDomainRedirdevDefPtr hostdev); > +int qemuDomainDetachRedirdevDevice(virQEMUDriverPtr driver, > + virDomainObjPtr vm, > + virDomainRedirdevDefPtr redirdev); > int qemuDomainAttachHostDevice(virConnectPtr conn, > virQEMUDriverPtr driver, > virDomainObjPtr vm, > -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list