[libvirt] [PATCH 0/8] domain: Support <address type='pci'/> allocation

This patch series allows the user to specify bare device <address type='pci'/> to explicitly request PCI address allocation. This has several uses, but the motivating one is providing an easy way to request PCI address allocation where it normally isn't the default address type, like for aarch64 VMs. Cole Robinson (8): domain: Add virDomainDefAddImplicitDevices domain: conf: Export virDomainDefPostParseDevices qemu: Assign device addresses in PostParse util: xml: add virXMLPropertyCount tests: Add failure flags to CompareDomainXML2XML domain: Make <address type='pci'/> request address allocation qemu: Wire up address type=pci auto_allocate tests: qemu: test <address type='pci'/> with aarch64 docs/schemas/domaincommon.rng | 5 +- src/conf/domain_conf.c | 81 +++++++++++++++++----- src/conf/domain_conf.h | 8 ++- src/libvirt_private.syms | 3 +- src/qemu/qemu_domain.c | 13 +++- src/qemu/qemu_domain_address.c | 47 +++++++++++++ src/qemu/qemu_driver.c | 6 +- src/util/virxml.c | 17 +++++ src/util/virxml.h | 1 + src/vmx/vmx.c | 2 +- src/vz/vz_sdk.c | 2 +- tests/bhyvexml2xmltest.c | 2 +- .../generic-pci-autofill-addr.xml | 27 ++++++++ tests/genericxml2xmltest.c | 17 +++-- tests/lxcxml2xmltest.c | 2 +- .../qemuargv2xmldata/qemuargv2xml-pseries-disk.xml | 4 +- ...l2argv-aarch64-virtio-pci-manual-addresses.args | 4 +- ...ml2argv-aarch64-virtio-pci-manual-addresses.xml | 5 ++ .../qemuxml2argv-pci-autofill-addr.args | 24 +++++++ .../qemuxml2argv-pci-autofill-addr.xml | 44 ++++++++++++ tests/qemuxml2argvtest.c | 21 +++--- ...2xmlout-aarch64-virtio-pci-manual-addresses.xml | 5 ++ .../qemuxml2xmlout-pci-autofill-addr.xml | 46 ++++++++++++ tests/qemuxml2xmltest.c | 18 +++-- tests/testutils.c | 10 ++- tests/testutils.h | 4 ++ 26 files changed, 359 insertions(+), 59 deletions(-) create mode 100644 tests/genericxml2xmlindata/generic-pci-autofill-addr.xml create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-pci-autofill-addr.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-pci-autofill-addr.xml create mode 100644 tests/qemuxml2xmloutdata/qemuxml2xmlout-pci-autofill-addr.xml -- 2.5.0

It's just a combination of AddImplicitControllers, and AddConsoleCompat. Every caller that wants ImplicitControllers also wants the ConsoleCompat AFAICT, so lump them together. We also need it for future patches. --- src/conf/domain_conf.c | 19 ++++++++++++++----- src/conf/domain_conf.h | 2 +- src/libvirt_private.syms | 2 +- src/qemu/qemu_driver.c | 6 +++--- src/vmx/vmx.c | 2 +- src/vz/vz_sdk.c | 2 +- 6 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 39cedbd..40b1929 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -3842,9 +3842,6 @@ virDomainDefPostParseInternal(virDomainDefPtr def, if (virDomainDefPostParseMemory(def, parseFlags) < 0) return -1; - if (virDomainDefAddConsoleCompat(def) < 0) - return -1; - if (virDomainDefRejectDuplicateControllers(def) < 0) return -1; @@ -3854,7 +3851,7 @@ virDomainDefPostParseInternal(virDomainDefPtr def, if (virDomainDefPostParseTimer(def) < 0) return -1; - if (virDomainDefAddImplicitControllers(def) < 0) + if (virDomainDefAddImplicitDevices(def) < 0) return -1; /* clean up possibly duplicated metadata entries */ @@ -18289,7 +18286,7 @@ virDomainDefMaybeAddSmartcardController(virDomainDefPtr def) * in the XML. This is for compat with existing apps which will * not know/care about <controller> info in the XML */ -int +static int virDomainDefAddImplicitControllers(virDomainDefPtr def) { if (virDomainDefAddDiskControllersForType(def, @@ -18324,6 +18321,18 @@ virDomainDefAddImplicitControllers(virDomainDefPtr def) return 0; } +int +virDomainDefAddImplicitDevices(virDomainDefPtr def) +{ + if (virDomainDefAddConsoleCompat(def) < 0) + return -1; + + if (virDomainDefAddImplicitControllers(def) < 0) + return -1; + + return 0; +} + virDomainIOThreadIDDefPtr virDomainIOThreadIDFind(virDomainDefPtr def, unsigned int iothread_id) diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 06305f0..6f044d2 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -2697,7 +2697,7 @@ virDomainObjPtr virDomainObjParseFile(const char *filename, bool virDomainDefCheckABIStability(virDomainDefPtr src, virDomainDefPtr dst); -int virDomainDefAddImplicitControllers(virDomainDefPtr def); +int virDomainDefAddImplicitDevices(virDomainDefPtr def); virDomainIOThreadIDDefPtr virDomainIOThreadIDFind(virDomainDefPtr def, unsigned int iothread_id); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 3a1b9e1..5399117 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -200,7 +200,7 @@ virDomainControllerRemove; virDomainControllerTypeToString; virDomainCpuPlacementModeTypeFromString; virDomainCpuPlacementModeTypeToString; -virDomainDefAddImplicitControllers; +virDomainDefAddImplicitDevices; virDomainDefAddUSBController; virDomainDefCheckABIStability; virDomainDefCheckDuplicateDiskInfo; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 102fade..9c60518 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -7924,7 +7924,7 @@ qemuDomainAttachDeviceConfig(virQEMUCapsPtr qemuCaps, /* vmdef has the pointer. Generic codes for vmdef will do all jobs */ dev->data.disk = NULL; if (disk->bus != VIR_DOMAIN_DISK_BUS_VIRTIO) - if (virDomainDefAddImplicitControllers(vmdef) < 0) + if (virDomainDefAddImplicitDevices(vmdef) < 0) return -1; if (qemuDomainAssignAddresses(vmdef, qemuCaps, NULL) < 0) return -1; @@ -7949,7 +7949,7 @@ qemuDomainAttachDeviceConfig(virQEMUCapsPtr qemuCaps, if (virDomainHostdevInsert(vmdef, hostdev)) return -1; dev->data.hostdev = NULL; - if (virDomainDefAddImplicitControllers(vmdef) < 0) + if (virDomainDefAddImplicitDevices(vmdef) < 0) return -1; if (qemuDomainAssignAddresses(vmdef, qemuCaps, NULL) < 0) return -1; @@ -7991,7 +7991,7 @@ qemuDomainAttachDeviceConfig(virQEMUCapsPtr qemuCaps, if (qemuDomainChrInsert(vmdef, dev->data.chr) < 0) return -1; dev->data.chr = NULL; - if (virDomainDefAddImplicitControllers(vmdef) < 0) + if (virDomainDefAddImplicitDevices(vmdef) < 0) return -1; if (qemuDomainAssignAddresses(vmdef, qemuCaps, NULL) < 0) return -1; diff --git a/src/vmx/vmx.c b/src/vmx/vmx.c index 4fd0a1d..893c77a 100644 --- a/src/vmx/vmx.c +++ b/src/vmx/vmx.c @@ -1691,7 +1691,7 @@ virVMXParseConfig(virVMXContext *ctx, } /* def:controllers */ - if (virDomainDefAddImplicitControllers(def) < 0) { + if (virDomainDefAddImplicitDevices(def) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not add controllers")); goto cleanup; } diff --git a/src/vz/vz_sdk.c b/src/vz/vz_sdk.c index de73c31..8bf2fe9 100644 --- a/src/vz/vz_sdk.c +++ b/src/vz/vz_sdk.c @@ -1356,7 +1356,7 @@ prlsdkLoadDomain(vzConnPtr privconn, virDomainObjPtr dom) if (prlsdkGetDomainState(sdkdom, &domainState) < 0) goto error; - if (virDomainDefAddImplicitControllers(def) < 0) + if (virDomainDefAddImplicitDevices(def) < 0) goto error; if (def->ngraphics > 0) { -- 2.5.0

On 03/08/2016 11:36 AM, Cole Robinson wrote:
It's just a combination of AddImplicitControllers, and AddConsoleCompat. Every caller that wants ImplicitControllers also wants the ConsoleCompat AFAICT, so lump them together. We also need it for future patches. --- src/conf/domain_conf.c | 19 ++++++++++++++----- src/conf/domain_conf.h | 2 +- src/libvirt_private.syms | 2 +- src/qemu/qemu_driver.c | 6 +++--- src/vmx/vmx.c | 2 +- src/vz/vz_sdk.c | 2 +- 6 files changed, 21 insertions(+), 12 deletions(-)
This just makes sense in general, regardless of what you need it for later :-)

