[libvirt] [PATCH v2 0/7] Redirdev attach & detach

v2 of: https://www.redhat.com/archives/libvir-list/2016-June/msg00615.html diff to v1: - Peter's review worked in - Pushed ACKed patches - Found one silly bug (patch 7/7) Michal Privoznik (7): domain_conf: Validate redirdev after parsing virDomainRedirdevDef: Introduce find & remove routines virDomainDeviceInfoIterateInternal: Iterate through redirdevs too qemuDomainAttachDeviceConfig: Allow redirdev coldplug qemuDomainDetachDeviceConfig: Allow cold unplug of redirdevs qemuDomainRemoveDevice: Enable live redirdev detach qemuDomainDetachDeviceFlags: Pass live device XML to live detach code src/conf/domain_conf.c | 105 ++++++++++++++++++++++++++++++++++++----------- src/conf/domain_conf.h | 4 ++ src/libvirt_private.syms | 2 + src/qemu/qemu_driver.c | 35 ++++++++++++---- src/qemu/qemu_hotplug.c | 76 +++++++++++++++++++++++++++++++++- src/qemu/qemu_hotplug.h | 3 ++ 6 files changed, 193 insertions(+), 32 deletions(-) -- 2.8.4

There's currently just one limitation: redirdevs that want to go on USB bus require a USB controller, surprisingly. At the same time, since I'm using virDomainDefHasUSB() in this new validator function, it has to be moved a few lines up and also its header needed to be changed a bit: it is now taking a const pointer to domain def since it's not changing anything in there. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/conf/domain_conf.c | 56 ++++++++++++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 85f6e31..c75279d 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -4567,14 +4567,45 @@ virDomainDiskDefValidate(const virDomainDiskDef *disk) return 0; } +static bool +virDomainDefHasUSB(const virDomainDef *def) +{ + size_t i; + + for (i = 0; i < def->ncontrollers; i++) { + if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_USB && + def->controllers[i]->model != VIR_DOMAIN_CONTROLLER_MODEL_USB_NONE) + return true; + } + + return false; +} + +static int +virDomainRedirdevDefValidate(const virDomainDef *def, + const virDomainRedirdevDef *redirdev) +{ + if (redirdev->bus == VIR_DOMAIN_REDIRDEV_BUS_USB && + !virDomainDefHasUSB(def)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Can't add redirected USB device: " + "USB is disabled for this domain")); + return -1; + } + + return 0; +} + static int virDomainDeviceDefValidateInternal(const virDomainDeviceDef *dev, - const virDomainDef *def ATTRIBUTE_UNUSED) + const virDomainDef *def) { switch ((virDomainDeviceType) dev->type) { case VIR_DOMAIN_DEVICE_DISK: return virDomainDiskDefValidate(dev->data.disk); + case VIR_DOMAIN_DEVICE_REDIRDEV: + return virDomainRedirdevDefValidate(def, dev->data.redirdev); case VIR_DOMAIN_DEVICE_LEASE: case VIR_DOMAIN_DEVICE_FS: case VIR_DOMAIN_DEVICE_NET: @@ -4586,7 +4617,6 @@ virDomainDeviceDefValidateInternal(const virDomainDeviceDef *dev, case VIR_DOMAIN_DEVICE_CONTROLLER: case VIR_DOMAIN_DEVICE_GRAPHICS: case VIR_DOMAIN_DEVICE_HUB: - case VIR_DOMAIN_DEVICE_REDIRDEV: case VIR_DOMAIN_DEVICE_SMARTCARD: case VIR_DOMAIN_DEVICE_CHR: case VIR_DOMAIN_DEVICE_MEMBALLOON: @@ -17060,14 +17090,6 @@ virDomainDefParseXML(xmlDocPtr xml, if (!redirdev) goto error; - if (redirdev->bus == VIR_DOMAIN_REDIRDEV_BUS_USB && usb_none) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("Can't add redirected USB device: " - "USB is disabled for this domain")); - virDomainRedirdevDefFree(redirdev); - goto error; - } - def->redirdevs[def->nredirdevs++] = redirdev; } VIR_FREE(nodes); @@ -23578,20 +23600,6 @@ virDomainObjFormat(virDomainXMLOptionPtr xmlopt, } static bool -virDomainDefHasUSB(virDomainDefPtr def) -{ - size_t i; - - for (i = 0; i < def->ncontrollers; i++) { - if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_USB && - def->controllers[i]->model != VIR_DOMAIN_CONTROLLER_MODEL_USB_NONE) - return true; - } - - return false; -} - -static bool virDomainDeviceIsUSB(virDomainDeviceDefPtr dev) { int t = dev->type; -- 2.8.4

