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,