[libvirt] [PATCH] qemu: Don't parse device twice in attach/detach
Laine Stump
laine at laine.org
Sat Mar 3 14:27:43 UTC 2012
On 03/01/2012 01:56 PM, Michal Privoznik wrote:
> Some nits are generated during XML parse (e.g. MAC address of
> an interface); However, with current implementation, if we
> are plugging a device both to persistent and live config,
> we parse given XML twice: first time for live, second for config.
> This is wrong then as the second time we are not guaranteed
> to generate same values as we did for the first time.
> To prevent that we need to create a copy of DeviceDefPtr;
> This is done through format/parse process instead of writing
> functions for deep copy as it is easier to maintain:
> adding new field to any virDomain*DefPtr doesn't require change
> of copying function.
> ---
> src/conf/domain_conf.c | 94 ++++++++++++++++++++++++++++++++++++++++++++++
> src/conf/domain_conf.h | 3 +
> src/libvirt_private.syms | 1 +
> src/qemu/qemu_driver.c | 40 +++++++++++--------
> 4 files changed, 121 insertions(+), 17 deletions(-)
>
> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
> index f9654f1..f001319 100644
> --- a/src/conf/domain_conf.c
> +++ b/src/conf/domain_conf.c
> @@ -14118,3 +14118,97 @@ virDomainNetFind(virDomainDefPtr def, const char *device)
>
> return net;
> }
> +
> +#define VIR_DOMAIN_DEVICE_FORMAT(func) \
> + if (func < 0) \
> + goto cleanup; \
> + xmlStr = virBufferContentAndReset(&buf); \
> + ret = virDomainDeviceDefParse(caps, def, xmlStr, flags)
> +
> +virDomainDeviceDefPtr
> +virDomainDeviceDefCopy(virCapsPtr caps,
> + const virDomainDefPtr def,
> + virDomainDeviceDefPtr src)
> +{
> + virDomainDeviceDefPtr ret = NULL;
> + virBuffer buf = VIR_BUFFER_INITIALIZER;
> + int flags = VIR_DOMAIN_XML_INACTIVE;
I'm concerned that this function has the appearance of being general
purpose, but actually isn't. There are many bits of data in
virDomainNetDef that won't be copied, because they're only
parsed/formatted if extra flags are given in the calls to Parse and
Format (VIR_DOMAIN_XML_INTERNAL_STATUS,
VIR_DOMAIN_XML_INTERNAL_STATUS_ACTUAL_NET, etc).
For the current use case, it should work adequately, since we know that
we just created the object by parsing with a limited flag set, but I can
imagine the day 6 months from now when somebody who doesn't know the
history/limitations of ths new function finds it and mistakenly believes
it's the answer to their current (probably very different) problem (and
then commits code with a bug that doesn't show up until it's in the field)
It's probably too much trouble in the short term to try and make this
function completely general purpose, but I think at least the function
name should reflect the limited nature and/or both the declaration and
definition of the function should be accompanied by a comment noting the
limitations.
Beyond that, this looks like the right way to fix the problem. Note that
libxl_driver.c:libxmlDomainModifyDeviceFlags() has exactly the same
problem, so maybe its fix can be included in this patch.
> + char *xmlStr = NULL;
> +
> + switch(src->type) {
> + case VIR_DOMAIN_DEVICE_DISK:
> + VIR_DOMAIN_DEVICE_FORMAT(virDomainDiskDefFormat(&buf,
> + src->data.disk,
> + flags));
> + break;
> + case VIR_DOMAIN_DEVICE_LEASE:
> + VIR_DOMAIN_DEVICE_FORMAT(virDomainLeaseDefFormat(&buf,
> + src->data.lease));
> + break;
> + case VIR_DOMAIN_DEVICE_FS:
> + VIR_DOMAIN_DEVICE_FORMAT(virDomainFSDefFormat(&buf,
> + src->data.fs,
> + flags));
> + break;
> + case VIR_DOMAIN_DEVICE_NET:
> + VIR_DOMAIN_DEVICE_FORMAT(virDomainNetDefFormat(&buf,
> + src->data.net,
> + flags));
> + break;
> + case VIR_DOMAIN_DEVICE_INPUT:
> + VIR_DOMAIN_DEVICE_FORMAT(virDomainInputDefFormat(&buf,
> + src->data.input,
> + flags));
> + break;
> + case VIR_DOMAIN_DEVICE_SOUND:
> + VIR_DOMAIN_DEVICE_FORMAT(virDomainSoundDefFormat(&buf,
> + src->data.sound,
> + flags));
> + break;
> + case VIR_DOMAIN_DEVICE_VIDEO:
> + VIR_DOMAIN_DEVICE_FORMAT(virDomainVideoDefFormat(&buf,
> + src->data.video,
> + flags));
> + break;
> + case VIR_DOMAIN_DEVICE_HOSTDEV:
> + VIR_DOMAIN_DEVICE_FORMAT(virDomainHostdevDefFormat(&buf,
> + src->data.hostdev,
> + flags));
> + break;
> + case VIR_DOMAIN_DEVICE_WATCHDOG:
> + VIR_DOMAIN_DEVICE_FORMAT(virDomainWatchdogDefFormat(&buf,
> + src->data.watchdog,
> + flags));
> + break;
> + case VIR_DOMAIN_DEVICE_CONTROLLER:
> + VIR_DOMAIN_DEVICE_FORMAT(virDomainControllerDefFormat(&buf,
> + src->data.controller,
> + flags));
> + break;
> + case VIR_DOMAIN_DEVICE_GRAPHICS:
> + VIR_DOMAIN_DEVICE_FORMAT(virDomainGraphicsDefFormat(&buf,
> + src->data.graphics,
> + flags));
> + break;
> + case VIR_DOMAIN_DEVICE_HUB:
> + VIR_DOMAIN_DEVICE_FORMAT(virDomainHubDefFormat(&buf,
> + src->data.hub,
> + flags));
> + break;
> + case VIR_DOMAIN_DEVICE_REDIRDEV:
> + VIR_DOMAIN_DEVICE_FORMAT(virDomainRedirdevDefFormat(&buf,
> + src->data.redirdev,
> + flags));
> + break;
> + default:
> + virDomainReportError(VIR_ERR_INTERNAL_ERROR,
> + _("Copying definition of '%d' type "
> + "is not implemented yet."),
> + src->type);
> + break;
> + }
> +
> +cleanup:
> + VIR_FREE(xmlStr);
> + return ret;
> +}
> diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
> index 596be4d..7e4a464 100644
> --- a/src/conf/domain_conf.h
> +++ b/src/conf/domain_conf.h
> @@ -1750,6 +1750,9 @@ void virDomainHostdevDefFree(virDomainHostdevDefPtr def);
> void virDomainHubDefFree(virDomainHubDefPtr def);
> void virDomainRedirdevDefFree(virDomainRedirdevDefPtr def);
> void virDomainDeviceDefFree(virDomainDeviceDefPtr def);
> +virDomainDeviceDefPtr virDomainDeviceDefCopy(virCapsPtr caps,
> + const virDomainDefPtr def,
> + virDomainDeviceDefPtr src);
> int virDomainDeviceAddressIsValid(virDomainDeviceInfoPtr info,
> int type);
> int virDomainDevicePCIAddressIsValid(virDomainDevicePCIAddressPtr addr);
> diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
> index a104e70..53f27e7 100644
> --- a/src/libvirt_private.syms
> +++ b/src/libvirt_private.syms
> @@ -286,6 +286,7 @@ virDomainDeviceAddressIsValid;
> virDomainDeviceAddressPciMultiTypeFromString;
> virDomainDeviceAddressPciMultiTypeToString;
> virDomainDeviceAddressTypeToString;
> +virDomainDeviceDefCopy;
> virDomainDeviceDefFree;
> virDomainDeviceDefParse;
> virDomainDeviceInfoIterate;
> diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
> index c6bdd29..3a52ded 100644
> --- a/src/qemu/qemu_driver.c
> +++ b/src/qemu/qemu_driver.c
> @@ -5561,8 +5561,9 @@ qemuDomainModifyDeviceFlags(virDomainPtr dom, const char *xml,
> struct qemud_driver *driver = dom->conn->privateData;
> virDomainObjPtr vm = NULL;
> virDomainDefPtr vmdef = NULL;
> - virDomainDeviceDefPtr dev = NULL;
> + virDomainDeviceDefPtr dev = NULL, dev_copy = NULL;
> bool force = (flags & VIR_DOMAIN_DEVICE_MODIFY_FORCE) != 0;
> + bool free_dev_copy = false;
> int ret = -1;
> unsigned int affect;
>
> @@ -5608,12 +5609,24 @@ qemuDomainModifyDeviceFlags(virDomainPtr dom, const char *xml,
> goto endjob;
> }
>
> - if (flags & VIR_DOMAIN_AFFECT_CONFIG) {
> - dev = virDomainDeviceDefParse(driver->caps, vm->def, xml,
> - VIR_DOMAIN_XML_INACTIVE);
> - if (dev == NULL)
> + dev = dev_copy = virDomainDeviceDefParse(driver->caps, vm->def, xml,
> + VIR_DOMAIN_XML_INACTIVE);
> + if (dev == NULL)
> + goto endjob;
> +
> + if (flags & VIR_DOMAIN_AFFECT_CONFIG &&
> + flags & VIR_DOMAIN_AFFECT_LIVE) {
> + /* If we are affecting both CONFIG and LIVE
> + * create a deep copy of device as adding
> + * to CONFIG takes one instance.
> + */
> + dev_copy = virDomainDeviceDefCopy(driver->caps, vm->def, dev);
> + if (!dev_copy)
> goto endjob;
> + free_dev_copy = true;
> + }
>
> + if (flags & VIR_DOMAIN_AFFECT_CONFIG) {
> /* Make a copy for updated domain. */
> vmdef = virDomainObjCopyPersistentDef(driver->caps, vm);
> if (!vmdef)
> @@ -5639,24 +5652,15 @@ qemuDomainModifyDeviceFlags(virDomainPtr dom, const char *xml,
> }
>
> if (flags & VIR_DOMAIN_AFFECT_LIVE) {
> - /* If dev exists it was created to modify the domain config. Free it. */
> - virDomainDeviceDefFree(dev);
> - dev = virDomainDeviceDefParse(driver->caps, vm->def, xml,
> - VIR_DOMAIN_XML_INACTIVE);
> - if (dev == NULL) {
> - ret = -1;
> - goto endjob;
> - }
> -
> switch (action) {
> case QEMU_DEVICE_ATTACH:
> - ret = qemuDomainAttachDeviceLive(vm, dev, dom);
> + ret = qemuDomainAttachDeviceLive(vm, dev_copy, dom);
> break;
> case QEMU_DEVICE_DETACH:
> - ret = qemuDomainDetachDeviceLive(vm, dev, dom);
> + ret = qemuDomainDetachDeviceLive(vm, dev_copy, dom);
> break;
> case QEMU_DEVICE_UPDATE:
> - ret = qemuDomainUpdateDeviceLive(vm, dev, dom, force);
> + ret = qemuDomainUpdateDeviceLive(vm, dev_copy, dom, force);
> break;
> default:
> qemuReportError(VIR_ERR_INTERNAL_ERROR,
> @@ -5694,6 +5698,8 @@ endjob:
> cleanup:
> virDomainDefFree(vmdef);
> virDomainDeviceDefFree(dev);
> + if (free_dev_copy)
> + virDomainDeviceDefFree(dev_copy);
> if (vm)
> virDomainObjUnlock(vm);
> qemuDriverUnlock(driver);
More information about the libvir-list
mailing list