On 03/08/2016 11:36 AM, Cole Robinson wrote:
It's just a combination of AddImplicitControllers, and AddConsoleCompat. Every caller that wants ImplicitControllers also wants the ConsoleCompat AFAICT, so lump them together. We also need it for future patches. --- src/conf/domain_conf.c | 19 ++++++++++++++----- src/conf/domain_conf.h | 2 +- src/libvirt_private.syms | 2 +- src/qemu/qemu_driver.c | 6 +++--- src/vmx/vmx.c | 2 +- src/vz/vz_sdk.c | 2 +- 6 files changed, 21 insertions(+), 12 deletions(-)
Not an issue, but a note below... ACK - John
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 39cedbd..40b1929 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -3842,9 +3842,6 @@ virDomainDefPostParseInternal(virDomainDefPtr def, if (virDomainDefPostParseMemory(def, parseFlags) < 0) return -1;
- if (virDomainDefAddConsoleCompat(def) < 0) - return -1; - if (virDomainDefRejectDuplicateControllers(def) < 0) return -1;
@@ -3854,7 +3851,7 @@ virDomainDefPostParseInternal(virDomainDefPtr def, if (virDomainDefPostParseTimer(def) < 0) return -1;
- if (virDomainDefAddImplicitControllers(def) < 0) + if (virDomainDefAddImplicitDevices(def) < 0) return -1;
/* clean up possibly duplicated metadata entries */ @@ -18289,7 +18286,7 @@ virDomainDefMaybeAddSmartcardController(virDomainDefPtr def) * in the XML. This is for compat with existing apps which will * not know/care about <controller> info in the XML */ -int +static int virDomainDefAddImplicitControllers(virDomainDefPtr def) { if (virDomainDefAddDiskControllersForType(def, @@ -18324,6 +18321,18 @@ virDomainDefAddImplicitControllers(virDomainDefPtr def) return 0; }
+int +virDomainDefAddImplicitDevices(virDomainDefPtr def) +{ + if (virDomainDefAddConsoleCompat(def) < 0) + return -1; + + if (virDomainDefAddImplicitControllers(def) < 0) + return -1; + + return 0; +} + virDomainIOThreadIDDefPtr virDomainIOThreadIDFind(virDomainDefPtr def, unsigned int iothread_id) diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 06305f0..6f044d2 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -2697,7 +2697,7 @@ virDomainObjPtr virDomainObjParseFile(const char *filename, bool virDomainDefCheckABIStability(virDomainDefPtr src, virDomainDefPtr dst);
-int virDomainDefAddImplicitControllers(virDomainDefPtr def); +int virDomainDefAddImplicitDevices(virDomainDefPtr def);
virDomainIOThreadIDDefPtr virDomainIOThreadIDFind(virDomainDefPtr def, unsigned int iothread_id); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 3a1b9e1..5399117 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -200,7 +200,7 @@ virDomainControllerRemove; virDomainControllerTypeToString; virDomainCpuPlacementModeTypeFromString; virDomainCpuPlacementModeTypeToString; -virDomainDefAddImplicitControllers; +virDomainDefAddImplicitDevices; virDomainDefAddUSBController; virDomainDefCheckABIStability; virDomainDefCheckDuplicateDiskInfo; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 102fade..9c60518 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -7924,7 +7924,7 @@ qemuDomainAttachDeviceConfig(virQEMUCapsPtr qemuCaps, /* vmdef has the pointer. Generic codes for vmdef will do all jobs */ dev->data.disk = NULL; if (disk->bus != VIR_DOMAIN_DISK_BUS_VIRTIO) - if (virDomainDefAddImplicitControllers(vmdef) < 0) + if (virDomainDefAddImplicitDevices(vmdef) < 0) return -1; if (qemuDomainAssignAddresses(vmdef, qemuCaps, NULL) < 0) return -1; @@ -7949,7 +7949,7 @@ qemuDomainAttachDeviceConfig(virQEMUCapsPtr qemuCaps, if (virDomainHostdevInsert(vmdef, hostdev)) return -1; dev->data.hostdev = NULL; - if (virDomainDefAddImplicitControllers(vmdef) < 0) + if (virDomainDefAddImplicitDevices(vmdef) < 0) return -1; if (qemuDomainAssignAddresses(vmdef, qemuCaps, NULL) < 0) return -1; @@ -7991,7 +7991,7 @@ qemuDomainAttachDeviceConfig(virQEMUCapsPtr qemuCaps, if (qemuDomainChrInsert(vmdef, dev->data.chr) < 0) return -1; dev->data.chr = NULL; - if (virDomainDefAddImplicitControllers(vmdef) < 0) + if (virDomainDefAddImplicitDevices(vmdef) < 0) return -1; if (qemuDomainAssignAddresses(vmdef, qemuCaps, NULL) < 0) return -1; diff --git a/src/vmx/vmx.c b/src/vmx/vmx.c index 4fd0a1d..893c77a 100644 --- a/src/vmx/vmx.c +++ b/src/vmx/vmx.c @@ -1691,7 +1691,7 @@ virVMXParseConfig(virVMXContext *ctx, }
/* def:controllers */ - if (virDomainDefAddImplicitControllers(def) < 0) { + if (virDomainDefAddImplicitDevices(def) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not add controllers"));
Do we care about the error message here? Existing, but perhaps notable... Interesting order in vmx code to add implicit controller/devices before adding defined controller devices. Although I suppose that SCSI controller range check would need adjustment too.
goto cleanup; } diff --git a/src/vz/vz_sdk.c b/src/vz/vz_sdk.c index de73c31..8bf2fe9 100644 --- a/src/vz/vz_sdk.c +++ b/src/vz/vz_sdk.c @@ -1356,7 +1356,7 @@ prlsdkLoadDomain(vzConnPtr privconn, virDomainObjPtr dom) if (prlsdkGetDomainState(sdkdom, &domainState) < 0) goto error;
- if (virDomainDefAddImplicitControllers(def) < 0) + if (virDomainDefAddImplicitDevices(def) < 0) goto error;
if (def->ngraphics > 0) {

On 03/14/2016 02:42 PM, John Ferlan wrote:
On 03/08/2016 11:36 AM, Cole Robinson wrote:
It's just a combination of AddImplicitControllers, and AddConsoleCompat. Every caller that wants ImplicitControllers also wants the ConsoleCompat AFAICT, so lump them together. We also need it for future patches. --- src/conf/domain_conf.c | 19 ++++++++++++++----- src/conf/domain_conf.h | 2 +- src/libvirt_private.syms | 2 +- src/qemu/qemu_driver.c | 6 +++--- src/vmx/vmx.c | 2 +- src/vz/vz_sdk.c | 2 +- 6 files changed, 21 insertions(+), 12 deletions(-)
Not an issue, but a note below...
ACK -
Thanks, pushed now
John
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 39cedbd..40b1929 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -3842,9 +3842,6 @@ virDomainDefPostParseInternal(virDomainDefPtr def, if (virDomainDefPostParseMemory(def, parseFlags) < 0) return -1;
- if (virDomainDefAddConsoleCompat(def) < 0) - return -1; - if (virDomainDefRejectDuplicateControllers(def) < 0) return -1;
@@ -3854,7 +3851,7 @@ virDomainDefPostParseInternal(virDomainDefPtr def, if (virDomainDefPostParseTimer(def) < 0) return -1;
- if (virDomainDefAddImplicitControllers(def) < 0) + if (virDomainDefAddImplicitDevices(def) < 0) return -1;
/* clean up possibly duplicated metadata entries */ @@ -18289,7 +18286,7 @@ virDomainDefMaybeAddSmartcardController(virDomainDefPtr def) * in the XML. This is for compat with existing apps which will * not know/care about <controller> info in the XML */ -int +static int virDomainDefAddImplicitControllers(virDomainDefPtr def) { if (virDomainDefAddDiskControllersForType(def, @@ -18324,6 +18321,18 @@ virDomainDefAddImplicitControllers(virDomainDefPtr def) return 0; }
+int +virDomainDefAddImplicitDevices(virDomainDefPtr def) +{ + if (virDomainDefAddConsoleCompat(def) < 0) + return -1; + + if (virDomainDefAddImplicitControllers(def) < 0) + return -1; + + return 0; +} + virDomainIOThreadIDDefPtr virDomainIOThreadIDFind(virDomainDefPtr def, unsigned int iothread_id) diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 06305f0..6f044d2 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -2697,7 +2697,7 @@ virDomainObjPtr virDomainObjParseFile(const char *filename, bool virDomainDefCheckABIStability(virDomainDefPtr src, virDomainDefPtr dst);
-int virDomainDefAddImplicitControllers(virDomainDefPtr def); +int virDomainDefAddImplicitDevices(virDomainDefPtr def);
virDomainIOThreadIDDefPtr virDomainIOThreadIDFind(virDomainDefPtr def, unsigned int iothread_id); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 3a1b9e1..5399117 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -200,7 +200,7 @@ virDomainControllerRemove; virDomainControllerTypeToString; virDomainCpuPlacementModeTypeFromString; virDomainCpuPlacementModeTypeToString; -virDomainDefAddImplicitControllers; +virDomainDefAddImplicitDevices; virDomainDefAddUSBController; virDomainDefCheckABIStability; virDomainDefCheckDuplicateDiskInfo; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 102fade..9c60518 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -7924,7 +7924,7 @@ qemuDomainAttachDeviceConfig(virQEMUCapsPtr qemuCaps, /* vmdef has the pointer. Generic codes for vmdef will do all jobs */ dev->data.disk = NULL; if (disk->bus != VIR_DOMAIN_DISK_BUS_VIRTIO) - if (virDomainDefAddImplicitControllers(vmdef) < 0) + if (virDomainDefAddImplicitDevices(vmdef) < 0) return -1; if (qemuDomainAssignAddresses(vmdef, qemuCaps, NULL) < 0) return -1; @@ -7949,7 +7949,7 @@ qemuDomainAttachDeviceConfig(virQEMUCapsPtr qemuCaps, if (virDomainHostdevInsert(vmdef, hostdev)) return -1; dev->data.hostdev = NULL; - if (virDomainDefAddImplicitControllers(vmdef) < 0) + if (virDomainDefAddImplicitDevices(vmdef) < 0) return -1; if (qemuDomainAssignAddresses(vmdef, qemuCaps, NULL) < 0) return -1; @@ -7991,7 +7991,7 @@ qemuDomainAttachDeviceConfig(virQEMUCapsPtr qemuCaps, if (qemuDomainChrInsert(vmdef, dev->data.chr) < 0) return -1; dev->data.chr = NULL; - if (virDomainDefAddImplicitControllers(vmdef) < 0) + if (virDomainDefAddImplicitDevices(vmdef) < 0) return -1; if (qemuDomainAssignAddresses(vmdef, qemuCaps, NULL) < 0) return -1; diff --git a/src/vmx/vmx.c b/src/vmx/vmx.c index 4fd0a1d..893c77a 100644 --- a/src/vmx/vmx.c +++ b/src/vmx/vmx.c @@ -1691,7 +1691,7 @@ virVMXParseConfig(virVMXContext *ctx, }
/* def:controllers */ - if (virDomainDefAddImplicitControllers(def) < 0) { + if (virDomainDefAddImplicitDevices(def) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not add controllers"));
Do we care about the error message here?
Existing, but perhaps notable... Interesting order in vmx code to add implicit controller/devices before adding defined controller devices. Although I suppose that SCSI controller range check would need adjustment too.
I just left the error as is. I think the ideal thing would be to get most callers to use virDomainDefAddImplicitDevices via virDomainDefPostParse rather than calling it manually. There's a few other usages spread around too. - Cole

We will use this in upcoming patches --- src/conf/domain_conf.c | 33 +++++++++++++++++++++++---------- src/conf/domain_conf.h | 5 +++++ src/libvirt_private.syms | 1 + 3 files changed, 29 insertions(+), 10 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 40b1929..bc4e369 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -4191,12 +4191,11 @@ virDomainDefPostParseDeviceIterator(virDomainDefPtr def ATTRIBUTE_UNUSED, data->parseFlags, data->xmlopt); } - int -virDomainDefPostParse(virDomainDefPtr def, - virCapsPtr caps, - unsigned int parseFlags, - virDomainXMLOptionPtr xmlopt) +virDomainDefPostParseDevices(virDomainDefPtr def, + virCapsPtr caps, + unsigned int parseFlags, + virDomainXMLOptionPtr xmlopt) { int ret; struct virDomainDefPostParseDeviceIteratorData data = { @@ -4206,6 +4205,23 @@ virDomainDefPostParse(virDomainDefPtr def, .parseFlags = parseFlags, }; + if ((ret = virDomainDeviceInfoIterateInternal(def, + virDomainDefPostParseDeviceIterator, + true, + &data)) < 0) + return ret; + + return 0; +} + +int +virDomainDefPostParse(virDomainDefPtr def, + virCapsPtr caps, + unsigned int parseFlags, + virDomainXMLOptionPtr xmlopt) +{ + int ret; + /* call the domain config callback */ if (xmlopt->config.domainPostParseCallback) { ret = xmlopt->config.domainPostParseCallback(def, caps, parseFlags, @@ -4215,13 +4231,10 @@ virDomainDefPostParse(virDomainDefPtr def, } /* iterate the devices */ - if ((ret = virDomainDeviceInfoIterateInternal(def, - virDomainDefPostParseDeviceIterator, - true, - &data)) < 0) + if ((ret = virDomainDefPostParseDevices(def, caps, + parseFlags, xmlopt)) < 0) return ret; - if ((ret = virDomainDefPostParseInternal(def, caps, parseFlags)) < 0) return ret; diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 6f044d2..aba53a2 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -2476,6 +2476,11 @@ virDomainDefPostParse(virDomainDefPtr def, virCapsPtr caps, unsigned int parseFlags, virDomainXMLOptionPtr xmlopt); +int +virDomainDefPostParseDevices(virDomainDefPtr def, + virCapsPtr caps, + unsigned int parseFlags, + virDomainXMLOptionPtr xmlopt); static inline bool virDomainObjIsActive(virDomainObjPtr dom) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 5399117..51d2721 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -235,6 +235,7 @@ virDomainDefParseFile; virDomainDefParseNode; virDomainDefParseString; virDomainDefPostParse; +virDomainDefPostParseDevices; virDomainDefSetMemoryInitial; virDomainDefSetMemoryTotal; virDomainDefSetVcpus; -- 2.5.0

$SUBJ: "domain: Introduce virDomainDefPostParseDevices" On 03/08/2016 11:36 AM, Cole Robinson wrote:
We will use this in upcoming patches
Export the virDomainDefPostParseDevices rather than having it only be callable during virDomainDefPostParse. Future patches will be able to make use of the optimization.
--- src/conf/domain_conf.c | 33 +++++++++++++++++++++++---------- src/conf/domain_conf.h | 5 +++++ src/libvirt_private.syms | 1 + 3 files changed, 29 insertions(+), 10 deletions(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 40b1929..bc4e369 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -4191,12 +4191,11 @@ virDomainDefPostParseDeviceIterator(virDomainDefPtr def ATTRIBUTE_UNUSED, data->parseFlags, data->xmlopt); }
- int -virDomainDefPostParse(virDomainDefPtr def, - virCapsPtr caps, - unsigned int parseFlags, - virDomainXMLOptionPtr xmlopt) +virDomainDefPostParseDevices(virDomainDefPtr def, + virCapsPtr caps, + unsigned int parseFlags, + virDomainXMLOptionPtr xmlopt) { int ret; struct virDomainDefPostParseDeviceIteratorData data = { @@ -4206,6 +4205,23 @@ virDomainDefPostParse(virDomainDefPtr def, .parseFlags = parseFlags, };
+ if ((ret = virDomainDeviceInfoIterateInternal(def, + virDomainDefPostParseDeviceIterator, + true, + &data)) < 0) + return ret; + + return 0;
I think you could just "return virDomainDeviceInfoIterateInternal(...);" It only returns 0 or -1 anyway. I'm not sure this is necessary - it works, but do we really want to open up just device post processing? Beyond your usage in patch 3, is there another need for it? My concern being missing some subsequent domain check such as virDomainDefPostParseInternal (or worse needing to add code somewhere that eventually adds a call here). The rest of my thoughts are in patch 3... John
+} + +int +virDomainDefPostParse(virDomainDefPtr def, + virCapsPtr caps, + unsigned int parseFlags, + virDomainXMLOptionPtr xmlopt) +{ + int ret; + /* call the domain config callback */ if (xmlopt->config.domainPostParseCallback) { ret = xmlopt->config.domainPostParseCallback(def, caps, parseFlags, @@ -4215,13 +4231,10 @@ virDomainDefPostParse(virDomainDefPtr def, }
/* iterate the devices */ - if ((ret = virDomainDeviceInfoIterateInternal(def, - virDomainDefPostParseDeviceIterator, - true, - &data)) < 0) + if ((ret = virDomainDefPostParseDevices(def, caps, + parseFlags, xmlopt)) < 0) return ret;
- if ((ret = virDomainDefPostParseInternal(def, caps, parseFlags)) < 0) return ret;
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 6f044d2..aba53a2 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -2476,6 +2476,11 @@ virDomainDefPostParse(virDomainDefPtr def, virCapsPtr caps, unsigned int parseFlags, virDomainXMLOptionPtr xmlopt); +int +virDomainDefPostParseDevices(virDomainDefPtr def, + virCapsPtr caps, + unsigned int parseFlags, + virDomainXMLOptionPtr xmlopt);
static inline bool virDomainObjIsActive(virDomainObjPtr dom) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 5399117..51d2721 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -235,6 +235,7 @@ virDomainDefParseFile; virDomainDefParseNode; virDomainDefParseString; virDomainDefPostParse; +virDomainDefPostParseDevices; virDomainDefSetMemoryInitial; virDomainDefSetMemoryTotal; virDomainDefSetVcpus;

In order to make this work, we need to short circuit the normal virDomainDefPostParse ordering, and manually add stock devices ourselves, since we need them in the XML before assigning addresses. The motivation for this is that upcoming patches will want to perform generic PostParse checks that need to run _after_ the driver has assigned all its addresses. Peter objected to this here: https://www.redhat.com/archives/libvir-list/2016-January/msg00200.html Suggesting adding an extra PostParse callback instead. I argued that change isn't necessarily sufficient either, and that this method should be safe since all these functions already need to be idemptotent. The lone test suite change is due to DomainNativeToXML now calling qemuDomainAssignAddresses... apparently that's the only test which hits qemu specific address logic. There's still quite a few manual callers of qemuDomainAssignAddresses that could be dropped too but it would need additional testing, and they shouldn't disrupt anything in the interim since extra calls will be no-ops. --- src/qemu/qemu_domain.c | 13 ++++++++++++- tests/qemuargv2xmldata/qemuargv2xml-pseries-disk.xml | 4 +++- tests/qemuxml2argvtest.c | 20 +++++++------------- tests/qemuxml2xmltest.c | 12 ++++-------- 4 files changed, 26 insertions(+), 23 deletions(-) diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 9044792..d697e99 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -1369,7 +1369,7 @@ qemuCanonicalizeMachine(virDomainDefPtr def, virQEMUCapsPtr qemuCaps) static int qemuDomainDefPostParse(virDomainDefPtr def, virCapsPtr caps, - unsigned int parseFlags ATTRIBUTE_UNUSED, + unsigned int parseFlags, void *opaque) { virQEMUDriverPtr driver = opaque; @@ -1408,6 +1408,17 @@ qemuDomainDefPostParse(virDomainDefPtr def, if (virSecurityManagerVerify(driver->securityManager, def) < 0) goto cleanup; + /* Device defaults are normally set after calling the driver specific + PostParse routine (this function), but we need them earlier. */ + if (virDomainDefPostParseDevices(def, caps, + parseFlags, driver->xmlopt) < 0) + goto cleanup; + if (virDomainDefAddImplicitDevices(def) < 0) + goto cleanup; + + if (qemuDomainAssignAddresses(def, qemuCaps, NULL) < 0) + goto cleanup; + ret = 0; cleanup: virObjectUnref(qemuCaps); diff --git a/tests/qemuargv2xmldata/qemuargv2xml-pseries-disk.xml b/tests/qemuargv2xmldata/qemuargv2xml-pseries-disk.xml index 97225f4..c0d7e94 100644 --- a/tests/qemuargv2xmldata/qemuargv2xml-pseries-disk.xml +++ b/tests/qemuargv2xmldata/qemuargv2xml-pseries-disk.xml @@ -29,7 +29,9 @@ </disk> <controller type='usb' index='0'/> <controller type='pci' index='0' model='pci-root'/> - <controller type='scsi' index='0'/> + <controller type='scsi' index='0'> + <address type='spapr-vio' reg='0x2000'/> + </controller> <input type='keyboard' bus='usb'/> <input type='mouse' bus='usb'/> <graphics type='sdl'/> diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index d29073d..aaec9de 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -277,6 +277,11 @@ static int testCompareXMLToArgvFiles(const char *xml, if (virBitmapParse("0-3", '\0', &nodeset, 4) < 0) goto out; + virQEMUCapsSetList(extraFlags, + QEMU_CAPS_NO_ACPI, + QEMU_CAPS_DEVICE, + QEMU_CAPS_LAST); + if (!(vmdef = virDomainDefParseFile(xml, driver.caps, driver.xmlopt, (VIR_DOMAIN_DEF_PARSE_INACTIVE | parseFlags)))) { @@ -302,11 +307,6 @@ static int testCompareXMLToArgvFiles(const char *xml, if (qemuProcessPrepareMonitorChr(&monitor_chr, domainLibDir) < 0) goto out; - virQEMUCapsSetList(extraFlags, - QEMU_CAPS_NO_ACPI, - QEMU_CAPS_DEVICE, - QEMU_CAPS_LAST); - if (STREQ(vmdef->os.machine, "pc") && STREQ(vmdef->emulator, "/usr/bin/qemu-system-x86_64")) { VIR_FREE(vmdef->os.machine); @@ -316,12 +316,6 @@ static int testCompareXMLToArgvFiles(const char *xml, virQEMUCapsFilterByMachineType(extraFlags, vmdef->os.machine); - if (qemuDomainAssignAddresses(vmdef, extraFlags, NULL)) { - if (flags & FLAG_EXPECT_ERROR) - goto ok; - goto out; - } - log = virtTestLogContentAndReset(); VIR_FREE(log); virResetLastError(); @@ -1420,7 +1414,7 @@ mymain(void) QEMU_CAPS_PCI_OHCI, QEMU_CAPS_PCI_MULTIFUNCTION); DO_TEST("pseries-vio-user-assigned", QEMU_CAPS_CHARDEV, QEMU_CAPS_NODEFCONFIG); - DO_TEST_ERROR("pseries-vio-address-clash", + DO_TEST_PARSE_ERROR("pseries-vio-address-clash", QEMU_CAPS_CHARDEV, QEMU_CAPS_NODEFCONFIG); DO_TEST("pseries-nvram", QEMU_CAPS_DEVICE_NVRAM); DO_TEST("pseries-usb-kbd", QEMU_CAPS_PCI_OHCI, @@ -1583,7 +1577,7 @@ mymain(void) QEMU_CAPS_DEVICE_VIDEO_PRIMARY, QEMU_CAPS_VGA_QXL, QEMU_CAPS_DEVICE_QXL); - DO_TEST_ERROR("pcie-root-port-too-many", + DO_TEST_PARSE_ERROR("pcie-root-port-too-many", QEMU_CAPS_DEVICE_PCI_BRIDGE, QEMU_CAPS_DEVICE_DMI_TO_PCI_BRIDGE, QEMU_CAPS_DEVICE_IOH3420, diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c index 0735677..251effd 100644 --- a/tests/qemuxml2xmltest.c +++ b/tests/qemuxml2xmltest.c @@ -37,12 +37,11 @@ struct testInfo { }; static int -qemuXML2XMLPreFormatCallback(virDomainDefPtr def, const void *opaque) +qemuXML2XMLPreFormatCallback(virDomainDefPtr def ATTRIBUTE_UNUSED, + const void *opaque ATTRIBUTE_UNUSED) { - const struct testInfo *info = opaque; - - if (qemuDomainAssignAddresses(def, info->qemuCaps, NULL)) - return -1; + /* Unused for now. But can eventually be used to test + qemuAssignDeviceAliases for example */ return 0; } @@ -151,9 +150,6 @@ testCompareStatusXMLToXMLFiles(const void *opaque) goto cleanup; } - if (qemuDomainAssignAddresses(obj->def, data->qemuCaps, NULL)) - goto cleanup; - /* format it back */ if (!(actual = virDomainObjFormat(driver.xmlopt, obj, NULL, VIR_DOMAIN_DEF_FORMAT_SECURE))) { -- 2.5.0

On 03/08/2016 11:36 AM, Cole Robinson wrote:
In order to make this work, we need to short circuit the normal virDomainDefPostParse ordering, and manually add stock devices ourselves, since we need them in the XML before assigning addresses.
The motivation for this is that upcoming patches will want to perform generic PostParse checks that need to run _after_ the driver has assigned all its addresses.
Do you mean you need to perform the generic DevicePostParse checks after the driver has assigned all its addresses or the generic (domain) PostParse checks. Based on reading patch 6 of this series, it seems the latter, but the ImplicitDevices check is involved. <snip>
Peter objected to this here: https://www.redhat.com/archives/libvir-list/2016-January/msg00200.html
Suggesting adding an extra PostParse callback instead. I argued that change isn't necessarily sufficient either, and that this method should be safe since all these functions already need to be idemptotent.
The preceding hunk seems to have been more relevant for something after the '---' so as to not be included in git history. </snip> Even without the upcoming patches - it seems to me this patch is designed to ensure that once the qemuDomainDefPostParse code adds DefaultDevices that we make sure that those devices and the existing ones for the domain go through the device post parse processing before adding implicit devices and assigning addresses for any devices without one. The key of course being the assign addresses which needs to be called after each device has been addressed. So the problems are: 1. We don't add the ImplicitDevices early enough 2. We don't assign the DeviceAddress early enough where "early enough" is defined as before virDomainDefPostParseInternal during virDomainDefPostParse. The chicken/egg problem being that the PostParseInternal function calls virDomainDefAddImplicitControllers. Another "option" it seems would be to add a 3rd callback mechanism to assign addresses for all domains (if supported/necessary). This would be called in virDomainDefPostParse before the *DefPostParseInternal. Going this way I don't think you need current patch 2... So starting with the implicit devices - it doesn't seem there is anything in the *PostParseInternal that's adding a device, so instead of the current patch 2, can we move that call to virDomainDefPostParse? Then patch 3 could add a call to a device address assignment callback, such as the following: virDomainDefPostParse .domainDefPostCallback (qemuDomainDefPostParse) ... qemuDomainAddDefaultDevices ... virDomainDeviceInfoIterateInternal for each device .devicesPostParseCallback virDomainDefAddImplicitControllers .deviceAssignAddrsPostParseCallback (qemuDomainAssignAddresses) virDomainDefPostParseInternal As compared to how I read this patch: virDomainDefPostParse .domainDefPostCallback (qemuDomainDefPostParse) ... qemuDomainAddDefaultDevices virDomainDefPostParseDevices for each device .devicesPostParseCallback virDomainDefAddImplicitDevices qemuDomainAssignAddresses ... virDomainDefPostParseDevices NOTE: Duplicated for qemu... for each device and shouldn't do anything .devicesPostParseCallback virDomainDefPostParseInternal FWIW: The rest of this was written first, then I started trying to figure out what problem was being solved... I'll leave the comments as is since they point out my thinking while reviewing. John (I am away from keyboard until later tonight if you read this today, but figured I'd post something for you to consider).
The lone test suite change is due to DomainNativeToXML now calling qemuDomainAssignAddresses... apparently that's the only test which hits qemu specific address logic.
There's still quite a few manual callers of qemuDomainAssignAddresses that could be dropped too but it would need additional testing, and they shouldn't disrupt anything in the interim since extra calls will be no-ops. --- src/qemu/qemu_domain.c | 13 ++++++++++++- tests/qemuargv2xmldata/qemuargv2xml-pseries-disk.xml | 4 +++- tests/qemuxml2argvtest.c | 20 +++++++------------- tests/qemuxml2xmltest.c | 12 ++++-------- 4 files changed, 26 insertions(+), 23 deletions(-)
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 9044792..d697e99 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -1369,7 +1369,7 @@ qemuCanonicalizeMachine(virDomainDefPtr def, virQEMUCapsPtr qemuCaps) static int qemuDomainDefPostParse(virDomainDefPtr def, virCapsPtr caps, - unsigned int parseFlags ATTRIBUTE_UNUSED, + unsigned int parseFlags, void *opaque) { virQEMUDriverPtr driver = opaque; @@ -1408,6 +1408,17 @@ qemuDomainDefPostParse(virDomainDefPtr def, if (virSecurityManagerVerify(driver->securityManager, def) < 0) goto cleanup;
Should this hunk go after qemuDomainDefAddDefaultDevices? Nothing in the three interceding calls seems to add a device, controller, etc.
+ /* Device defaults are normally set after calling the driver specific + PostParse routine (this function), but we need them earlier. */ + if (virDomainDefPostParseDevices(def, caps, + parseFlags, driver->xmlopt) < 0) + goto cleanup;
NIT: Add extra blank line here. So this is the one I'm really struggling with mainly because we go through the device iteration twice, where it seems the claim is that the second iteration would be unnecessary (that's how I read the previous discussion at least). Although if not necessary, then we wouldn't assign addresses to whatever could have been changed by the second call. So, I'm curious if using the recently added xmlopt->config.features bits could/should somehow be used to avoid the second pass? That is after this call could we set a xmlopt->config.features bit that could be checked in the called function to not initiate the virDomainDefPostParseDeviceIterator? I guess the other option is to understand whether it's qemuDomainDeviceDefPostParse that needs to be run or whether it's the virDomainDeviceDefPostParseInternal and virDomainDeviceDefPostParseCheckFeatures that needs to be run. Guess I'm really thinking about some environment where there's 100's of devices (or more) that we'll needlessly spin in while loops.
+ if (virDomainDefAddImplicitDevices(def) < 0) + goto cleanup;
Would calling this a second time be duplicitous? e.g.it's called in virDomainDefPostParseInternal
+ + if (qemuDomainAssignAddresses(def, qemuCaps, NULL) < 0) + goto cleanup; + ret = 0; cleanup: virObjectUnref(qemuCaps); diff --git a/tests/qemuargv2xmldata/qemuargv2xml-pseries-disk.xml b/tests/qemuargv2xmldata/qemuargv2xml-pseries-disk.xml index 97225f4..c0d7e94 100644 --- a/tests/qemuargv2xmldata/qemuargv2xml-pseries-disk.xml +++ b/tests/qemuargv2xmldata/qemuargv2xml-pseries-disk.xml @@ -29,7 +29,9 @@ </disk> <controller type='usb' index='0'/> <controller type='pci' index='0' model='pci-root'/> - <controller type='scsi' index='0'/> + <controller type='scsi' index='0'> + <address type='spapr-vio' reg='0x2000'/> + </controller> <input type='keyboard' bus='usb'/> <input type='mouse' bus='usb'/> <graphics type='sdl'/> diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index d29073d..aaec9de 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -277,6 +277,11 @@ static int testCompareXMLToArgvFiles(const char *xml, if (virBitmapParse("0-3", '\0', &nodeset, 4) < 0) goto out;
+ virQEMUCapsSetList(extraFlags, + QEMU_CAPS_NO_ACPI, + QEMU_CAPS_DEVICE, + QEMU_CAPS_LAST); +
Why is this moved unless perhaps the goal was to use the flags in the following call... The 'extraFlags' is only used later anyway in the virQEMUCapsFilterByMachineType. Since qemuDomainAssignAddresses was removed and the series involves erroring during parse, I started to wonder... especially since the removed call used the extraFlags.
if (!(vmdef = virDomainDefParseFile(xml, driver.caps, driver.xmlopt, (VIR_DOMAIN_DEF_PARSE_INACTIVE | parseFlags)))) { @@ -302,11 +307,6 @@ static int testCompareXMLToArgvFiles(const char *xml, if (qemuProcessPrepareMonitorChr(&monitor_chr, domainLibDir) < 0) goto out;
- virQEMUCapsSetList(extraFlags, - QEMU_CAPS_NO_ACPI, - QEMU_CAPS_DEVICE, - QEMU_CAPS_LAST); - if (STREQ(vmdef->os.machine, "pc") && STREQ(vmdef->emulator, "/usr/bin/qemu-system-x86_64")) { VIR_FREE(vmdef->os.machine); @@ -316,12 +316,6 @@ static int testCompareXMLToArgvFiles(const char *xml,
virQEMUCapsFilterByMachineType(extraFlags, vmdef->os.machine);
- if (qemuDomainAssignAddresses(vmdef, extraFlags, NULL)) { - if (flags & FLAG_EXPECT_ERROR) - goto ok; - goto out; - } - log = virtTestLogContentAndReset(); VIR_FREE(log); virResetLastError(); @@ -1420,7 +1414,7 @@ mymain(void) QEMU_CAPS_PCI_OHCI, QEMU_CAPS_PCI_MULTIFUNCTION); DO_TEST("pseries-vio-user-assigned", QEMU_CAPS_CHARDEV, QEMU_CAPS_NODEFCONFIG); - DO_TEST_ERROR("pseries-vio-address-clash", + DO_TEST_PARSE_ERROR("pseries-vio-address-clash", QEMU_CAPS_CHARDEV, QEMU_CAPS_NODEFCONFIG); DO_TEST("pseries-nvram", QEMU_CAPS_DEVICE_NVRAM); DO_TEST("pseries-usb-kbd", QEMU_CAPS_PCI_OHCI, @@ -1583,7 +1577,7 @@ mymain(void) QEMU_CAPS_DEVICE_VIDEO_PRIMARY, QEMU_CAPS_VGA_QXL, QEMU_CAPS_DEVICE_QXL);
- DO_TEST_ERROR("pcie-root-port-too-many", + DO_TEST_PARSE_ERROR("pcie-root-port-too-many", QEMU_CAPS_DEVICE_PCI_BRIDGE, QEMU_CAPS_DEVICE_DMI_TO_PCI_BRIDGE, QEMU_CAPS_DEVICE_IOH3420, diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c index 0735677..251effd 100644 --- a/tests/qemuxml2xmltest.c +++ b/tests/qemuxml2xmltest.c @@ -37,12 +37,11 @@ struct testInfo { };
static int -qemuXML2XMLPreFormatCallback(virDomainDefPtr def, const void *opaque) +qemuXML2XMLPreFormatCallback(virDomainDefPtr def ATTRIBUTE_UNUSED, + const void *opaque ATTRIBUTE_UNUSED) { - const struct testInfo *info = opaque; - - if (qemuDomainAssignAddresses(def, info->qemuCaps, NULL)) - return -1; + /* Unused for now. But can eventually be used to test + qemuAssignDeviceAliases for example */
return 0; } @@ -151,9 +150,6 @@ testCompareStatusXMLToXMLFiles(const void *opaque) goto cleanup; }
- if (qemuDomainAssignAddresses(obj->def, data->qemuCaps, NULL)) - goto cleanup; - /* format it back */ if (!(actual = virDomainObjFormat(driver.xmlopt, obj, NULL, VIR_DOMAIN_DEF_FORMAT_SECURE))) {

On 03/14/2016 02:42 PM, John Ferlan wrote:
On 03/08/2016 11:36 AM, Cole Robinson wrote:
In order to make this work, we need to short circuit the normal virDomainDefPostParse ordering, and manually add stock devices ourselves, since we need them in the XML before assigning addresses.
The motivation for this is that upcoming patches will want to perform generic PostParse checks that need to run _after_ the driver has assigned all its addresses.
Do you mean you need to perform the generic DevicePostParse checks after the driver has assigned all its addresses or the generic (domain) PostParse checks. Based on reading patch 6 of this series, it seems the latter, but the ImplicitDevices check is involved.
After patch #6 I want the control flow to be virDomainDefPostParse-> qemuDomainPostParse AddImplicitDevices qemuDomainAssignAddresses virDomainCheckUnallocatedDeviceAddrs AddImplicitDevices needs to come before qemuDomainAssignAddresses, so the implicit controllers get addresses allocated by the qemu driver virDomainCheckUnallocatedDeviceAddrs needs to come after qemuDomainAssignAddresses. I want virDomainCheckUnallocatedDeviceAddrs in generic code, so we don't need to cram it in every HVs PostParse callback.
<snip>
Peter objected to this here: https://www.redhat.com/archives/libvir-list/2016-January/msg00200.html
Suggesting adding an extra PostParse callback instead. I argued that change isn't necessarily sufficient either, and that this method should be safe since all these functions already need to be idemptotent.
The preceding hunk seems to have been more relevant for something after the '---' so as to not be included in git history.
</snip>
Even without the upcoming patches - it seems to me this patch is designed to ensure that once the qemuDomainDefPostParse code adds DefaultDevices that we make sure that those devices and the existing ones for the domain go through the device post parse processing before adding implicit devices and assigning addresses for any devices without one.
The key of course being the assign addresses which needs to be called after each device has been addressed.
So the problems are:
1. We don't add the ImplicitDevices early enough 2. We don't assign the DeviceAddress early enough
where "early enough" is defined as before virDomainDefPostParseInternal during virDomainDefPostParse. The chicken/egg problem being that the PostParseInternal function calls virDomainDefAddImplicitControllers.
Another "option" it seems would be to add a 3rd callback mechanism to assign addresses for all domains (if supported/necessary). This would be called in virDomainDefPostParse before the *DefPostParseInternal. Going this way I don't think you need current patch 2...
So starting with the implicit devices - it doesn't seem there is anything in the *PostParseInternal that's adding a device, so instead of the current patch 2, can we move that call to virDomainDefPostParse?
Then patch 3 could add a call to a device address assignment callback, such as the following:
virDomainDefPostParse .domainDefPostCallback (qemuDomainDefPostParse) ... qemuDomainAddDefaultDevices ...
virDomainDeviceInfoIterateInternal for each device .devicesPostParseCallback
virDomainDefAddImplicitControllers
.deviceAssignAddrsPostParseCallback (qemuDomainAssignAddresses)
virDomainDefPostParseInternal
As compared to how I read this patch:
virDomainDefPostParse .domainDefPostCallback (qemuDomainDefPostParse) ... qemuDomainAddDefaultDevices virDomainDefPostParseDevices for each device .devicesPostParseCallback virDomainDefAddImplicitDevices qemuDomainAssignAddresses ...
virDomainDefPostParseDevices NOTE: Duplicated for qemu... for each device and shouldn't do anything .devicesPostParseCallback
virDomainDefPostParseInternal
FWIW: The rest of this was written first, then I started trying to figure out what problem was being solved... I'll leave the comments as is since they point out my thinking while reviewing.
Thank you for your comments. I think the idea of adding a new post parse callback specifically for assigning addresses is a good one; giving it an explicit purpose makes it more clear when hv drivers should actually implement it. And it's probably the lowest effort way to implement all this :) I'm not going to respond in detail to every point, since if your above suggestion will eliminate some of the code you responded to.
diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index d29073d..aaec9de 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -277,6 +277,11 @@ static int testCompareXMLToArgvFiles(const char *xml, if (virBitmapParse("0-3", '\0', &nodeset, 4) < 0) goto out;
+ virQEMUCapsSetList(extraFlags, + QEMU_CAPS_NO_ACPI, + QEMU_CAPS_DEVICE, + QEMU_CAPS_LAST); +
Why is this moved unless perhaps the goal was to use the flags in the following call... The 'extraFlags' is only used later anyway in the virQEMUCapsFilterByMachineType. Since qemuDomainAssignAddresses was removed and the series involves erroring during parse, I started to wonder... especially since the removed call used the extraFlags.
The code now does virDomainDefParseFile->...->qemuDomainAssignAddresses and the latter bit depends heavily on QEMU_CAPS flags. extraFlags in this context is already indirectly wedged into the fake qemu driver state, so its implicitly sent to qemuDomainAssignAddresses - Cole

Returns an integer count of the number of XML properties an element has. Will be used in future patches. --- src/util/virxml.c | 17 +++++++++++++++++ src/util/virxml.h | 1 + 2 files changed, 18 insertions(+) diff --git a/src/util/virxml.c b/src/util/virxml.c index 489bad8..86c021c 100644 --- a/src/util/virxml.c +++ b/src/util/virxml.c @@ -890,6 +890,23 @@ virXMLChildElementCount(xmlNodePtr node) return ret; } +/* Returns the number of node's properties, or -1 on error. */ +long +virXMLPropertyCount(xmlNodePtr node) +{ + long ret = 0; + xmlAttrPtr cur = NULL; + + if (!node || node->type != XML_ELEMENT_NODE) + return -1; + cur = node->properties; + while (cur) { + ret++; + cur = cur->next; + } + return ret; +} + /** * virXMLNodeToString: convert an XML node ptr to an XML string diff --git a/src/util/virxml.h b/src/util/virxml.h index b94de74..5cf082e 100644 --- a/src/util/virxml.h +++ b/src/util/virxml.h @@ -72,6 +72,7 @@ int virXPathNodeSet(const char *xpath, char * virXMLPropString(xmlNodePtr node, const char *name); long virXMLChildElementCount(xmlNodePtr node); +long virXMLPropertyCount(xmlNodePtr node); /* Internal function; prefer the macros below. */ xmlDocPtr virXMLParseHelper(int domcode, -- 2.5.0

On 03/08/2016 11:36 AM, Cole Robinson wrote:
Returns an integer count of the number of XML properties an element has. Will be used in future patches. --- src/util/virxml.c | 17 +++++++++++++++++ src/util/virxml.h | 1 + 2 files changed, 18 insertions(+)
Sorry for not completing, I got side tracked with the hostdev series... Then of course my own work (and play) interrupts... I see virXMLChildElementCount in libvirt_private.syms, but this doesn't have to be there? Soft ACK - depends on usage in patch 6... John

Will be used in future patches --- tests/bhyvexml2xmltest.c | 2 +- tests/genericxml2xmltest.c | 2 +- tests/lxcxml2xmltest.c | 2 +- tests/qemuxml2xmltest.c | 5 +++-- tests/testutils.c | 10 ++++++++-- tests/testutils.h | 4 ++++ 6 files changed, 18 insertions(+), 7 deletions(-) diff --git a/tests/bhyvexml2xmltest.c b/tests/bhyvexml2xmltest.c index 8f556ee..87bc39c 100644 --- a/tests/bhyvexml2xmltest.c +++ b/tests/bhyvexml2xmltest.c @@ -32,7 +32,7 @@ testCompareXMLToXMLHelper(const void *data) ret = testCompareDomXML2XMLFiles(driver.caps, driver.xmlopt, xml_in, info->different ? xml_out : xml_in, - false, + false, 0, NULL, NULL, 0); cleanup: diff --git a/tests/genericxml2xmltest.c b/tests/genericxml2xmltest.c index bf9b11d..666fc86 100644 --- a/tests/genericxml2xmltest.c +++ b/tests/genericxml2xmltest.c @@ -39,7 +39,7 @@ testCompareXMLToXMLHelper(const void *data) ret = testCompareDomXML2XMLFiles(caps, xmlopt, xml_in, info->different ? xml_out : xml_in, - !info->inactive_only, + !info->inactive_only, 0, NULL, NULL, 0); cleanup: VIR_FREE(xml_in); diff --git a/tests/lxcxml2xmltest.c b/tests/lxcxml2xmltest.c index 0b51340..c2140bc 100644 --- a/tests/lxcxml2xmltest.c +++ b/tests/lxcxml2xmltest.c @@ -45,7 +45,7 @@ testCompareXMLToXMLHelper(const void *data) ret = testCompareDomXML2XMLFiles(caps, xmlopt, xml_in, info->different ? xml_out : xml_in, - !info->inactive_only, + !info->inactive_only, 0, NULL, NULL, info->parse_flags); cleanup: VIR_FREE(xml_in); diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c index 251effd..b3568df 100644 --- a/tests/qemuxml2xmltest.c +++ b/tests/qemuxml2xmltest.c @@ -52,7 +52,8 @@ testXML2XMLActive(const void *opaque) const struct testInfo *info = opaque; return testCompareDomXML2XMLFiles(driver.caps, driver.xmlopt, - info->inName, info->outActiveName, true, + info->inName, info->outActiveName, + true, 0, qemuXML2XMLPreFormatCallback, opaque, 0); } @@ -63,7 +64,7 @@ testXML2XMLInactive(const void *opaque) const struct testInfo *info = opaque; return testCompareDomXML2XMLFiles(driver.caps, driver.xmlopt, info->inName, - info->outInactiveName, false, + info->outInactiveName, false, 0, qemuXML2XMLPreFormatCallback, opaque, 0); } diff --git a/tests/testutils.c b/tests/testutils.c index b1bd4e8..ec1ee38 100644 --- a/tests/testutils.c +++ b/tests/testutils.c @@ -1052,7 +1052,8 @@ virDomainXMLOptionPtr virTestGenericDomainXMLConfInit(void) int testCompareDomXML2XMLFiles(virCapsPtr caps, virDomainXMLOptionPtr xmlopt, - const char *infile, const char *outfile, bool live, + const char *infile, const char *outfile, + bool live, testCompareDomXML2XMLFlags flags, testCompareDomXML2XMLPreFormatCallback cb, const void *opaque, unsigned int parseFlags) { @@ -1067,8 +1068,12 @@ testCompareDomXML2XMLFiles(virCapsPtr caps, virDomainXMLOptionPtr xmlopt, if (!live) format_flags |= VIR_DOMAIN_DEF_FORMAT_INACTIVE; - if (!(def = virDomainDefParseFile(infile, caps, xmlopt, parse_flags))) + if (!(def = virDomainDefParseFile(infile, caps, xmlopt, parse_flags))) { + if (!virtTestOOMActive() && + (flags & TEST_COMPARE_DOM_XML2XML_FLAG_EXPECT_PARSE_ERROR)) + goto ok; goto fail; + } if (!virDomainDefCheckABIStability(def, def)) { VIR_TEST_DEBUG("ABI stability check failed on %s", infile); @@ -1084,6 +1089,7 @@ testCompareDomXML2XMLFiles(virCapsPtr caps, virDomainXMLOptionPtr xmlopt, if (virtTestCompareToFile(actual, outfile) < 0) goto fail; + ok: ret = 0; fail: VIR_FREE(actual); diff --git a/tests/testutils.h b/tests/testutils.h index 752fa52..a09fd58 100644 --- a/tests/testutils.h +++ b/tests/testutils.h @@ -134,6 +134,9 @@ int virtTestMain(int argc, virCapsPtr virTestGenericCapsInit(void); virDomainXMLOptionPtr virTestGenericDomainXMLConfInit(void); +typedef enum { + TEST_COMPARE_DOM_XML2XML_FLAG_EXPECT_PARSE_ERROR = 1 << 0, +} testCompareDomXML2XMLFlags; typedef int (*testCompareDomXML2XMLPreFormatCallback)(virDomainDefPtr def, const void *opaque); int testCompareDomXML2XMLFiles(virCapsPtr caps, @@ -141,6 +144,7 @@ int testCompareDomXML2XMLFiles(virCapsPtr caps, const char *inxml, const char *outfile, bool live, + testCompareDomXML2XMLFlags flags, testCompareDomXML2XMLPreFormatCallback cb, const void *opaque, unsigned int parseFlags); -- 2.5.0

On 03/08/2016 11:36 AM, Cole Robinson wrote:
Will be used in future patches
Could be beefed up to indicate how it's expected to be used...
--- tests/bhyvexml2xmltest.c | 2 +- tests/genericxml2xmltest.c | 2 +- tests/lxcxml2xmltest.c | 2 +- tests/qemuxml2xmltest.c | 5 +++-- tests/testutils.c | 10 ++++++++-- tests/testutils.h | 4 ++++ 6 files changed, 18 insertions(+), 7 deletions(-)
soft ACK (looks right, but I'm just not as fluent in tests changes as I should be ;-)) John

If a bare device <address type='pci'/> is specified, set an internal flag address->auto_allocate. Individual hv drivers can then check for this and act on it if they want, nothing is allocated in generic code. If drivers allocate an address, they are expected to unset auto_allocate. Generic domain conf code then checks at XML format time to ensure no device addresses still have auto_allocate set; this ensures we aren't formatting any bogus address XML, and informing the user if their request didn't work. Add a genericxml2xml test case for this. The auto_allocate property is a part of the generic address structure and not the PCI specific bits, this will make it easier to reuse with other address types too. One note: we detect <address type='pci'/> by counting it's XML properties, rather than comparing specifically against parsed values, which seems easier to maintain. --- docs/schemas/domaincommon.rng | 5 +++- src/conf/domain_conf.c | 29 +++++++++++++++++++++- src/conf/domain_conf.h | 1 + .../generic-pci-autofill-addr.xml | 27 ++++++++++++++++++++ tests/genericxml2xmltest.c | 17 +++++++++---- 5 files changed, 72 insertions(+), 7 deletions(-) create mode 100644 tests/genericxml2xmlindata/generic-pci-autofill-addr.xml diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index 6ca937c..d083250 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -4471,7 +4471,10 @@ <attribute name="type"> <value>pci</value> </attribute> - <ref name="pciaddress"/> + <choice> + <ref name="pciaddress"/> + <empty/> + </choice> </group> <group> <attribute name="type"> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index bc4e369..bbc42a4 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -3827,6 +3827,23 @@ virDomainDefPostParseTimer(virDomainDefPtr def) } + static int +virDomainCheckUnallocatedDeviceAddrs(virDomainDefPtr def ATTRIBUTE_UNUSED, + virDomainDeviceDefPtr dev, + virDomainDeviceInfoPtr info, + void *data ATTRIBUTE_UNUSED) +{ + if (!info->auto_allocate) + return 0; + + virReportError(VIR_ERR_INTERNAL_ERROR, + _("driver didn't allocate requested address type '%s' for device '%s'"), + virDomainDeviceAddressTypeToString(info->type), + virDomainDeviceTypeToString(dev->type)); + return -1; +} + + static int virDomainDefPostParseInternal(virDomainDefPtr def, virCapsPtr caps ATTRIBUTE_UNUSED, @@ -3851,6 +3868,11 @@ virDomainDefPostParseInternal(virDomainDefPtr def, if (virDomainDefPostParseTimer(def) < 0) return -1; + /* ensure the driver filled in any auto_allocate addrs */ + if (virDomainDeviceInfoIterate(def, virDomainCheckUnallocatedDeviceAddrs, + NULL) < 0) + return -1; + if (virDomainDefAddImplicitDevices(def) < 0) return -1; @@ -4872,8 +4894,13 @@ virDomainDeviceInfoParseXML(xmlNodePtr node, switch ((virDomainDeviceAddressType) info->type) { case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI: - if (virDevicePCIAddressParseXML(address, &info->addr.pci) < 0) + if (virXMLPropertyCount(address) == 1) { + /* Bare <address type='pci'/> is a request to allocate + the address. */ + info->auto_allocate = true; + } else if (virDevicePCIAddressParseXML(address, &info->addr.pci) < 0) { goto cleanup; + } break; case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE: diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index aba53a2..dd9d0b1 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -346,6 +346,7 @@ struct _virDomainDeviceInfo { */ char *alias; int type; /* virDomainDeviceAddressType */ + bool auto_allocate; union { virDevicePCIAddress pci; virDomainDeviceDriveAddress drive; diff --git a/tests/genericxml2xmlindata/generic-pci-autofill-addr.xml b/tests/genericxml2xmlindata/generic-pci-autofill-addr.xml new file mode 100644 index 0000000..06eadb6 --- /dev/null +++ b/tests/genericxml2xmlindata/generic-pci-autofill-addr.xml @@ -0,0 +1,27 @@ +<domain type='test'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219136</memory> + <currentMemory unit='KiB'>219136</currentMemory> + <vcpu placement='static'>1</vcpu> + <os> + <type arch='i686' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu</emulator> + <disk type='block' device='disk'> + <driver name='qemu' type='raw'/> + <source dev='/dev/HostVG/QEMUGuest1'/> + <target dev='vda' bus='virtio'/> + <address type='pci'/> + </disk> + <controller type='usb' index='0'> + <address type='pci'/> + </controller> + </devices> +</domain> diff --git a/tests/genericxml2xmltest.c b/tests/genericxml2xmltest.c index 666fc86..b329d10 100644 --- a/tests/genericxml2xmltest.c +++ b/tests/genericxml2xmltest.c @@ -21,6 +21,7 @@ struct testInfo { const char *name; int different; bool inactive_only; + testCompareDomXML2XMLFlags compare_flags; }; static int @@ -39,7 +40,7 @@ testCompareXMLToXMLHelper(const void *data) ret = testCompareDomXML2XMLFiles(caps, xmlopt, xml_in, info->different ? xml_out : xml_in, - !info->inactive_only, 0, + !info->inactive_only, info->compare_flags, NULL, NULL, 0); cleanup: VIR_FREE(xml_in); @@ -59,21 +60,27 @@ mymain(void) if (!(xmlopt = virTestGenericDomainXMLConfInit())) return EXIT_FAILURE; -#define DO_TEST_FULL(name, is_different, inactive) \ +#define DO_TEST_FULL(name, is_different, inactive, compare_flags) \ do { \ - const struct testInfo info = {name, is_different, inactive}; \ + const struct testInfo info = {name, is_different, \ + inactive, compare_flags}; \ if (virtTestRun("GENERIC XML-2-XML " name, \ testCompareXMLToXMLHelper, &info) < 0) \ ret = -1; \ } while (0) #define DO_TEST(name) \ - DO_TEST_FULL(name, 0, false) + DO_TEST_FULL(name, 0, false, 0) + +#define DO_TEST_PARSE_ERROR(name) \ + DO_TEST_FULL(name, 0, false, \ + TEST_COMPARE_DOM_XML2XML_FLAG_EXPECT_PARSE_ERROR) #define DO_TEST_DIFFERENT(name) \ - DO_TEST_FULL(name, 1, false) + DO_TEST_FULL(name, 1, false, 0) DO_TEST_DIFFERENT("disk-virtio"); + DO_TEST_PARSE_ERROR("pci-autofill-addr"); virObjectUnref(caps); virObjectUnref(xmlopt); -- 2.5.0

On 03/08/2016 11:36 AM, Cole Robinson wrote:
If a bare device <address type='pci'/> is specified, set an internal flag address->auto_allocate. Individual hv drivers can then check for this and act on it if they want, nothing is allocated in generic code.
If drivers allocate an address, they are expected to unset auto_allocate. Generic domain conf code then checks at XML format time to ensure no device addresses still have auto_allocate set; this ensures we aren't formatting any bogus address XML, and informing the user if their request didn't work. Add a genericxml2xml test case for this.
The auto_allocate property is a part of the generic address structure and not the PCI specific bits, this will make it easier to reuse with other address types too.
One note: we detect <address type='pci'/> by counting it's XML properties, rather than comparing specifically against parsed values, which seems easier to maintain. --- docs/schemas/domaincommon.rng | 5 +++- src/conf/domain_conf.c | 29 +++++++++++++++++++++- src/conf/domain_conf.h | 1 + .../generic-pci-autofill-addr.xml | 27 ++++++++++++++++++++ tests/genericxml2xmltest.c | 17 +++++++++---- 5 files changed, 72 insertions(+), 7 deletions(-) create mode 100644 tests/genericxml2xmlindata/generic-pci-autofill-addr.xml
Will also need to update 'formatdomain.html.in' to describe the new allowance of just "<address type='pci'>... Would this be useful if "multifunction='on'" without specifying an address? e.g.: <address type='pci' multifunction='on'> Could other address types find the functionality useful? That is, I want address type 'drive' or 'usb', but I have no idea how to fill in the rest, I want the hv to do it for me. Rather than focus on anything that could change if patch3 is adjusted... I'll point only a couple of things here. I do think the less times to iterate through devices the better - the whole address assignment processing and post part device iteration is complex enough already! John
diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index 6ca937c..d083250 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -4471,7 +4471,10 @@ <attribute name="type"> <value>pci</value> </attribute> - <ref name="pciaddress"/> + <choice> + <ref name="pciaddress"/> + <empty/> + </choice> </group> <group> <attribute name="type"> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index bc4e369..bbc42a4 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -3827,6 +3827,23 @@ virDomainDefPostParseTimer(virDomainDefPtr def) }
+ static int ^ Extraneous space
+virDomainCheckUnallocatedDeviceAddrs(virDomainDefPtr def ATTRIBUTE_UNUSED, + virDomainDeviceDefPtr dev, + virDomainDeviceInfoPtr info, + void *data ATTRIBUTE_UNUSED) +{ + if (!info->auto_allocate) + return 0; + + virReportError(VIR_ERR_INTERNAL_ERROR, + _("driver didn't allocate requested address type '%s' for device '%s'"), + virDomainDeviceAddressTypeToString(info->type), + virDomainDeviceTypeToString(dev->type)); + return -1; +} + + static int virDomainDefPostParseInternal(virDomainDefPtr def, virCapsPtr caps ATTRIBUTE_UNUSED, @@ -3851,6 +3868,11 @@ virDomainDefPostParseInternal(virDomainDefPtr def, if (virDomainDefPostParseTimer(def) < 0) return -1;
+ /* ensure the driver filled in any auto_allocate addrs */ + if (virDomainDeviceInfoIterate(def, virDomainCheckUnallocatedDeviceAddrs, + NULL) < 0) + return -1; +
Why couldn't this go in virDomainDefPostParseDeviceIterator? That is, rather than have yet another iterator through the devices, call virDomainCheckUnallocatedDeviceAddrs right after or at the end of virDomainDeviceDefPostParse...
if (virDomainDefAddImplicitDevices(def) < 0) return -1;
@@ -4872,8 +4894,13 @@ virDomainDeviceInfoParseXML(xmlNodePtr node,
switch ((virDomainDeviceAddressType) info->type) { case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI: - if (virDevicePCIAddressParseXML(address, &info->addr.pci) < 0) + if (virXMLPropertyCount(address) == 1) {
Wish there was some other way than counting attributes, but I guess since "type" is an attribute we can hopefully guarantee that only "type=<something>" has been supplied.
+ /* Bare <address type='pci'/> is a request to allocate + the address. */ + info->auto_allocate = true; + } else if (virDevicePCIAddressParseXML(address, &info->addr.pci) < 0) { goto cleanup; + } break;
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE: diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index aba53a2..dd9d0b1 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -346,6 +346,7 @@ struct _virDomainDeviceInfo { */ char *alias; int type; /* virDomainDeviceAddressType */ + bool auto_allocate;
The name is generic enough to make me think it could work for other address types, but only PCI is supported.
union { virDevicePCIAddress pci; virDomainDeviceDriveAddress drive; diff --git a/tests/genericxml2xmlindata/generic-pci-autofill-addr.xml b/tests/genericxml2xmlindata/generic-pci-autofill-addr.xml new file mode 100644 index 0000000..06eadb6 --- /dev/null +++ b/tests/genericxml2xmlindata/generic-pci-autofill-addr.xml @@ -0,0 +1,27 @@ +<domain type='test'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219136</memory> + <currentMemory unit='KiB'>219136</currentMemory> + <vcpu placement='static'>1</vcpu> + <os> + <type arch='i686' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu</emulator> + <disk type='block' device='disk'> + <driver name='qemu' type='raw'/> + <source dev='/dev/HostVG/QEMUGuest1'/> + <target dev='vda' bus='virtio'/> + <address type='pci'/> + </disk> + <controller type='usb' index='0'> + <address type='pci'/> + </controller> + </devices> +</domain> diff --git a/tests/genericxml2xmltest.c b/tests/genericxml2xmltest.c index 666fc86..b329d10 100644 --- a/tests/genericxml2xmltest.c +++ b/tests/genericxml2xmltest.c @@ -21,6 +21,7 @@ struct testInfo { const char *name; int different; bool inactive_only; + testCompareDomXML2XMLFlags compare_flags; };
static int @@ -39,7 +40,7 @@ testCompareXMLToXMLHelper(const void *data)
ret = testCompareDomXML2XMLFiles(caps, xmlopt, xml_in, info->different ? xml_out : xml_in, - !info->inactive_only, 0, + !info->inactive_only, info->compare_flags, NULL, NULL, 0); cleanup: VIR_FREE(xml_in); @@ -59,21 +60,27 @@ mymain(void) if (!(xmlopt = virTestGenericDomainXMLConfInit())) return EXIT_FAILURE;
-#define DO_TEST_FULL(name, is_different, inactive) \ +#define DO_TEST_FULL(name, is_different, inactive, compare_flags) \ do { \ - const struct testInfo info = {name, is_different, inactive}; \ + const struct testInfo info = {name, is_different, \ + inactive, compare_flags}; \ if (virtTestRun("GENERIC XML-2-XML " name, \ testCompareXMLToXMLHelper, &info) < 0) \ ret = -1; \ } while (0)
#define DO_TEST(name) \ - DO_TEST_FULL(name, 0, false) + DO_TEST_FULL(name, 0, false, 0) + +#define DO_TEST_PARSE_ERROR(name) \ + DO_TEST_FULL(name, 0, false, \ + TEST_COMPARE_DOM_XML2XML_FLAG_EXPECT_PARSE_ERROR)
#define DO_TEST_DIFFERENT(name) \ - DO_TEST_FULL(name, 1, false) + DO_TEST_FULL(name, 1, false, 0)
DO_TEST_DIFFERENT("disk-virtio"); + DO_TEST_PARSE_ERROR("pci-autofill-addr");
virObjectUnref(caps); virObjectUnref(xmlopt);

On 03/21/2016 02:44 PM, John Ferlan wrote:
On 03/08/2016 11:36 AM, Cole Robinson wrote:
If a bare device <address type='pci'/> is specified, set an internal flag address->auto_allocate. Individual hv drivers can then check for this and act on it if they want, nothing is allocated in generic code.
If drivers allocate an address, they are expected to unset auto_allocate. Generic domain conf code then checks at XML format time to ensure no device addresses still have auto_allocate set; this ensures we aren't formatting any bogus address XML, and informing the user if their request didn't work. Add a genericxml2xml test case for this.
The auto_allocate property is a part of the generic address structure and not the PCI specific bits, this will make it easier to reuse with other address types too.
One note: we detect <address type='pci'/> by counting it's XML properties, rather than comparing specifically against parsed values, which seems easier to maintain. --- docs/schemas/domaincommon.rng | 5 +++- src/conf/domain_conf.c | 29 +++++++++++++++++++++- src/conf/domain_conf.h | 1 + .../generic-pci-autofill-addr.xml | 27 ++++++++++++++++++++ tests/genericxml2xmltest.c | 17 +++++++++---- 5 files changed, 72 insertions(+), 7 deletions(-) create mode 100644 tests/genericxml2xmlindata/generic-pci-autofill-addr.xml
Will also need to update 'formatdomain.html.in' to describe the new allowance of just "<address type='pci'>...
Darn I must have remembered and then forgot to update the docs like 10 times :/
Would this be useful if "multifunction='on'" without specifying an address? e.g.:
<address type='pci' multifunction='on'>
Maybe? Honestly I don't know much about when multifunction should be used.
Could other address types find the functionality useful? That is, I want address type 'drive' or 'usb', but I have no idea how to fill in the rest, I want the hv to do it for me.
My intent with how this was implemented is that it shouldn't take much change in generic domain_conf.c code to handle this for other address types: position of auto_allocate in the address structure, and the generic device iterator validating that all addresses were allocated.
diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index 6ca937c..d083250 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -4471,7 +4471,10 @@ <attribute name="type"> <value>pci</value> </attribute> - <ref name="pciaddress"/> + <choice> + <ref name="pciaddress"/> + <empty/> + </choice> </group> <group> <attribute name="type"> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index bc4e369..bbc42a4 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -3827,6 +3827,23 @@ virDomainDefPostParseTimer(virDomainDefPtr def) }
+ static int ^ Extraneous space
+virDomainCheckUnallocatedDeviceAddrs(virDomainDefPtr def ATTRIBUTE_UNUSED, + virDomainDeviceDefPtr dev, + virDomainDeviceInfoPtr info, + void *data ATTRIBUTE_UNUSED) +{ + if (!info->auto_allocate) + return 0; + + virReportError(VIR_ERR_INTERNAL_ERROR, + _("driver didn't allocate requested address type '%s' for device '%s'"), + virDomainDeviceAddressTypeToString(info->type), + virDomainDeviceTypeToString(dev->type)); + return -1; +} + + static int virDomainDefPostParseInternal(virDomainDefPtr def, virCapsPtr caps ATTRIBUTE_UNUSED, @@ -3851,6 +3868,11 @@ virDomainDefPostParseInternal(virDomainDefPtr def, if (virDomainDefPostParseTimer(def) < 0) return -1;
+ /* ensure the driver filled in any auto_allocate addrs */ + if (virDomainDeviceInfoIterate(def, virDomainCheckUnallocatedDeviceAddrs, + NULL) < 0) + return -1; +
Why couldn't this go in virDomainDefPostParseDeviceIterator?
That is, rather than have yet another iterator through the devices, call virDomainCheckUnallocatedDeviceAddrs right after or at the end of virDomainDeviceDefPostParse...
As the patches stand, it won't work with due to the ordering: this validation check needs to come after qemuDomainAssignAddresses. When the patches are reworked it may be able to be combined with DeviceDefPostParse
if (virDomainDefAddImplicitDevices(def) < 0) return -1;
@@ -4872,8 +4894,13 @@ virDomainDeviceInfoParseXML(xmlNodePtr node,
switch ((virDomainDeviceAddressType) info->type) { case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI: - if (virDevicePCIAddressParseXML(address, &info->addr.pci) < 0) + if (virXMLPropertyCount(address) == 1) {
Wish there was some other way than counting attributes, but I guess since "type" is an attribute we can hopefully guarantee that only "type=<something>" has been supplied.
+ /* Bare <address type='pci'/> is a request to allocate + the address. */ + info->auto_allocate = true; + } else if (virDevicePCIAddressParseXML(address, &info->addr.pci) < 0) { goto cleanup; + } break;
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE: diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index aba53a2..dd9d0b1 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -346,6 +346,7 @@ struct _virDomainDeviceInfo { */ char *alias; int type; /* virDomainDeviceAddressType */ + bool auto_allocate;
The name is generic enough to make me think it could work for other address types, but only PCI is supported.
Yup, that's what I was going for. - Cole

On 03/21/2016 02:59 PM, Cole Robinson wrote:
On 03/21/2016 02:44 PM, John Ferlan wrote:
Would this be useful if "multifunction='on'" without specifying an address? e.g.:
<address type='pci' multifunction='on'>
Maybe? Honestly I don't know much about when multifunction should be used.
I doubt that would ever be useful, since you need to match the multiple devices that are plugged into the slot by specifying a matching bus/slot, (the function# must always be 0 for the device that has "multifunction" option specified - it is supposed to be turned on for function 0 on any slot that has multiple functions in use. It seems to me that multifunction could have been left out of libvirt's XML completely, with 100% success of automatically turning it on at the correct time (since you *always* know ahead of time whether or not there will be multiple functions for a slot - hotplugging of multiple functions into a slot isn't supported, and in the eventuality that we do support it, it will only be supported by plugging in all the functions at once.) Several years too late for that now though :-P )

We do this in 2 passes: before PCI addresses are about to be collected, we convert type=pci auto_allocate=true to type=none auto_allocate=true, since the existing code is already expecting type=none here. After all PCI allocation should be complete, we do another pass of the device addresses converting type=pci auto_allocate=true to auto_allocate=false, so we don't trigger the unallocated address validation check in generic domain code. --- src/qemu/qemu_domain_address.c | 47 ++++++++++++++++++++++ .../qemuxml2argv-pci-autofill-addr.args | 24 +++++++++++ .../qemuxml2argv-pci-autofill-addr.xml | 44 ++++++++++++++++++++ tests/qemuxml2argvtest.c | 1 + .../qemuxml2xmlout-pci-autofill-addr.xml | 46 +++++++++++++++++++++ tests/qemuxml2xmltest.c | 1 + 6 files changed, 163 insertions(+) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-pci-autofill-addr.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-pci-autofill-addr.xml create mode 100644 tests/qemuxml2xmloutdata/qemuxml2xmlout-pci-autofill-addr.xml diff --git a/src/qemu/qemu_domain_address.c b/src/qemu/qemu_domain_address.c index eff33fc..74d13b6 100644 --- a/src/qemu/qemu_domain_address.c +++ b/src/qemu/qemu_domain_address.c @@ -1399,6 +1399,45 @@ qemuDomainSupportsPCI(virDomainDefPtr def, static int +qemuDomainPrepPCIAutoAllocate(virDomainDefPtr def ATTRIBUTE_UNUSED, + virDomainDeviceDefPtr device ATTRIBUTE_UNUSED, + virDomainDeviceInfoPtr info, + void *opaque ATTRIBUTE_UNUSED) +{ + /* If PCI auto_allocate requested, set type to NONE since the rest + of the code expects it. */ + if (info->auto_allocate && + info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) + info->type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE; + + return 0; +} + + +static int +qemuDomainFinishPCIAutoAllocate(virDomainDefPtr def ATTRIBUTE_UNUSED, + virDomainDeviceDefPtr device ATTRIBUTE_UNUSED, + virDomainDeviceInfoPtr info, + void *opaque ATTRIBUTE_UNUSED) +{ + /* A PCI device was allocated as requested, unset auto_allocate so + we don't trip the domain error about unallocated addresses */ + if (info->auto_allocate && + info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) + info->auto_allocate = false; + + /* We wanted to allocate a PCI address but it was never filled in... + this is likely an XML error. Re-set type=PCI to give a correct + error from domain conf */ + if (info->auto_allocate && + info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) + info->type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI; + + return 0; +} + + +static int qemuDomainAssignPCIAddresses(virDomainDefPtr def, virQEMUCapsPtr qemuCaps, virDomainObjPtr obj) @@ -1423,6 +1462,10 @@ qemuDomainAssignPCIAddresses(virDomainDefPtr def, } } + if (virDomainDeviceInfoIterate(def, qemuDomainPrepPCIAutoAllocate, + NULL) < 0) + goto cleanup; + nbuses = max_idx + 1; if (nbuses > 0 && @@ -1553,6 +1596,10 @@ qemuDomainAssignPCIAddresses(virDomainDefPtr def, } } } + + if (virDomainDeviceInfoIterate(def, qemuDomainFinishPCIAutoAllocate, + NULL) < 0) + goto cleanup; } if (obj && obj->privateData) { diff --git a/tests/qemuxml2argvdata/qemuxml2argv-pci-autofill-addr.args b/tests/qemuxml2argvdata/qemuxml2argv-pci-autofill-addr.args new file mode 100644 index 0000000..2ab305e --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-pci-autofill-addr.args @@ -0,0 +1,24 @@ +LC_ALL=C \ +PATH=/bin \ +HOME=/home/test \ +USER=test \ +LOGNAME=test \ +QEMU_AUDIO_DRV=none \ +/usr/libexec/qemu-kvm \ +-name fdr-br \ +-S \ +-M pc-1.2 \ +-m 2048 \ +-smp 2 \ +-uuid 3ec6cbe1-b5a2-4515-b800-31a61855df41 \ +-nographic \ +-nodefaults \ +-monitor unix:/tmp/lib/domain--1-fdr-br/monitor.sock,server,nowait \ +-boot c \ +-usb \ +-drive file=/var/iso/f18kde.iso,format=raw,if=none,media=cdrom,\ +id=drive-virtio-disk0 \ +-device virtio-blk-pci,bus=pci.0,addr=0x3,drive=drive-virtio-disk0,\ +id=virtio-disk0 \ +-vga cirrus \ +-device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x4 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-pci-autofill-addr.xml b/tests/qemuxml2argvdata/qemuxml2argv-pci-autofill-addr.xml new file mode 100644 index 0000000..8e7b6ab --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-pci-autofill-addr.xml @@ -0,0 +1,44 @@ +<domain type='qemu'> + <name>fdr-br</name> + <uuid>3ec6cbe1-b5a2-4515-b800-31a61855df41</uuid> + <memory unit='KiB'>2097152</memory> + <currentMemory unit='KiB'>2097152</currentMemory> + <vcpu placement='static' cpuset='0-1'>2</vcpu> + <os> + <type arch='x86_64' machine='pc-1.2'>hvm</type> + <boot dev='hd'/> + </os> + <features> + <acpi/> + <apic/> + <pae/> + </features> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>restart</on_crash> + <devices> + <emulator>/usr/libexec/qemu-kvm</emulator> + <disk type='file' device='cdrom'> + <driver name='qemu' type='raw'/> + <source file='/var/iso/f18kde.iso'/> + <target dev='vda' bus='virtio'/> + <readonly/> + <address type='pci'/> + </disk> + <controller type='usb' index='0'> + <address type='pci'/> + </controller> + <controller type='ide' index='0'> + <address type='pci'/> + </controller> + <input type='mouse' bus='ps2'/> + <video> + <model type='cirrus' vram='16384' heads='1'/> + <address type='pci'/> + </video> + <memballoon model='virtio'> + <address type='pci'/> + </memballoon> + </devices> +</domain> diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index aaec9de..79339c8 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -1524,6 +1524,7 @@ mymain(void) DO_TEST("pci-autoadd-addr", QEMU_CAPS_DEVICE_PCI_BRIDGE); DO_TEST("pci-autoadd-idx", QEMU_CAPS_DEVICE_PCI_BRIDGE); + DO_TEST("pci-autofill-addr", NONE); DO_TEST("pci-many", QEMU_CAPS_DEVICE_PCI_BRIDGE); DO_TEST("pci-bridge-many-disks", diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-pci-autofill-addr.xml b/tests/qemuxml2xmloutdata/qemuxml2xmlout-pci-autofill-addr.xml new file mode 100644 index 0000000..0f32c23 --- /dev/null +++ b/tests/qemuxml2xmloutdata/qemuxml2xmlout-pci-autofill-addr.xml @@ -0,0 +1,46 @@ +<domain type='qemu'> + <name>fdr-br</name> + <uuid>3ec6cbe1-b5a2-4515-b800-31a61855df41</uuid> + <memory unit='KiB'>2097152</memory> + <currentMemory unit='KiB'>2097152</currentMemory> + <vcpu placement='static' cpuset='0-1'>2</vcpu> + <os> + <type arch='x86_64' machine='pc-1.2'>hvm</type> + <boot dev='hd'/> + </os> + <features> + <acpi/> + <apic/> + <pae/> + </features> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>restart</on_crash> + <devices> + <emulator>/usr/libexec/qemu-kvm</emulator> + <disk type='file' device='cdrom'> + <driver name='qemu' type='raw'/> + <source file='/var/iso/f18kde.iso'/> + <target dev='vda' bus='virtio'/> + <readonly/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/> + </disk> + <controller type='usb' index='0'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/> + </controller> + <controller type='ide' index='0'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/> + </controller> + <controller type='pci' index='0' model='pci-root'/> + <input type='mouse' bus='ps2'/> + <input type='keyboard' bus='ps2'/> + <video> + <model type='cirrus' vram='16384' heads='1' primary='yes'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/> + </video> + <memballoon model='virtio'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/> + </memballoon> + </devices> +</domain> diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c index b3568df..d43ca13 100644 --- a/tests/qemuxml2xmltest.c +++ b/tests/qemuxml2xmltest.c @@ -580,6 +580,7 @@ mymain(void) QEMU_CAPS_DEVICE_PCI_BRIDGE); DO_TEST_FULL("pci-autoadd-idx", WHEN_ACTIVE, QEMU_CAPS_DEVICE_PCI_BRIDGE); + DO_TEST("pci-autofill-addr"); DO_TEST_FULL("q35", WHEN_ACTIVE, QEMU_CAPS_DEVICE_PCI_BRIDGE, -- 2.5.0

On 03/08/2016 11:36 AM, Cole Robinson wrote:
We do this in 2 passes: before PCI addresses are about to be collected, we convert type=pci auto_allocate=true to type=none auto_allocate=true, since the existing code is already expecting type=none here.
After all PCI allocation should be complete, we do another pass of the device addresses converting type=pci auto_allocate=true to auto_allocate=false, so we don't trigger the unallocated address validation check in generic domain code.
This sounds confusing. What about instead changing the existing code so that it checks for a valid PCI address instead of checking for type=none? A simple check for this is if domain == bus == slot == 0 (bus 0 is *always* either a pcie-root or a pci-root, and for both of those slot 0 is reserved, so once an address has been assigned, it will never be 0000:00:00.x .) So rather than doing this dance with type and auto_allocate, you can just modify the auto allocation to say: if (info->type == ...NONE || (info->type == ...PCI && !virDevicePCIAddressIsValid(&info->addr.pci)) { // Bob Loblaw } (virDevicePCIAddressIsValid() has some extra checks for values being too large, but does the essential check for domain/bus/slot == 0 as well)

On 03/23/2016 02:20 PM, Laine Stump wrote:
On 03/08/2016 11:36 AM, Cole Robinson wrote:
We do this in 2 passes: before PCI addresses are about to be collected, we convert type=pci auto_allocate=true to type=none auto_allocate=true, since the existing code is already expecting type=none here.
After all PCI allocation should be complete, we do another pass of the device addresses converting type=pci auto_allocate=true to auto_allocate=false, so we don't trigger the unallocated address validation check in generic domain code.
This sounds confusing. What about instead changing the existing code so that it checks for a valid PCI address instead of checking for type=none? A simple check for this is if domain == bus == slot == 0 (bus 0 is *always* either a pcie-root or a pci-root, and for both of those slot 0 is reserved, so once an address has been assigned, it will never be 0000:00:00.x .)
So rather than doing this dance with type and auto_allocate, you can just modify the auto allocation to say:
if (info->type == ...NONE || (info->type == ...PCI && !virDevicePCIAddressIsValid(&info->addr.pci)) {
// Bob Loblaw
}
(virDevicePCIAddressIsValid() has some extra checks for values being too large, but does the essential check for domain/bus/slot == 0 as well)
I'm refreshing these patches at the moment. I poked at this but it's a bit of a pain... Multiple places in the address assignment code assume 'if info->type == PCI, an address has been specified', like the code the scoops up all the already specified PCI addresses to check for collisions. Every place that makes that assumption now needs to be taught to check for auto_allocate and PCIDeviceAddressIsValid which seems fragile, since other checks may slip in that don't take those values into account. I'm reposting the patches using the old technique, but I'm happy to change things if we can come up with some more clear and sustainable alternative Thanks, Cole

This is an interesting test case since PCI isn't the default for aarch64. --- .../qemuxml2argv-aarch64-virtio-pci-manual-addresses.args | 4 +++- .../qemuxml2argv-aarch64-virtio-pci-manual-addresses.xml | 5 +++++ .../qemuxml2xmlout-aarch64-virtio-pci-manual-addresses.xml | 5 +++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/tests/qemuxml2argvdata/qemuxml2argv-aarch64-virtio-pci-manual-addresses.args b/tests/qemuxml2argvdata/qemuxml2argv-aarch64-virtio-pci-manual-addresses.args index e3e962b..f6a148c 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-aarch64-virtio-pci-manual-addresses.args +++ b/tests/qemuxml2argvdata/qemuxml2argv-aarch64-virtio-pci-manual-addresses.args @@ -29,4 +29,6 @@ QEMU_AUDIO_DRV=none \ -device scsi-disk,bus=scsi0.0,channel=0,scsi-id=0,lun=0,drive=drive-scsi0-0-0-0,\ id=scsi0-0-0-0 \ -device virtio-net-pci,vlan=0,id=net0,mac=52:54:00:09:a4:37,bus=pcie.0,addr=0x2 \ --net user,vlan=0,name=hostnet0 +-net user,vlan=0,name=hostnet0 \ +-device virtio-net-pci,vlan=1,id=net1,mac=52:54:00:09:a4:38,bus=pci.2,addr=0x1 \ +-net user,vlan=1,name=hostnet1 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-aarch64-virtio-pci-manual-addresses.xml b/tests/qemuxml2argvdata/qemuxml2argv-aarch64-virtio-pci-manual-addresses.xml index 6a44f19..bf0f249 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-aarch64-virtio-pci-manual-addresses.xml +++ b/tests/qemuxml2argvdata/qemuxml2argv-aarch64-virtio-pci-manual-addresses.xml @@ -39,5 +39,10 @@ <model type='virtio'/> <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/> </interface> + <interface type='user'> + <mac address='52:54:00:09:a4:38'/> + <model type='virtio'/> + <address type='pci'/> + </interface> </devices> </domain> diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-aarch64-virtio-pci-manual-addresses.xml b/tests/qemuxml2xmloutdata/qemuxml2xmlout-aarch64-virtio-pci-manual-addresses.xml index 0ee40f5..8a43d75 100644 --- a/tests/qemuxml2xmloutdata/qemuxml2xmlout-aarch64-virtio-pci-manual-addresses.xml +++ b/tests/qemuxml2xmloutdata/qemuxml2xmlout-aarch64-virtio-pci-manual-addresses.xml @@ -50,5 +50,10 @@ <model type='virtio'/> <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/> </interface> + <interface type='user'> + <mac address='52:54:00:09:a4:38'/> + <model type='virtio'/> + <address type='pci' domain='0x0000' bus='0x02' slot='0x01' function='0x0'/> + </interface> </devices> </domain> -- 2.5.0
participants (3)
-
Cole Robinson
-
John Ferlan
-
Laine Stump