On 06/10/2016 11:32 AM, Michal Privoznik wrote:
There's currently just one limitation: redirdevs that want to go on USB bus require a USB controller, surprisingly. At the same time, since I'm using virDomainDefHasUSB() in this new validator function, it has to be moved a few lines up and also its header needed to be changed a bit: it is now taking a const pointer to domain def since it's not changing anything in there.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/conf/domain_conf.c | 56 ++++++++++++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 24 deletions(-)
The commit message makes me feel like I missed part of the story at least with the way it starts out... ACK (although some may say this could have been two patches - one to move the function and one to add the virDomainRedirdevDefValidate) John I made two nit notes below... Not anything that would hold this up.
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 85f6e31..c75279d 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -4567,14 +4567,45 @@ virDomainDiskDefValidate(const virDomainDiskDef *disk) return 0; }
+static bool +virDomainDefHasUSB(const virDomainDef *def) +{ + size_t i; + + for (i = 0; i < def->ncontrollers; i++) { + if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_USB && + def->controllers[i]->model != VIR_DOMAIN_CONTROLLER_MODEL_USB_NONE) + return true; + } + + return false; +} + +static int +virDomainRedirdevDefValidate(const virDomainDef *def, + const virDomainRedirdevDef *redirdev) +{ + if (redirdev->bus == VIR_DOMAIN_REDIRDEV_BUS_USB && + !virDomainDefHasUSB(def)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Can't add redirected USB device: " + "USB is disabled for this domain"));
Sigh - Can't... I'm probably just as guilty though... We ought to have a large bite size task to remove all contractions such as this... and maybe another to standardize whether we start w/ capital letter or lowercase.
+ return -1; + } + + return 0; +} +
static int virDomainDeviceDefValidateInternal(const virDomainDeviceDef *dev, - const virDomainDef *def ATTRIBUTE_UNUSED) + const virDomainDef *def) { switch ((virDomainDeviceType) dev->type) { case VIR_DOMAIN_DEVICE_DISK: return virDomainDiskDefValidate(dev->data.disk); + case VIR_DOMAIN_DEVICE_REDIRDEV: + return virDomainRedirdevDefValidate(def, dev->data.redirdev);
NIT: my eyes, my eyes... Nice to have extra lines between sometimes!
case VIR_DOMAIN_DEVICE_LEASE: case VIR_DOMAIN_DEVICE_FS: case VIR_DOMAIN_DEVICE_NET: @@ -4586,7 +4617,6 @@ virDomainDeviceDefValidateInternal(const virDomainDeviceDef *dev, case VIR_DOMAIN_DEVICE_CONTROLLER: case VIR_DOMAIN_DEVICE_GRAPHICS: case VIR_DOMAIN_DEVICE_HUB: - case VIR_DOMAIN_DEVICE_REDIRDEV: case VIR_DOMAIN_DEVICE_SMARTCARD: case VIR_DOMAIN_DEVICE_CHR: case VIR_DOMAIN_DEVICE_MEMBALLOON: @@ -17060,14 +17090,6 @@ virDomainDefParseXML(xmlDocPtr xml, if (!redirdev) goto error;
- if (redirdev->bus == VIR_DOMAIN_REDIRDEV_BUS_USB && usb_none) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("Can't add redirected USB device: " - "USB is disabled for this domain")); - virDomainRedirdevDefFree(redirdev); - goto error; - } - def->redirdevs[def->nredirdevs++] = redirdev; } VIR_FREE(nodes); @@ -23578,20 +23600,6 @@ virDomainObjFormat(virDomainXMLOptionPtr xmlopt, }
static bool -virDomainDefHasUSB(virDomainDefPtr def) -{ - size_t i; - - for (i = 0; i < def->ncontrollers; i++) { - if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_USB && - def->controllers[i]->model != VIR_DOMAIN_CONTROLLER_MODEL_USB_NONE) - return true; - } - - return false; -} - -static bool virDomainDeviceIsUSB(virDomainDeviceDefPtr dev) { int t = dev->type;

On Fri, Jun 10, 2016 at 05:32:55PM +0200, Michal Privoznik wrote:
There's currently just one limitation: redirdevs that want to go on USB bus require a USB controller, surprisingly. At the same time, since I'm using virDomainDefHasUSB() in this new validator function, it has to be moved a few lines up and also its header needed to be changed a bit: it is now taking a const pointer to domain def since it's not changing anything in there.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/conf/domain_conf.c | 56 ++++++++++++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 24 deletions(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 85f6e31..c75279d 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -4567,14 +4567,45 @@ virDomainDiskDefValidate(const virDomainDiskDef *disk) return 0; }
+static bool +virDomainDefHasUSB(const virDomainDef *def) +{ + size_t i; + + for (i = 0; i < def->ncontrollers; i++) { + if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_USB && + def->controllers[i]->model != VIR_DOMAIN_CONTROLLER_MODEL_USB_NONE) + return true; + }
This function returns false either if there is a model='none' USB controller or if there is none at all,
+ + return false; +} +
@@ -17060,14 +17090,6 @@ virDomainDefParseXML(xmlDocPtr xml, if (!redirdev) goto error;
- if (redirdev->bus == VIR_DOMAIN_REDIRDEV_BUS_USB && usb_none) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("Can't add redirected USB device: " - "USB is disabled for this domain")); - virDomainRedirdevDefFree(redirdev);
but this error was only reported for model="none'. This broke virt-manager's test: https://ci.centos.org/job/virt-manager-test/systems=libvirt-fedora-rawhide/1... Jan

On 20.06.2016 10:32, Ján Tomko wrote:
On Fri, Jun 10, 2016 at 05:32:55PM +0200, Michal Privoznik wrote:
There's currently just one limitation: redirdevs that want to go on USB bus require a USB controller, surprisingly. At the same time, since I'm using virDomainDefHasUSB() in this new validator function, it has to be moved a few lines up and also its header needed to be changed a bit: it is now taking a const pointer to domain def since it's not changing anything in there.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/conf/domain_conf.c | 56 ++++++++++++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 24 deletions(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 85f6e31..c75279d 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -4567,14 +4567,45 @@ virDomainDiskDefValidate(const virDomainDiskDef *disk) return 0; }
+static bool +virDomainDefHasUSB(const virDomainDef *def) +{ + size_t i; + + for (i = 0; i < def->ncontrollers; i++) { + if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_USB && + def->controllers[i]->model != VIR_DOMAIN_CONTROLLER_MODEL_USB_NONE) + return true; + }
This function returns false either if there is a model='none' USB controller or if there is none at all,
But there's an implicit controlled being added after domain XML is parsed (if none was provided in the XML), unless this behaviour has been suppressed by model='none'.
+ + return false; +} +
@@ -17060,14 +17090,6 @@ virDomainDefParseXML(xmlDocPtr xml, if (!redirdev) goto error;
- if (redirdev->bus == VIR_DOMAIN_REDIRDEV_BUS_USB && usb_none) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("Can't add redirected USB device: " - "USB is disabled for this domain")); - virDomainRedirdevDefFree(redirdev);
but this error was only reported for model="none'.
This broke virt-manager's test: https://ci.centos.org/job/virt-manager-test/systems=libvirt-fedora-rawhide/1...
I don't see the problem. Virt-manager relied on a bug and I've fixed it. I mean, what is happening here is: virt-manager tries to open a test connection whilst providing its own test driver state XML. This big XML has domain XMLs too. These are missing usb controllers and since we are not adding implicit ones in post parse callback (there are none defined for the test driver), domains really are missing USB controllers and thus USB bus. It's just our buggy behaviour that caused virt-manager to work by chance. Michal

Basically, there are just two functions introduced here: virDomainRedirdevDefFind which looks up given redirdev in domain definition, and virDomainRedirdevDefRemove which removes the device at given index in the array of devices. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/conf/domain_conf.c | 42 ++++++++++++++++++++++++++++++++++++++++++ src/conf/domain_conf.h | 4 ++++ src/libvirt_private.syms | 2 ++ 3 files changed, 48 insertions(+) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index c75279d..e0d10e9 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -14603,6 +14603,48 @@ virDomainMemoryRemove(virDomainDefPtr def, } +ssize_t +virDomainRedirdevDefFind(virDomainDefPtr def, + virDomainRedirdevDefPtr redirdev) +{ + size_t i; + + for (i = 0; i < def->nredirdevs; i++) { + virDomainRedirdevDefPtr tmp = def->redirdevs[i]; + + if (redirdev->bus != tmp->bus) + continue; + + if (!virDomainChrSourceDefIsEqual(&redirdev->source.chr, + &tmp->source.chr)) + continue; + + if (redirdev->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE && + !virDomainDeviceInfoAddressIsEqual(&redirdev->info, &tmp->info)) + continue; + + if (redirdev->info.alias && + STRNEQ_NULLABLE(redirdev->info.alias, tmp->info.alias)) + continue; + + return i; + } + + return -1; +} + + +virDomainRedirdevDefPtr +virDomainRedirdevDefRemove(virDomainDefPtr def, size_t idx) +{ + virDomainRedirdevDefPtr ret = def->redirdevs[idx]; + + VIR_DELETE_ELEMENT(def->redirdevs, idx, def->nredirdevs); + + return ret; +} + + char * virDomainDefGetDefaultEmulator(virDomainDefPtr def, virCapsPtr caps) diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 3792562..c1b002a 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -2827,6 +2827,10 @@ virDomainChrRemove(virDomainDefPtr vmdef, ssize_t virDomainRNGFind(virDomainDefPtr def, virDomainRNGDefPtr rng); virDomainRNGDefPtr virDomainRNGRemove(virDomainDefPtr def, size_t idx); +ssize_t virDomainRedirdevDefFind(virDomainDefPtr def, + virDomainRedirdevDefPtr redirdev); +virDomainRedirdevDefPtr virDomainRedirdevDefRemove(virDomainDefPtr def, size_t idx); + int virDomainSaveXML(const char *configDir, virDomainDefPtr def, const char *xml); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 85b9cd1..4625886 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -424,7 +424,9 @@ virDomainPMSuspendedReasonTypeFromString; virDomainPMSuspendedReasonTypeToString; virDomainRedirdevBusTypeFromString; virDomainRedirdevBusTypeToString; +virDomainRedirdevDefFind; virDomainRedirdevDefFree; +virDomainRedirdevDefRemove; virDomainRNGBackendTypeToString; virDomainRNGDefFree; virDomainRNGFind; -- 2.8.4

On 06/10/2016 11:32 AM, Michal Privoznik wrote:
Basically, there are just two functions introduced here: virDomainRedirdevDefFind which looks up given redirdev in domain definition, and virDomainRedirdevDefRemove which removes the device at given index in the array of devices.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/conf/domain_conf.c | 42 ++++++++++++++++++++++++++++++++++++++++++ src/conf/domain_conf.h | 4 ++++ src/libvirt_private.syms | 2 ++ 3 files changed, 48 insertions(+)
ACK - one note below. John
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index c75279d..e0d10e9 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -14603,6 +14603,48 @@ virDomainMemoryRemove(virDomainDefPtr def, }
+ssize_t +virDomainRedirdevDefFind(virDomainDefPtr def, + virDomainRedirdevDefPtr redirdev) +{ + size_t i; + + for (i = 0; i < def->nredirdevs; i++) { + virDomainRedirdevDefPtr tmp = def->redirdevs[i]; + + if (redirdev->bus != tmp->bus) + continue; + + if (!virDomainChrSourceDefIsEqual(&redirdev->source.chr, + &tmp->source.chr)) + continue; +
I would think if type == NONE, then neither AddressIsEqual or Alias will matter. This way works, but after seeing Laine's recent patch about && I had to think a bit!
+ if (redirdev->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE && + !virDomainDeviceInfoAddressIsEqual(&redirdev->info, &tmp->info)) + continue; + + if (redirdev->info.alias && + STRNEQ_NULLABLE(redirdev->info.alias, tmp->info.alias)) + continue; + + return i; + } + + return -1; +} + + +virDomainRedirdevDefPtr +virDomainRedirdevDefRemove(virDomainDefPtr def, size_t idx) +{ + virDomainRedirdevDefPtr ret = def->redirdevs[idx]; + + VIR_DELETE_ELEMENT(def->redirdevs, idx, def->nredirdevs); + + return ret; +} + + char * virDomainDefGetDefaultEmulator(virDomainDefPtr def, virCapsPtr caps) diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 3792562..c1b002a 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -2827,6 +2827,10 @@ virDomainChrRemove(virDomainDefPtr vmdef, ssize_t virDomainRNGFind(virDomainDefPtr def, virDomainRNGDefPtr rng); virDomainRNGDefPtr virDomainRNGRemove(virDomainDefPtr def, size_t idx);
+ssize_t virDomainRedirdevDefFind(virDomainDefPtr def, + virDomainRedirdevDefPtr redirdev); +virDomainRedirdevDefPtr virDomainRedirdevDefRemove(virDomainDefPtr def, size_t idx); + int virDomainSaveXML(const char *configDir, virDomainDefPtr def, const char *xml); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 85b9cd1..4625886 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -424,7 +424,9 @@ virDomainPMSuspendedReasonTypeFromString; virDomainPMSuspendedReasonTypeToString; virDomainRedirdevBusTypeFromString; virDomainRedirdevBusTypeToString; +virDomainRedirdevDefFind; virDomainRedirdevDefFree; +virDomainRedirdevDefRemove; virDomainRNGBackendTypeToString; virDomainRNGDefFree; virDomainRNGFind;

This is going to be important later when we received DEVICE_DELETED event on the qemu monitor. If we do, virDomainDefFindDevice() is called to find the device for given device alias in the virDomainDef tree. When we enable removal for redirdevs we need to include them in the lookup process too. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/conf/domain_conf.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index e0d10e9..11e6ed6 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -3503,6 +3503,13 @@ virDomainDeviceInfoIterateInternal(virDomainDefPtr def, return -1; } + device.type = VIR_DOMAIN_DEVICE_REDIRDEV; + for (i = 0; i < def->nredirdevs; i++) { + device.data.redirdev = def->redirdevs[i]; + if (cb(def, &device, &def->redirdevs[i]->info, opaque) < 0) + return -1; + } + /* Coverity is not very happy with this - all dead_error_condition */ #if !STATIC_ANALYSIS /* This switch statement is here to trigger compiler warning when adding -- 2.8.4

On 06/10/2016 11:32 AM, Michal Privoznik wrote:
This is going to be important later when we received DEVICE_DELETED event on the qemu monitor. If we do, virDomainDefFindDevice() is called to find the device for given device alias in the virDomainDef tree. When we enable removal for redirdevs we need to include them in the lookup process too.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/conf/domain_conf.c | 7 +++++++ 1 file changed, 7 insertions(+)
ACK John

This is really simple, we just need to append the device into the domain def and that's it. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_driver.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index efb3f85..33763c7 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -7794,6 +7794,7 @@ qemuDomainAttachDeviceConfig(virDomainDefPtr vmdef, virDomainLeaseDefPtr lease; virDomainControllerDefPtr controller; virDomainFSDefPtr fs; + virDomainRedirdevDefPtr redirdev; switch ((virDomainDeviceType) dev->type) { case VIR_DOMAIN_DEVICE_DISK: @@ -7911,6 +7912,14 @@ qemuDomainAttachDeviceConfig(virDomainDefPtr vmdef, dev->data.memory = NULL; break; + case VIR_DOMAIN_DEVICE_REDIRDEV: + redirdev = dev->data.redirdev; + + if (VIR_APPEND_ELEMENT(vmdef->redirdevs, vmdef->nredirdevs, redirdev) < 0) + return -1; + dev->data.redirdev = NULL; + break; + case VIR_DOMAIN_DEVICE_INPUT: case VIR_DOMAIN_DEVICE_SOUND: case VIR_DOMAIN_DEVICE_VIDEO: @@ -7921,7 +7930,6 @@ qemuDomainAttachDeviceConfig(virDomainDefPtr vmdef, 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: -- 2.8.4

On 06/10/2016 11:32 AM, Michal Privoznik wrote:
This is really simple, we just need to append the device into the domain def and that's it.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_driver.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-)
ACK John

This is fairly simple. We lookup the device in the array of devices and remove it. No magic. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_driver.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 33763c7..254da59 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -8062,6 +8062,18 @@ qemuDomainDetachDeviceConfig(virDomainDefPtr vmdef, virDomainMemoryDefFree(virDomainMemoryRemove(vmdef, idx)); break; + case VIR_DOMAIN_DEVICE_REDIRDEV: + if ((idx = virDomainRedirdevDefFind(vmdef, + dev->data.redirdev)) < 0) { + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("no matching redirdev was not found")); + return -1; + } + + virDomainRedirdevDefFree(virDomainRedirdevDefRemove(vmdef, idx)); + break; + + case VIR_DOMAIN_DEVICE_INPUT: case VIR_DOMAIN_DEVICE_SOUND: case VIR_DOMAIN_DEVICE_VIDEO: @@ -8072,7 +8084,6 @@ qemuDomainDetachDeviceConfig(virDomainDefPtr vmdef, 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: -- 2.8.4

On 06/10/2016 11:32 AM, Michal Privoznik wrote:
This is fairly simple. We lookup the device in the array of devices and remove it. No magic.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_driver.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-)
ACK - although attach and detach could be "together" too... John

For some reason, as soon as redirdev is detached, qemu removes the chardev too. Well, it's easier for us then, so hard feelings here. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_driver.c | 4 ++- src/qemu/qemu_hotplug.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++++- src/qemu/qemu_hotplug.h | 3 ++ 3 files changed, 81 insertions(+), 2 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 254da59..65e96be 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -7639,6 +7639,9 @@ qemuDomainDetachDeviceLive(virDomainObjPtr vm, 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: @@ -7651,7 +7654,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 58156c6..a7a3675 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -3230,6 +3230,34 @@ qemuDomainRemoveRNGDevice(virQEMUDriverPtr driver, return ret; } +static int +qemuDomainRemoveRedirdevDevice(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainRedirdevDefPtr redirdev) +{ + virObjectEventPtr event; + ssize_t idx; + + VIR_DEBUG("Removing redirdev device %s from domain %p %s", + redirdev->info.alias, vm, vm->def->name); + + if ((idx = virDomainRedirdevDefFind(vm->def, redirdev)) < 0) + return 0; + + /* QEMU removed the chardev for us already. */ + + virDomainAuditRedirdev(vm, redirdev, "detach", true); + + event = virDomainEventDeviceRemovedNewFromObj(vm, redirdev->info.alias); + qemuDomainEventQueue(driver, event); + + virDomainRedirdevDefRemove(vm->def, idx); + qemuDomainReleaseDeviceAddress(vm, &redirdev->info, NULL); + virDomainRedirdevDefFree(redirdev); + return 0; +} + + int qemuDomainRemoveDevice(virQEMUDriverPtr driver, @@ -3261,6 +3289,9 @@ qemuDomainRemoveDevice(virQEMUDriverPtr driver, case VIR_DOMAIN_DEVICE_MEMORY: 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: @@ -3271,7 +3302,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: @@ -4172,3 +4202,47 @@ qemuDomainDetachMemoryDevice(virQEMUDriverPtr driver, qemuDomainResetDeviceRemoval(vm); return ret; } + +int +qemuDomainDetachRedirdevDevice(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainRedirdevDefPtr redirdev) +{ + int ret = -1; + qemuDomainObjPrivatePtr priv = vm->privateData; + virDomainRedirdevDefPtr tmpRedirdev; + ssize_t idx; + + if ((idx = virDomainRedirdevDefFind(vm->def, redirdev)) < 0) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("no matching redirdev was not found")); + return -1; + } + + tmpRedirdev = vm->def->redirdevs[idx]; + + if (!tmpRedirdev->info.alias) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("alias for the redirdev was not found")); + goto cleanup; + } + + qemuDomainMarkDeviceForRemoval(vm, &tmpRedirdev->info); + + qemuDomainObjEnterMonitor(driver, vm); + if (qemuMonitorDelDevice(priv->mon, tmpRedirdev->info.alias) < 0) { + ignore_value(qemuDomainObjExitMonitor(driver, vm)); + goto cleanup; + } + if (qemuDomainObjExitMonitor(driver, vm) < 0) + goto cleanup; + + if ((ret = qemuDomainWaitForDeviceRemoval(vm)) == 1) { + qemuDomainReleaseDeviceAddress(vm, &tmpRedirdev->info, NULL); + ret = qemuDomainRemoveRedirdevDevice(driver, vm, tmpRedirdev); + } + + cleanup: + qemuDomainResetDeviceRemoval(vm); + return ret; +} diff --git a/src/qemu/qemu_hotplug.h b/src/qemu/qemu_hotplug.h index 165d345..fc79ee5 100644 --- a/src/qemu/qemu_hotplug.h +++ b/src/qemu/qemu_hotplug.h @@ -46,6 +46,9 @@ int qemuDomainAttachNetDevice(virQEMUDriverPtr driver, int qemuDomainAttachRedirdevDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainRedirdevDefPtr hostdev); +int qemuDomainDetachRedirdevDevice(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainRedirdevDefPtr hostdev); int qemuDomainAttachHostDevice(virConnectPtr conn, virQEMUDriverPtr driver, virDomainObjPtr vm, -- 2.8.4

On 06/10/2016 11:33 AM, Michal Privoznik wrote:
For some reason, as soon as redirdev is detached, qemu removes the chardev too. Well, it's easier for us then, so hard feelings here.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_driver.c | 4 ++- src/qemu/qemu_hotplug.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++++- src/qemu/qemu_hotplug.h | 3 ++ 3 files changed, 81 insertions(+), 2 deletions(-)
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 254da59..65e96be 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -7639,6 +7639,9 @@ qemuDomainDetachDeviceLive(virDomainObjPtr vm, 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: @@ -7651,7 +7654,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 58156c6..a7a3675 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -3230,6 +3230,34 @@ qemuDomainRemoveRNGDevice(virQEMUDriverPtr driver, return ret; }
+static int +qemuDomainRemoveRedirdevDevice(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainRedirdevDefPtr redirdev) +{ + virObjectEventPtr event; + ssize_t idx; + + VIR_DEBUG("Removing redirdev device %s from domain %p %s", + redirdev->info.alias, vm, vm->def->name); + + if ((idx = virDomainRedirdevDefFind(vm->def, redirdev)) < 0) + return 0;
This is a bit different than RNG processing... If for some reason this doesn't exist, then the audit doesn't happen nor does the event. It would seem both would be wanted, wouldn't they? Especially since we're claiming success.
+ + /* QEMU removed the chardev for us already. */
Will this always be true in the future? Should we just try (like RNG) and ignore the result.
+ + virDomainAuditRedirdev(vm, redirdev, "detach", true); + + event = virDomainEventDeviceRemovedNewFromObj(vm, redirdev->info.alias); + qemuDomainEventQueue(driver, event); + + virDomainRedirdevDefRemove(vm->def, idx); + qemuDomainReleaseDeviceAddress(vm, &redirdev->info, NULL); + virDomainRedirdevDefFree(redirdev); + return 0; +} + +
int qemuDomainRemoveDevice(virQEMUDriverPtr driver, @@ -3261,6 +3289,9 @@ qemuDomainRemoveDevice(virQEMUDriverPtr driver, case VIR_DOMAIN_DEVICE_MEMORY: 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: @@ -3271,7 +3302,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: @@ -4172,3 +4202,47 @@ qemuDomainDetachMemoryDevice(virQEMUDriverPtr driver, qemuDomainResetDeviceRemoval(vm); return ret; } + +int +qemuDomainDetachRedirdevDevice(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainRedirdevDefPtr redirdev) +{ + int ret = -1; + qemuDomainObjPrivatePtr priv = vm->privateData; + virDomainRedirdevDefPtr tmpRedirdev; + ssize_t idx; + + if ((idx = virDomainRedirdevDefFind(vm->def, redirdev)) < 0) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("no matching redirdev was not found")); + return -1; + } + + tmpRedirdev = vm->def->redirdevs[idx]; + + if (!tmpRedirdev->info.alias) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("alias for the redirdev was not found")); + goto cleanup; + } + + qemuDomainMarkDeviceForRemoval(vm, &tmpRedirdev->info); + + qemuDomainObjEnterMonitor(driver, vm); + if (qemuMonitorDelDevice(priv->mon, tmpRedirdev->info.alias) < 0) { + ignore_value(qemuDomainObjExitMonitor(driver, vm)); + goto cleanup; + } + if (qemuDomainObjExitMonitor(driver, vm) < 0) + goto cleanup; + + if ((ret = qemuDomainWaitForDeviceRemoval(vm)) == 1) { + qemuDomainReleaseDeviceAddress(vm, &tmpRedirdev->info, NULL);
^^ This is called in RemoveRedirdevDevice, so it'd be duplicitous. I think this is ACK-able with some obvious cleanups and perhaps thoughts about the ordering in qemuDomainRemoveRedirdevDevice John
+ ret = qemuDomainRemoveRedirdevDevice(driver, vm, tmpRedirdev); + } + + cleanup: + qemuDomainResetDeviceRemoval(vm); + return ret; +} diff --git a/src/qemu/qemu_hotplug.h b/src/qemu/qemu_hotplug.h index 165d345..fc79ee5 100644 --- a/src/qemu/qemu_hotplug.h +++ b/src/qemu/qemu_hotplug.h @@ -46,6 +46,9 @@ int qemuDomainAttachNetDevice(virQEMUDriverPtr driver, int qemuDomainAttachRedirdevDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainRedirdevDefPtr hostdev); +int qemuDomainDetachRedirdevDevice(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainRedirdevDefPtr hostdev); int qemuDomainAttachHostDevice(virConnectPtr conn, virQEMUDriverPtr driver, virDomainObjPtr vm,

The problem is this: when working on redirdev detach, I've noticed that even though I've passed device alias in the input device XML, it got transformed into inactive in qemuDomainDetachDeviceLive() while in qemuDomainDetachDeviceConfig() it was the active version of it. Apparently, if you are doing: virsh # detach-device --config --live domain device.xml our code correctly detects that you want to detach the device from both config and live domain definition. However, due to some internal implementation we need to make a copy of the device that user had provided. And to do that, we call virDomainDeviceDefCopy(). This function basically formats the device into XML and then parse it again. But there's a problem, it's formatting the XML with VIR_DOMAIN_DEF_FORMAT_INACTIVE flag which means that no live data are present in the copy. So we have a (possibly live) device definition that we pass to code detaching it from inactive XML, and inactive device definition that we pass to code detaching it from active XML. This makes no sense. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_driver.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 65e96be..8255d7b 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -8523,22 +8523,22 @@ qemuDomainDetachDeviceFlags(virDomainPtr dom, if (!vmdef) goto endjob; - if (virDomainDefCompatibleDevice(vmdef, dev, + if (virDomainDefCompatibleDevice(vmdef, dev_copy, VIR_DOMAIN_DEVICE_ACTION_DETACH) < 0) goto endjob; - if ((ret = qemuDomainDetachDeviceConfig(vmdef, dev, caps, + if ((ret = qemuDomainDetachDeviceConfig(vmdef, dev_copy, caps, parse_flags, driver->xmlopt)) < 0) goto endjob; } if (flags & VIR_DOMAIN_AFFECT_LIVE) { - if (virDomainDefCompatibleDevice(vm->def, dev_copy, + if (virDomainDefCompatibleDevice(vm->def, dev, VIR_DOMAIN_DEVICE_ACTION_DETACH) < 0) goto endjob; - if ((ret = qemuDomainDetachDeviceLive(vm, dev_copy, dom)) < 0) + if ((ret = qemuDomainDetachDeviceLive(vm, dev, dom)) < 0) goto endjob; /* * update domain status forcibly because the domain status may be -- 2.8.4

On 06/10/2016 11:33 AM, Michal Privoznik wrote:
The problem is this: when working on redirdev detach, I've noticed that even though I've passed device alias in the input device XML, it got transformed into inactive in qemuDomainDetachDeviceLive() while in qemuDomainDetachDeviceConfig() it was the active version of it.
Apparently, if you are doing:
virsh # detach-device --config --live domain device.xml
our code correctly detects that you want to detach the device from both config and live domain definition. However, due to some internal implementation we need to make a copy of the device that user had provided. And to do that, we call virDomainDeviceDefCopy(). This function basically formats the
If you take off the (), then I assume that function name fits on the previous line. The blank space just looks odd.
device into XML and then parse it again. But there's a problem,
s/parse/parses
it's formatting the XML with VIR_DOMAIN_DEF_FORMAT_INACTIVE flag which means that no live data are present in the copy. So we have
s/are/is
a (possibly live) device definition that we pass to code detaching it from inactive XML, and inactive device definition that we pass to code detaching it from active XML. This makes no sense.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_driver.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
Hazards of cut-n-paste code? And perhaps adjustments to the Copy function after original implementation? I didn't dig on history. Why is this not a problem for Attach and Update? What about LXC which also uses the Copy function. BTW: There's a comment: /* If we are affecting both CONFIG and LIVE * create a deep copy of device as adding * to CONFIG takes one instance. */ just above the code you changed that has I think an obvious adjustment to "deleting from"... Similarly the update code could use "updating" I also think for each comment (I read code and don't necessarily hunt for the commit message) that summarizes your investigation: "NB: The 'dev' is the actual/live device, while 'dev_copy' will be a somewhat reduced copy generated by using VIR_DOMAIN_DEF_PARSE_INACTIVE which means conditionally parsed/formatted fields will not be copied." (I assume that means stuff we've generated, that isn't part of the XML, but is part of the virDmainDef) I think my summary is - all 3 should use 'dev' for live and 'dev_copy' for config. John
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 65e96be..8255d7b 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -8523,22 +8523,22 @@ qemuDomainDetachDeviceFlags(virDomainPtr dom, if (!vmdef) goto endjob;
- if (virDomainDefCompatibleDevice(vmdef, dev, + if (virDomainDefCompatibleDevice(vmdef, dev_copy, VIR_DOMAIN_DEVICE_ACTION_DETACH) < 0) goto endjob;
- if ((ret = qemuDomainDetachDeviceConfig(vmdef, dev, caps, + if ((ret = qemuDomainDetachDeviceConfig(vmdef, dev_copy, caps, parse_flags, driver->xmlopt)) < 0) goto endjob; }
if (flags & VIR_DOMAIN_AFFECT_LIVE) { - if (virDomainDefCompatibleDevice(vm->def, dev_copy, + if (virDomainDefCompatibleDevice(vm->def, dev, VIR_DOMAIN_DEVICE_ACTION_DETACH) < 0) goto endjob;
- if ((ret = qemuDomainDetachDeviceLive(vm, dev_copy, dom)) < 0) + if ((ret = qemuDomainDetachDeviceLive(vm, dev, dom)) < 0) goto endjob; /* * update domain status forcibly because the domain status may be
participants (3)
-
John Ferlan
-
Ján Tomko
-
Michal Privoznik