[libvirt] [PATCH v2 00/20] Fix regression caused by recent CPU driver changes

https://bugzilla.redhat.com/show_bug.cgi?id=1441662 when I was enhancing libvirt's guest CPU configuration code to be able to really ensure stable guest CPU ABI, I added a new attribute //cpu/@check which is nicely backward compatible... an old libvirt will just ignore it. However, even if check='full' will be ignored, an old libvirt will still see the updated CPU definition (features added or removed by the hypervisor will be shown there). And because we need QEMU 2.9.0 to check what features are going to be added or removed before we actually start the domain, migrating such domain to an older libvirt or QEMU may fail if QEMU enables a feature which is not supported by the host CPU. Known features causing problems are, e.g., x2apic, hypervisor, and arat. To make things even worse, updating a CPU definition with the automatically added/removed features can be done since QEMU 1.5.0. Even save/restore or snapshot revert on a single host running new libvirt and QEMU < 2.9.0 is now affected by this regression. This series fixes the regression by storing the original guest CPU definition in migratable XML and sending the updated CPU in a side channel (a migration or save cookie). Version 2: - see individual patches for changes Jiri Denemark (20): conf: Make error reporting in virCPUDefIsEqual optional conf: Refactor virCPUDefParseXML conf: Make virDomainSnapshotDefFormat easier to read conf: Pass xmlopt to virDomainSnapshotDefFormat qemu: Rename xml_len in virQEMUSaveHeader as data_len qemu: Fix memory leaks in qemuDomainSaveImageOpen qemu: Introduce virQEMUSaveData{New,Free} qemu: Introduce virQEMUSaveDataFinish qemu: Refactor qemuDomainSaveHeader qemu: Introduce virQEMUSaveData structure conf: Introduce virSaveCookie conf: Add save cookie callbacks to xmlopt qemu: Implement virSaveCookie object and callbacks qemu: Store save cookie in save images and snapshots qemu: Remember CPU def from domain start qemu: Report the original CPU in migratable xml qemu: Always send persistent XML during migration qemu: Send updated CPU in migration cookie qemu: Store updated CPU in save cookie qemu: Use updated CPU when starting QEMU if possible docs/formatsnapshot.html.in | 6 + docs/schemas/domainsnapshot.rng | 7 + po/POTFILES.in | 1 + src/Makefile.am | 1 + src/bhyve/bhyve_domain.c | 2 +- src/conf/cpu_conf.c | 192 ++++++++-------- src/conf/cpu_conf.h | 12 +- src/conf/domain_conf.c | 30 ++- src/conf/domain_conf.h | 7 +- src/conf/snapshot_conf.c | 40 +++- src/conf/snapshot_conf.h | 3 + src/conf/virsavecookie.c | 144 ++++++++++++ src/conf/virsavecookie.h | 62 +++++ src/cpu/cpu.c | 5 +- src/esx/esx_driver.c | 2 +- src/libvirt_private.syms | 10 + src/libxl/libxl_conf.c | 2 +- src/lxc/lxc_conf.c | 2 +- src/openvz/openvz_driver.c | 2 +- src/phyp/phyp_driver.c | 2 +- src/qemu/qemu_capabilities.c | 2 +- src/qemu/qemu_conf.c | 3 +- src/qemu/qemu_domain.c | 207 +++++++++++++++-- src/qemu/qemu_domain.h | 22 ++ src/qemu/qemu_driver.c | 468 ++++++++++++++++++++++++++------------ src/qemu/qemu_migration.c | 18 +- src/qemu/qemu_migration_cookie.c | 31 ++- src/qemu/qemu_migration_cookie.h | 5 + src/qemu/qemu_process.c | 37 ++- src/qemu/qemu_process.h | 2 + src/security/virt-aa-helper.c | 2 +- src/test/test_driver.c | 3 +- src/uml/uml_driver.c | 2 +- src/vbox/vbox_common.c | 6 +- src/vmware/vmware_driver.c | 3 +- src/vmx/vmx.c | 2 +- src/vz/vz_driver.c | 3 +- src/xen/xen_driver.c | 2 +- src/xenapi/xenapi_driver.c | 2 +- tests/bhyveargv2xmltest.c | 2 +- tests/cputest.c | 5 +- tests/domainsnapshotxml2xmltest.c | 1 + tests/testutils.c | 2 +- 43 files changed, 1047 insertions(+), 315 deletions(-) create mode 100644 src/conf/virsavecookie.c create mode 100644 src/conf/virsavecookie.h -- 2.13.0

The function will be used in paths where mismatching CPU defs are not an error. Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- Notes: Version 2: - move all the if (reportError) statements into a macro src/conf/cpu_conf.c | 83 +++++++++++++++++++++++--------------------------- src/conf/cpu_conf.h | 3 +- src/conf/domain_conf.c | 2 +- 3 files changed, 41 insertions(+), 47 deletions(-) diff --git a/src/conf/cpu_conf.c b/src/conf/cpu_conf.c index a4be5742e..ffb2e83d6 100644 --- a/src/conf/cpu_conf.c +++ b/src/conf/cpu_conf.c @@ -811,7 +811,8 @@ virCPUDefAddFeature(virCPUDefPtr def, bool virCPUDefIsEqual(virCPUDefPtr src, - virCPUDefPtr dst) + virCPUDefPtr dst, + bool reportError) { bool identical = false; size_t i; @@ -819,98 +820,89 @@ virCPUDefIsEqual(virCPUDefPtr src, if (!src && !dst) return true; +#define MISMATCH(fmt, ...) \ + if (reportError) \ + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, fmt, __VA_ARGS__) + if ((src && !dst) || (!src && dst)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("Target CPU does not match source")); + MISMATCH("%s", _("Target CPU does not match source")); goto cleanup; } if (src->type != dst->type) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("Target CPU type %s does not match source %s"), - virCPUTypeToString(dst->type), - virCPUTypeToString(src->type)); + MISMATCH(_("Target CPU type %s does not match source %s"), + virCPUTypeToString(dst->type), + virCPUTypeToString(src->type)); goto cleanup; } if (src->mode != dst->mode) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("Target CPU mode %s does not match source %s"), - virCPUModeTypeToString(dst->mode), - virCPUModeTypeToString(src->mode)); + MISMATCH(_("Target CPU mode %s does not match source %s"), + virCPUModeTypeToString(dst->mode), + virCPUModeTypeToString(src->mode)); goto cleanup; } if (src->arch != dst->arch) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("Target CPU arch %s does not match source %s"), - virArchToString(dst->arch), - virArchToString(src->arch)); + MISMATCH(_("Target CPU arch %s does not match source %s"), + virArchToString(dst->arch), + virArchToString(src->arch)); goto cleanup; } if (STRNEQ_NULLABLE(src->model, dst->model)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("Target CPU model %s does not match source %s"), - NULLSTR(dst->model), NULLSTR(src->model)); + MISMATCH(_("Target CPU model %s does not match source %s"), + NULLSTR(dst->model), NULLSTR(src->model)); goto cleanup; } if (STRNEQ_NULLABLE(src->vendor, dst->vendor)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("Target CPU vendor %s does not match source %s"), - NULLSTR(dst->vendor), NULLSTR(src->vendor)); + MISMATCH(_("Target CPU vendor %s does not match source %s"), + NULLSTR(dst->vendor), NULLSTR(src->vendor)); goto cleanup; } if (STRNEQ_NULLABLE(src->vendor_id, dst->vendor_id)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("Target CPU vendor id %s does not match source %s"), - NULLSTR(dst->vendor_id), NULLSTR(src->vendor_id)); + MISMATCH(_("Target CPU vendor id %s does not match source %s"), + NULLSTR(dst->vendor_id), NULLSTR(src->vendor_id)); goto cleanup; } if (src->sockets != dst->sockets) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("Target CPU sockets %d does not match source %d"), - dst->sockets, src->sockets); + MISMATCH(_("Target CPU sockets %d does not match source %d"), + dst->sockets, src->sockets); goto cleanup; } if (src->cores != dst->cores) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("Target CPU cores %d does not match source %d"), - dst->cores, src->cores); + MISMATCH(_("Target CPU cores %d does not match source %d"), + dst->cores, src->cores); goto cleanup; } if (src->threads != dst->threads) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("Target CPU threads %d does not match source %d"), - dst->threads, src->threads); + MISMATCH(_("Target CPU threads %d does not match source %d"), + dst->threads, src->threads); goto cleanup; } if (src->nfeatures != dst->nfeatures) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("Target CPU feature count %zu does not match source %zu"), - dst->nfeatures, src->nfeatures); + MISMATCH(_("Target CPU feature count %zu does not match source %zu"), + dst->nfeatures, src->nfeatures); goto cleanup; } for (i = 0; i < src->nfeatures; i++) { if (STRNEQ(src->features[i].name, dst->features[i].name)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("Target CPU feature %s does not match source %s"), - dst->features[i].name, src->features[i].name); + MISMATCH(_("Target CPU feature %s does not match source %s"), + dst->features[i].name, src->features[i].name); goto cleanup; } if (src->features[i].policy != dst->features[i].policy) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("Target CPU feature policy %s does not match source %s"), - virCPUFeaturePolicyTypeToString(dst->features[i].policy), - virCPUFeaturePolicyTypeToString(src->features[i].policy)); + MISMATCH(_("Target CPU feature policy %s does not match source %s"), + virCPUFeaturePolicyTypeToString(dst->features[i].policy), + virCPUFeaturePolicyTypeToString(src->features[i].policy)); goto cleanup; } } @@ -920,11 +912,12 @@ virCPUDefIsEqual(virCPUDefPtr src, (src->cache && dst->cache && (src->cache->level != dst->cache->level || src->cache->mode != dst->cache->mode))) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("Target CPU cache does not match source")); + MISMATCH("%s", _("Target CPU cache does not match source")); goto cleanup; } +#undef MISMATCH + identical = true; cleanup: diff --git a/src/conf/cpu_conf.h b/src/conf/cpu_conf.h index 09438b68b..b0d891552 100644 --- a/src/conf/cpu_conf.h +++ b/src/conf/cpu_conf.h @@ -189,7 +189,8 @@ virCPUDefParseXML(xmlNodePtr node, bool virCPUDefIsEqual(virCPUDefPtr src, - virCPUDefPtr dst); + virCPUDefPtr dst, + bool reportError); char * virCPUDefFormat(virCPUDefPtr def, diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 958a5b7cd..5f99dad9c 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -20090,7 +20090,7 @@ virDomainDefCheckABIStabilityFlags(virDomainDefPtr src, goto error; } - if (!virCPUDefIsEqual(src->cpu, dst->cpu)) + if (!virCPUDefIsEqual(src->cpu, dst->cpu, true)) goto error; if (!virSysinfoIsEqual(src->sysinfo, dst->sysinfo)) -- 2.13.0

On Wed, Jun 07, 2017 at 10:37:26AM +0200, Jiri Denemark wrote:
The function will be used in paths where mismatching CPU defs are not an error.
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> ---
Notes: Version 2: - move all the if (reportError) statements into a macro
src/conf/cpu_conf.c | 83 +++++++++++++++++++++++--------------------------- src/conf/cpu_conf.h | 3 +- src/conf/domain_conf.c | 2 +- 3 files changed, 41 insertions(+), 47 deletions(-)
Reviewed-by: Pavel Hrdina <phrdina@redhat.com>

Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- Notes: Version 2: - no need to distinguish 0 vs 1 return codes - better documentation src/conf/cpu_conf.c | 109 +++++++++++++++++++++++++++---------------------- src/conf/cpu_conf.h | 9 ++-- src/conf/domain_conf.c | 12 +----- src/cpu/cpu.c | 5 +-- tests/cputest.c | 5 +-- 5 files changed, 72 insertions(+), 68 deletions(-) diff --git a/src/conf/cpu_conf.c b/src/conf/cpu_conf.c index ffb2e83d6..ae9f52c8d 100644 --- a/src/conf/cpu_conf.c +++ b/src/conf/cpu_conf.c @@ -245,12 +245,25 @@ virCPUDefCopy(const virCPUDef *cpu) } -virCPUDefPtr -virCPUDefParseXML(xmlNodePtr node, - xmlXPathContextPtr ctxt, - virCPUType type) +/* + * Parses CPU definition XML from a node pointed to by @xpath. If @xpath is + * NULL, the current node of @ctxt is used (i.e., it is a shortcut to "."). + * + * Missing <cpu> element in the XML document is not considered an error unless + * @xpath is NULL in which case the function expects it was provided with a + * valid <cpu> element already. In other words, the function returns success + * and sets @cpu to NULL if @xpath is not NULL and the node pointed to by + * @xpath is not found. + * + * Returns 0 on success, -1 on error. + */ +int +virCPUDefParseXML(xmlXPathContextPtr ctxt, + const char *xpath, + virCPUType type, + virCPUDefPtr *cpu) { - virCPUDefPtr def; + virCPUDefPtr def = NULL; xmlNodePtr *nodes = NULL; xmlNodePtr oldnode = ctxt->node; int n; @@ -258,15 +271,23 @@ virCPUDefParseXML(xmlNodePtr node, char *cpuMode; char *fallback = NULL; char *vendor_id = NULL; + int ret = -1; - if (!xmlStrEqual(node->name, BAD_CAST "cpu")) { + *cpu = NULL; + + if (xpath && !(ctxt->node = virXPathNode(xpath, ctxt))) { + ret = 0; + goto cleanup; + } + + if (!xmlStrEqual(ctxt->node->name, BAD_CAST "cpu")) { virReportError(VIR_ERR_XML_ERROR, "%s", _("XML does not contain expected 'cpu' element")); - return NULL; + goto cleanup; } if (VIR_ALLOC(def) < 0) - return NULL; + goto cleanup; if (type == VIR_CPU_TYPE_AUTO) { if (virXPathBoolean("boolean(./arch)", ctxt)) { @@ -274,7 +295,7 @@ virCPUDefParseXML(xmlNodePtr node, virReportError(VIR_ERR_XML_ERROR, "%s", _("'arch' element cannot be used inside 'cpu'" " element with 'match' attribute'")); - goto error; + goto cleanup; } def->type = VIR_CPU_TYPE_HOST; } else { @@ -284,12 +305,12 @@ virCPUDefParseXML(xmlNodePtr node, def->type = type; } - if ((cpuMode = virXMLPropString(node, "mode"))) { + if ((cpuMode = virXMLPropString(ctxt->node, "mode"))) { if (def->type == VIR_CPU_TYPE_HOST) { VIR_FREE(cpuMode); virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Attribute mode is only allowed for guest CPU")); - goto error; + goto cleanup; } else { def->mode = virCPUModeTypeFromString(cpuMode); @@ -298,7 +319,7 @@ virCPUDefParseXML(xmlNodePtr node, _("Invalid mode attribute '%s'"), cpuMode); VIR_FREE(cpuMode); - goto error; + goto cleanup; } VIR_FREE(cpuMode); } @@ -310,7 +331,7 @@ virCPUDefParseXML(xmlNodePtr node, } if (def->type == VIR_CPU_TYPE_GUEST) { - char *match = virXMLPropString(node, "match"); + char *match = virXMLPropString(ctxt->node, "match"); char *check; if (!match) { @@ -326,11 +347,11 @@ virCPUDefParseXML(xmlNodePtr node, virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Invalid match attribute for CPU " "specification")); - goto error; + goto cleanup; } } - if ((check = virXMLPropString(node, "check"))) { + if ((check = virXMLPropString(ctxt->node, "check"))) { int value = virCPUCheckTypeFromString(check); VIR_FREE(check); @@ -338,7 +359,7 @@ virCPUDefParseXML(xmlNodePtr node, virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Invalid check attribute for CPU " "specification")); - goto error; + goto cleanup; } def->check = value; } @@ -349,13 +370,13 @@ virCPUDefParseXML(xmlNodePtr node, if (!arch) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Missing CPU architecture")); - goto error; + goto cleanup; } if ((def->arch = virArchFromString(arch)) == VIR_ARCH_NONE) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Unknown architecture %s"), arch); VIR_FREE(arch); - goto error; + goto cleanup; } VIR_FREE(arch); } @@ -364,7 +385,7 @@ virCPUDefParseXML(xmlNodePtr node, def->type == VIR_CPU_TYPE_HOST) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Missing CPU model name")); - goto error; + goto cleanup; } if (def->type == VIR_CPU_TYPE_GUEST && @@ -374,7 +395,7 @@ virCPUDefParseXML(xmlNodePtr node, if ((def->fallback = virCPUFallbackTypeFromString(fallback)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Invalid fallback attribute")); - goto error; + goto cleanup; } } @@ -384,14 +405,14 @@ virCPUDefParseXML(xmlNodePtr node, virReportError(VIR_ERR_XML_ERROR, _("vendor_id must be exactly %d characters long"), VIR_CPU_VENDOR_ID_LENGTH); - goto error; + goto cleanup; } /* ensure that the string can be passed to qemu*/ if (strchr(vendor_id, ',')) { virReportError(VIR_ERR_XML_ERROR, "%s", _("vendor id is invalid")); - goto error; + goto cleanup; } def->vendor_id = vendor_id; @@ -403,61 +424,54 @@ virCPUDefParseXML(xmlNodePtr node, if (def->vendor && !def->model) { virReportError(VIR_ERR_XML_ERROR, "%s", _("CPU vendor specified without CPU model")); - goto error; + goto cleanup; } if (virXPathNode("./topology[1]", ctxt)) { - int ret; unsigned long ul; - ret = virXPathULong("string(./topology[1]/@sockets)", - ctxt, &ul); - if (ret < 0) { + if (virXPathULong("string(./topology[1]/@sockets)", ctxt, &ul) < 0) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Missing 'sockets' attribute in CPU topology")); - goto error; + goto cleanup; } def->sockets = (unsigned int) ul; - ret = virXPathULong("string(./topology[1]/@cores)", - ctxt, &ul); - if (ret < 0) { + if (virXPathULong("string(./topology[1]/@cores)", ctxt, &ul) < 0) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Missing 'cores' attribute in CPU topology")); - goto error; + goto cleanup; } def->cores = (unsigned int) ul; - ret = virXPathULong("string(./topology[1]/@threads)", - ctxt, &ul); - if (ret < 0) { + if (virXPathULong("string(./topology[1]/@threads)", ctxt, &ul) < 0) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Missing 'threads' attribute in CPU topology")); - goto error; + goto cleanup; } def->threads = (unsigned int) ul; if (!def->sockets || !def->cores || !def->threads) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Invalid CPU topology")); - goto error; + goto cleanup; } } if ((n = virXPathNodeSet("./feature", ctxt, &nodes)) < 0) - goto error; + goto cleanup; if (n > 0) { if (!def->model && def->mode == VIR_CPU_MODE_CUSTOM) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Non-empty feature list specified without " "CPU model")); - goto error; + goto cleanup; } if (VIR_RESIZE_N(def->features, def->nfeatures_max, def->nfeatures, n) < 0) - goto error; + goto cleanup; def->nfeatures = n; } @@ -480,7 +494,7 @@ virCPUDefParseXML(xmlNodePtr node, if (policy < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Invalid CPU feature policy")); - goto error; + goto cleanup; } } else { policy = -1; @@ -490,7 +504,7 @@ virCPUDefParseXML(xmlNodePtr node, VIR_FREE(name); virReportError(VIR_ERR_XML_ERROR, "%s", _("Invalid CPU feature name")); - goto error; + goto cleanup; } for (j = 0; j < i; j++) { @@ -499,7 +513,7 @@ virCPUDefParseXML(xmlNodePtr node, _("CPU feature '%s' specified more than once"), name); VIR_FREE(name); - goto error; + goto cleanup; } } @@ -542,17 +556,16 @@ virCPUDefParseXML(xmlNodePtr node, def->cache->mode = mode; } + VIR_STEAL_PTR(*cpu, def); + ret = 0; + cleanup: ctxt->node = oldnode; VIR_FREE(fallback); VIR_FREE(vendor_id); VIR_FREE(nodes); - return def; - - error: virCPUDefFree(def); - def = NULL; - goto cleanup; + return ret; } diff --git a/src/conf/cpu_conf.h b/src/conf/cpu_conf.h index b0d891552..b44974f47 100644 --- a/src/conf/cpu_conf.h +++ b/src/conf/cpu_conf.h @@ -182,10 +182,11 @@ virCPUDefCopy(const virCPUDef *cpu); virCPUDefPtr virCPUDefCopyWithoutModel(const virCPUDef *cpu); -virCPUDefPtr -virCPUDefParseXML(xmlNodePtr node, - xmlXPathContextPtr ctxt, - virCPUType mode); +int +virCPUDefParseXML(xmlXPathContextPtr ctxt, + const char *xpath, + virCPUType mode, + virCPUDefPtr *cpu); bool virCPUDefIsEqual(virCPUDefPtr src, diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 5f99dad9c..53c145f16 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -17444,16 +17444,8 @@ virDomainDefParseXML(xmlDocPtr xml, } VIR_FREE(nodes); - /* analysis of cpu handling */ - if ((node = virXPathNode("./cpu[1]", ctxt)) != NULL) { - xmlNodePtr oldnode = ctxt->node; - ctxt->node = node; - def->cpu = virCPUDefParseXML(node, ctxt, VIR_CPU_TYPE_GUEST); - ctxt->node = oldnode; - - if (def->cpu == NULL) - goto error; - } + if (virCPUDefParseXML(ctxt, "./cpu[1]", VIR_CPU_TYPE_GUEST, &def->cpu) < 0) + goto error; if (virDomainNumaDefCPUParseXML(def->numa, ctxt) < 0) goto error; diff --git a/src/cpu/cpu.c b/src/cpu/cpu.c index 702b14dbb..96160901e 100644 --- a/src/cpu/cpu.c +++ b/src/cpu/cpu.c @@ -130,7 +130,7 @@ virCPUCompareXML(virArch arch, if (!(doc = virXMLParseStringCtxt(xml, _("(CPU_definition)"), &ctxt))) goto cleanup; - if (!(cpu = virCPUDefParseXML(ctxt->node, ctxt, VIR_CPU_TYPE_AUTO))) + if (virCPUDefParseXML(ctxt, NULL, VIR_CPU_TYPE_AUTO, &cpu) < 0) goto cleanup; ret = virCPUCompare(arch, host, cpu, failIncompatible); @@ -562,8 +562,7 @@ cpuBaselineXML(const char **xmlCPUs, if (!(doc = virXMLParseStringCtxt(xmlCPUs[i], _("(CPU_definition)"), &ctxt))) goto error; - cpus[i] = virCPUDefParseXML(ctxt->node, ctxt, VIR_CPU_TYPE_HOST); - if (cpus[i] == NULL) + if (virCPUDefParseXML(ctxt, NULL, VIR_CPU_TYPE_HOST, &cpus[i]) < 0) goto error; xmlXPathFreeContext(ctxt); diff --git a/tests/cputest.c b/tests/cputest.c index d5e023c40..89c645e64 100644 --- a/tests/cputest.c +++ b/tests/cputest.c @@ -88,7 +88,7 @@ cpuTestLoadXML(virArch arch, const char *name) if (!(doc = virXMLParseFileCtxt(xml, &ctxt))) goto cleanup; - cpu = virCPUDefParseXML(ctxt->node, ctxt, VIR_CPU_TYPE_AUTO); + virCPUDefParseXML(ctxt, NULL, VIR_CPU_TYPE_AUTO, &cpu); cleanup: xmlXPathFreeContext(ctxt); @@ -126,8 +126,7 @@ cpuTestLoadMultiXML(virArch arch, for (i = 0; i < n; i++) { ctxt->node = nodes[i]; - cpus[i] = virCPUDefParseXML(nodes[i], ctxt, VIR_CPU_TYPE_HOST); - if (!cpus[i]) + if (virCPUDefParseXML(ctxt, NULL, VIR_CPU_TYPE_HOST, &cpus[i]) < 0) goto cleanup_cpus; } -- 2.13.0

On Wed, Jun 07, 2017 at 10:37:27AM +0200, Jiri Denemark wrote:
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> ---
Notes: Version 2: - no need to distinguish 0 vs 1 return codes - better documentation
src/conf/cpu_conf.c | 109 +++++++++++++++++++++++++++---------------------- src/conf/cpu_conf.h | 9 ++-- src/conf/domain_conf.c | 12 +----- src/cpu/cpu.c | 5 +-- tests/cputest.c | 5 +-- 5 files changed, 72 insertions(+), 68 deletions(-)
diff --git a/src/conf/cpu_conf.c b/src/conf/cpu_conf.c index ffb2e83d6..ae9f52c8d 100644 --- a/src/conf/cpu_conf.c +++ b/src/conf/cpu_conf.c @@ -245,12 +245,25 @@ virCPUDefCopy(const virCPUDef *cpu) }
-virCPUDefPtr -virCPUDefParseXML(xmlNodePtr node, - xmlXPathContextPtr ctxt, - virCPUType type) +/* + * Parses CPU definition XML from a node pointed to by @xpath. If @xpath is + * NULL, the current node of @ctxt is used (i.e., it is a shortcut to "."). + * + * Missing <cpu> element in the XML document is not considered an error unless + * @xpath is NULL in which case the function expects it was provided with a + * valid <cpu> element already. In other words, the function returns success + * and sets @cpu to NULL if @xpath is not NULL and the node pointed to by + * @xpath is not found. + * + * Returns 0 on success, -1 on error.
Extra space or missing colon. Reviewed-by: Pavel Hrdina <phrdina@redhat.com>

Signed-off-by: Jiri Denemark <jdenemar@redhat.com> Reviewed-by: Pavel Hrdina <phrdina@redhat.com> --- Notes: Version 2: - no change src/conf/snapshot_conf.c | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/src/conf/snapshot_conf.c b/src/conf/snapshot_conf.c index b6cba5ac3..7daa9b22a 100644 --- a/src/conf/snapshot_conf.c +++ b/src/conf/snapshot_conf.c @@ -686,11 +686,13 @@ virDomainSnapshotDiskDefFormat(virBufferPtr buf, virBufferAddLit(buf, "</disk>\n"); } -char *virDomainSnapshotDefFormat(const char *domain_uuid, - virDomainSnapshotDefPtr def, - virCapsPtr caps, - unsigned int flags, - int internal) + +char * +virDomainSnapshotDefFormat(const char *domain_uuid, + virDomainSnapshotDefPtr def, + virCapsPtr caps, + unsigned int flags, + int internal) { virBuffer buf = VIR_BUFFER_INITIALIZER; size_t i; @@ -702,12 +704,14 @@ char *virDomainSnapshotDefFormat(const char *domain_uuid, virBufferAddLit(&buf, "<domainsnapshot>\n"); virBufferAdjustIndent(&buf, 2); + virBufferEscapeString(&buf, "<name>%s</name>\n", def->name); if (def->description) virBufferEscapeString(&buf, "<description>%s</description>\n", def->description); virBufferAsprintf(&buf, "<state>%s</state>\n", virDomainSnapshotStateTypeToString(def->state)); + if (def->parent) { virBufferAddLit(&buf, "<parent>\n"); virBufferAdjustIndent(&buf, 2); @@ -715,14 +719,17 @@ char *virDomainSnapshotDefFormat(const char *domain_uuid, virBufferAdjustIndent(&buf, -2); virBufferAddLit(&buf, "</parent>\n"); } + virBufferAsprintf(&buf, "<creationTime>%lld</creationTime>\n", def->creationTime); + if (def->memory) { virBufferAsprintf(&buf, "<memory snapshot='%s'", virDomainSnapshotLocationTypeToString(def->memory)); virBufferEscapeString(&buf, " file='%s'", def->file); virBufferAddLit(&buf, "/>\n"); } + if (def->ndisks) { virBufferAddLit(&buf, "<disks>\n"); virBufferAdjustIndent(&buf, 2); @@ -731,11 +738,10 @@ char *virDomainSnapshotDefFormat(const char *domain_uuid, virBufferAdjustIndent(&buf, -2); virBufferAddLit(&buf, "</disks>\n"); } + if (def->dom) { - if (virDomainDefFormatInternal(def->dom, caps, flags, &buf) < 0) { - virBufferFreeAndReset(&buf); - return NULL; - } + if (virDomainDefFormatInternal(def->dom, caps, flags, &buf) < 0) + goto error; } else if (domain_uuid) { virBufferAddLit(&buf, "<domain>\n"); virBufferAdjustIndent(&buf, 2); @@ -743,8 +749,10 @@ char *virDomainSnapshotDefFormat(const char *domain_uuid, virBufferAdjustIndent(&buf, -2); virBufferAddLit(&buf, "</domain>\n"); } + if (internal) virBufferAsprintf(&buf, "<active>%d</active>\n", def->current); + virBufferAdjustIndent(&buf, -2); virBufferAddLit(&buf, "</domainsnapshot>\n"); @@ -752,6 +760,10 @@ char *virDomainSnapshotDefFormat(const char *domain_uuid, return NULL; return virBufferContentAndReset(&buf); + + error: + virBufferFreeAndReset(&buf); + return NULL; } /* Snapshot Obj functions */ -- 2.13.0

This will be used later when a save cookie will become part of the snapshot XML using new driver specific parser/formatter functions. Signed-off-by: Jiri Denemark <jdenemar@redhat.com> Reviewed-by: Pavel Hrdina <phrdina@redhat.com> --- Notes: I know the line in vbox_common.c is too long, but I don't really want to touch that horrible piece of code more than making it compile. Version 2: - enhanced commit message src/conf/snapshot_conf.c | 1 + src/conf/snapshot_conf.h | 1 + src/esx/esx_driver.c | 2 +- src/qemu/qemu_domain.c | 4 +++- src/qemu/qemu_domain.h | 1 + src/qemu/qemu_driver.c | 16 ++++++++++++---- src/test/test_driver.c | 1 + src/vbox/vbox_common.c | 4 ++-- src/vz/vz_driver.c | 1 + tests/domainsnapshotxml2xmltest.c | 1 + 10 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/conf/snapshot_conf.c b/src/conf/snapshot_conf.c index 7daa9b22a..e3bba985d 100644 --- a/src/conf/snapshot_conf.c +++ b/src/conf/snapshot_conf.c @@ -691,6 +691,7 @@ char * virDomainSnapshotDefFormat(const char *domain_uuid, virDomainSnapshotDefPtr def, virCapsPtr caps, + virDomainXMLOptionPtr xmlopt ATTRIBUTE_UNUSED, unsigned int flags, int internal) { diff --git a/src/conf/snapshot_conf.h b/src/conf/snapshot_conf.h index da904f946..2ce526fa6 100644 --- a/src/conf/snapshot_conf.h +++ b/src/conf/snapshot_conf.h @@ -114,6 +114,7 @@ void virDomainSnapshotDefFree(virDomainSnapshotDefPtr def); char *virDomainSnapshotDefFormat(const char *domain_uuid, virDomainSnapshotDefPtr def, virCapsPtr caps, + virDomainXMLOptionPtr xmlopt, unsigned int flags, int internal); int virDomainSnapshotAlignDisks(virDomainSnapshotDefPtr snapshot, diff --git a/src/esx/esx_driver.c b/src/esx/esx_driver.c index 6333855c7..1f4f2c7a7 100644 --- a/src/esx/esx_driver.c +++ b/src/esx/esx_driver.c @@ -4265,7 +4265,7 @@ esxDomainSnapshotGetXMLDesc(virDomainSnapshotPtr snapshot, virUUIDFormat(snapshot->domain->uuid, uuid_string); - xml = virDomainSnapshotDefFormat(uuid_string, &def, priv->caps, + xml = virDomainSnapshotDefFormat(uuid_string, &def, priv->caps, priv->xmlopt, virDomainDefFormatConvertXMLFlags(flags), 0); diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 698632489..814232164 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -4820,6 +4820,7 @@ int qemuDomainSnapshotWriteMetadata(virDomainObjPtr vm, virDomainSnapshotObjPtr snapshot, virCapsPtr caps, + virDomainXMLOptionPtr xmlopt, char *snapshotDir) { char *newxml = NULL; @@ -4830,7 +4831,7 @@ qemuDomainSnapshotWriteMetadata(virDomainObjPtr vm, virUUIDFormat(vm->def->uuid, uuidstr); newxml = virDomainSnapshotDefFormat( - uuidstr, snapshot->def, caps, + uuidstr, snapshot->def, caps, xmlopt, virDomainDefFormatConvertXMLFlags(QEMU_DOMAIN_FORMAT_LIVE_FLAGS), 1); if (newxml == NULL) @@ -4991,6 +4992,7 @@ qemuDomainSnapshotDiscard(virQEMUDriverPtr driver, } else { parentsnap->def->current = true; if (qemuDomainSnapshotWriteMetadata(vm, parentsnap, driver->caps, + driver->xmlopt, cfg->snapshotDir) < 0) { VIR_WARN("failed to set parent snapshot '%s' as current", snap->def->parent); diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index 829f7746e..d0e2e0628 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -555,6 +555,7 @@ const char *qemuFindQemuImgBinary(virQEMUDriverPtr driver); int qemuDomainSnapshotWriteMetadata(virDomainObjPtr vm, virDomainSnapshotObjPtr snapshot, virCapsPtr caps, + virDomainXMLOptionPtr xmlopt, char *snapshotDir); int qemuDomainSnapshotForEachQcow2(virQEMUDriverPtr driver, diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index f0cdea659..f97d41030 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -14649,7 +14649,7 @@ qemuDomainSnapshotCreateXML(virDomainPtr domain, if (update_current) { vm->current_snapshot->def->current = false; if (qemuDomainSnapshotWriteMetadata(vm, vm->current_snapshot, - driver->caps, + driver->caps, driver->xmlopt, cfg->snapshotDir) < 0) goto endjob; vm->current_snapshot = NULL; @@ -14699,6 +14699,7 @@ qemuDomainSnapshotCreateXML(virDomainPtr domain, endjob: if (snapshot && !(flags & VIR_DOMAIN_SNAPSHOT_CREATE_NO_METADATA)) { if (qemuDomainSnapshotWriteMetadata(vm, snap, driver->caps, + driver->xmlopt, cfg->snapshotDir) < 0) { /* if writing of metadata fails, error out rather than trying * to silently carry on without completing the snapshot */ @@ -15036,7 +15037,8 @@ qemuDomainSnapshotGetXMLDesc(virDomainSnapshotPtr snapshot, virUUIDFormat(snapshot->domain->uuid, uuidstr); - xml = virDomainSnapshotDefFormat(uuidstr, snap->def, driver->caps, + xml = virDomainSnapshotDefFormat(uuidstr, snap->def, + driver->caps, driver->xmlopt, virDomainDefFormatConvertXMLFlags(flags), 0); @@ -15218,7 +15220,8 @@ qemuDomainRevertToSnapshot(virDomainSnapshotPtr snapshot, if (vm->current_snapshot) { vm->current_snapshot->def->current = false; if (qemuDomainSnapshotWriteMetadata(vm, vm->current_snapshot, - driver->caps, cfg->snapshotDir) < 0) + driver->caps, driver->xmlopt, + cfg->snapshotDir) < 0) goto endjob; vm->current_snapshot = NULL; /* XXX Should we restore vm->current_snapshot after this point @@ -15458,6 +15461,7 @@ qemuDomainRevertToSnapshot(virDomainSnapshotPtr snapshot, cleanup: if (ret == 0) { if (qemuDomainSnapshotWriteMetadata(vm, snap, driver->caps, + driver->xmlopt, cfg->snapshotDir) < 0) ret = -1; else @@ -15494,6 +15498,7 @@ struct _virQEMUSnapReparent { virDomainSnapshotObjPtr parent; virDomainObjPtr vm; virCapsPtr caps; + virDomainXMLOptionPtr xmlopt; int err; virDomainSnapshotObjPtr last; }; @@ -15522,7 +15527,8 @@ qemuDomainSnapshotReparentChildren(void *payload, if (!snap->sibling) rep->last = snap; - rep->err = qemuDomainSnapshotWriteMetadata(rep->vm, snap, rep->caps, + rep->err = qemuDomainSnapshotWriteMetadata(rep->vm, snap, + rep->caps, rep->xmlopt, rep->cfg->snapshotDir); return 0; } @@ -15593,6 +15599,7 @@ qemuDomainSnapshotDelete(virDomainSnapshotPtr snapshot, if (flags & VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN_ONLY) { snap->def->current = true; if (qemuDomainSnapshotWriteMetadata(vm, snap, driver->caps, + driver->xmlopt, cfg->snapshotDir) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("failed to set snapshot '%s' as current"), @@ -15610,6 +15617,7 @@ qemuDomainSnapshotDelete(virDomainSnapshotPtr snapshot, rep.err = 0; rep.last = NULL; rep.caps = driver->caps; + rep.xmlopt = driver->xmlopt; virDomainSnapshotForEachChild(snap, qemuDomainSnapshotReparentChildren, &rep); diff --git a/src/test/test_driver.c b/src/test/test_driver.c index 338b7d35d..8cecc6add 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -6235,6 +6235,7 @@ testDomainSnapshotGetXMLDesc(virDomainSnapshotPtr snapshot, virUUIDFormat(snapshot->domain->uuid, uuidstr); xml = virDomainSnapshotDefFormat(uuidstr, snap->def, privconn->caps, + privconn->xmlopt, virDomainDefFormatConvertXMLFlags(flags), 0); diff --git a/src/vbox/vbox_common.c b/src/vbox/vbox_common.c index c66939e01..1a90d00aa 100644 --- a/src/vbox/vbox_common.c +++ b/src/vbox/vbox_common.c @@ -5105,7 +5105,7 @@ vboxSnapshotRedefine(virDomainPtr dom, VIR_FREE(currentSnapshotXmlFilePath); if (virAsprintf(¤tSnapshotXmlFilePath, "%s%s.xml", machineLocationPath, snapshotMachineDesc->currentSnapshot) < 0) goto cleanup; - char *snapshotContent = virDomainSnapshotDefFormat(NULL, def, data->caps, VIR_DOMAIN_DEF_FORMAT_SECURE, 0); + char *snapshotContent = virDomainSnapshotDefFormat(NULL, def, data->caps, data->xmlopt, VIR_DOMAIN_DEF_FORMAT_SECURE, 0); if (snapshotContent == NULL) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Unable to get snapshot content")); @@ -6027,7 +6027,7 @@ static char *vboxDomainSnapshotGetXMLDesc(virDomainSnapshotPtr snapshot, virUUIDFormat(dom->uuid, uuidstr); memcpy(def->dom->uuid, dom->uuid, VIR_UUID_BUFLEN); - ret = virDomainSnapshotDefFormat(uuidstr, def, data->caps, + ret = virDomainSnapshotDefFormat(uuidstr, def, data->caps, data->xmlopt, virDomainDefFormatConvertXMLFlags(flags), 0); diff --git a/src/vz/vz_driver.c b/src/vz/vz_driver.c index 2c021884f..e6bb20182 100644 --- a/src/vz/vz_driver.c +++ b/src/vz/vz_driver.c @@ -2303,6 +2303,7 @@ vzDomainSnapshotGetXMLDesc(virDomainSnapshotPtr snapshot, unsigned int flags) virUUIDFormat(snapshot->domain->uuid, uuidstr); xml = virDomainSnapshotDefFormat(uuidstr, snap->def, privconn->driver->caps, + privconn->driver->xmlopt, virDomainDefFormatConvertXMLFlags(flags), 0); diff --git a/tests/domainsnapshotxml2xmltest.c b/tests/domainsnapshotxml2xmltest.c index bb4d60f7d..3a6f86b4a 100644 --- a/tests/domainsnapshotxml2xmltest.c +++ b/tests/domainsnapshotxml2xmltest.c @@ -101,6 +101,7 @@ testCompareXMLToXMLFiles(const char *inxml, goto cleanup; if (!(actual = virDomainSnapshotDefFormat(uuid, def, driver.caps, + driver.xmlopt, VIR_DOMAIN_DEF_FORMAT_SECURE, internal))) goto cleanup; -- 2.13.0

Since virQEMUSaveHeader will be followed by more than just domain XML, the old name would be confusing as it was designed to describe the length of all data following the save image header. Signed-off-by: Jiri Denemark <jdenemar@redhat.com> Reviewed-by: Pavel Hrdina <phrdina@redhat.com> --- Notes: Version 2: - enhanced commit message src/qemu/qemu_driver.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index f97d41030..b0d67adc1 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -2805,7 +2805,7 @@ typedef virQEMUSaveHeader *virQEMUSaveHeaderPtr; struct _virQEMUSaveHeader { char magic[sizeof(QEMU_SAVE_MAGIC)-1]; uint32_t version; - uint32_t xml_len; + uint32_t data_len; uint32_t was_running; uint32_t compressed; uint32_t unused[15]; @@ -2815,7 +2815,7 @@ static inline void bswap_header(virQEMUSaveHeaderPtr hdr) { hdr->version = bswap_32(hdr->version); - hdr->xml_len = bswap_32(hdr->xml_len); + hdr->data_len = bswap_32(hdr->data_len); hdr->was_running = bswap_32(hdr->was_running); hdr->compressed = bswap_32(hdr->compressed); } @@ -2836,7 +2836,7 @@ qemuDomainSaveHeader(int fd, const char *path, const char *xml, goto endjob; } - if (safewrite(fd, xml, header->xml_len) != header->xml_len) { + if (safewrite(fd, xml, header->data_len) != header->data_len) { ret = -errno; virReportError(VIR_ERR_OPERATION_FAILED, _("failed to write xml to '%s'"), path); @@ -3082,7 +3082,7 @@ qemuDomainSaveMemory(virQEMUDriverPtr driver, header.version = QEMU_SAVE_VERSION; header.was_running = was_running ? 1 : 0; header.compressed = compressed; - header.xml_len = strlen(domXML) + 1; + header.data_len = strlen(domXML) + 1; /* Obtain the file handle. */ if ((flags & VIR_DOMAIN_SAVE_BYPASS_CACHE)) { @@ -6276,16 +6276,16 @@ qemuDomainSaveImageOpen(virQEMUDriverPtr driver, goto error; } - if (header.xml_len <= 0) { + if (header.data_len <= 0) { virReportError(VIR_ERR_OPERATION_FAILED, - _("invalid XML length: %d"), header.xml_len); + _("invalid XML length: %d"), header.data_len); goto error; } - if (VIR_ALLOC_N(xml, header.xml_len) < 0) + if (VIR_ALLOC_N(xml, header.data_len) < 0) goto error; - if (saferead(fd, xml, header.xml_len) != header.xml_len) { + if (saferead(fd, xml, header.data_len) != header.data_len) { virReportError(VIR_ERR_OPERATION_FAILED, "%s", _("failed to read XML")); goto error; @@ -6629,12 +6629,12 @@ qemuDomainSaveImageDefineXML(virConnectPtr conn, const char *path, goto cleanup; len = strlen(xml) + 1; - if (len > header.xml_len) { + if (len > header.data_len) { virReportError(VIR_ERR_OPERATION_FAILED, "%s", _("new xml too large to fit in file")); goto cleanup; } - if (VIR_EXPAND_N(xml, len, header.xml_len - len) < 0) + if (VIR_EXPAND_N(xml, len, header.data_len - len) < 0) goto cleanup; if (lseek(fd, 0, SEEK_SET) != 0) { -- 2.13.0

Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- Notes: Version 2: - new patch, separated from the original 06/15 src/qemu/qemu_driver.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index b0d67adc1..9d0feb268 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -6235,12 +6235,13 @@ qemuDomainSaveImageOpen(virQEMUDriverPtr driver, virReportSystemError(errno, _("cannot remove corrupt file: %s"), path); - goto error; + } else { + fd = -3; } - return -3; + } else { + virReportError(VIR_ERR_OPERATION_FAILED, + "%s", _("failed to read qemu header")); } - virReportError(VIR_ERR_OPERATION_FAILED, - "%s", _("failed to read qemu header")); goto error; } @@ -6255,9 +6256,10 @@ qemuDomainSaveImageOpen(virQEMUDriverPtr driver, virReportSystemError(errno, _("cannot remove corrupt file: %s"), path); - goto error; + } else { + fd = -3; } - return -3; + goto error; } } virReportError(VIR_ERR_OPERATION_FAILED, "%s", msg); -- 2.13.0

On Wed, Jun 07, 2017 at 10:37:31AM +0200, Jiri Denemark wrote:
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> ---
Notes: Version 2: - new patch, separated from the original 06/15
src/qemu/qemu_driver.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-)
Reviewed-by: Pavel Hrdina <phrdina@redhat.com>

This is a preparation for creating a new virQEMUSaveData structure which will encapsulate all save image header data. Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- Notes: Version 2: - new patch (separated from the original 06/15) src/qemu/qemu_driver.c | 128 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 83 insertions(+), 45 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 9d0feb268..be6bedd6a 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -2821,6 +2821,36 @@ bswap_header(virQEMUSaveHeaderPtr hdr) } +static void +virQEMUSaveDataFree(virQEMUSaveHeaderPtr header) +{ + if (!header) + return; + + VIR_FREE(header); +} + + +static virQEMUSaveHeaderPtr +virQEMUSaveDataNew(char *domXML, + bool running, + int compressed) +{ + virQEMUSaveHeaderPtr header = NULL; + + if (VIR_ALLOC(header) < 0) + return NULL; + + memcpy(header->magic, QEMU_SAVE_PARTIAL, sizeof(header->magic)); + header->version = QEMU_SAVE_VERSION; + header->was_running = running ? 1 : 0; + header->compressed = compressed; + header->data_len = strlen(domXML) + 1; + + return header; +} + + /* return -errno on failure, or 0 on success */ static int qemuDomainSaveHeader(int fd, const char *path, const char *xml, @@ -3062,13 +3092,11 @@ qemuDomainSaveMemory(virQEMUDriverPtr driver, virDomainObjPtr vm, const char *path, const char *domXML, - int compressed, + virQEMUSaveHeaderPtr header, const char *compressedpath, - bool was_running, unsigned int flags, qemuDomainAsyncJob asyncJob) { - virQEMUSaveHeader header; bool bypassSecurityDriver = false; bool needUnlink = false; int ret = -1; @@ -3077,13 +3105,6 @@ qemuDomainSaveMemory(virQEMUDriverPtr driver, virFileWrapperFdPtr wrapperFd = NULL; unsigned int wrapperFlags = VIR_FILE_WRAPPER_NON_BLOCKING; - memset(&header, 0, sizeof(header)); - memcpy(header.magic, QEMU_SAVE_PARTIAL, sizeof(header.magic)); - header.version = QEMU_SAVE_VERSION; - header.was_running = was_running ? 1 : 0; - header.compressed = compressed; - header.data_len = strlen(domXML) + 1; - /* Obtain the file handle. */ if ((flags & VIR_DOMAIN_SAVE_BYPASS_CACHE)) { wrapperFlags |= VIR_FILE_WRAPPER_BYPASS_CACHE; @@ -3107,7 +3128,7 @@ qemuDomainSaveMemory(virQEMUDriverPtr driver, goto cleanup; /* Write header to file, followed by XML */ - if (qemuDomainSaveHeader(fd, path, domXML, &header) < 0) + if (qemuDomainSaveHeader(fd, path, domXML, header) < 0) goto cleanup; /* Perform the migration */ @@ -3131,7 +3152,7 @@ qemuDomainSaveMemory(virQEMUDriverPtr driver, if ((fd = qemuOpenFile(driver, vm, path, O_WRONLY, NULL, NULL)) < 0) goto cleanup; - memcpy(header.magic, QEMU_SAVE_MAGIC, sizeof(header.magic)); + memcpy(header->magic, QEMU_SAVE_MAGIC, sizeof(header->magic)); if (safewrite(fd, &header, sizeof(header)) != sizeof(header)) { virReportSystemError(errno, _("unable to write %s"), path); @@ -3172,6 +3193,7 @@ qemuDomainSaveInternal(virQEMUDriverPtr driver, virDomainPtr dom, virObjectEventPtr event = NULL; qemuDomainObjPrivatePtr priv = vm->privateData; virCapsPtr caps; + virQEMUSaveHeaderPtr header = NULL; if (!(caps = virQEMUDriverGetCapabilities(driver, false))) goto cleanup; @@ -3237,9 +3259,11 @@ qemuDomainSaveInternal(virQEMUDriverPtr driver, virDomainPtr dom, goto endjob; } - ret = qemuDomainSaveMemory(driver, vm, path, xml, compressed, - compressedpath, was_running, flags, - QEMU_ASYNC_JOB_SAVE); + if (!(header = virQEMUSaveDataNew(xml, was_running, compressed))) + goto endjob; + + ret = qemuDomainSaveMemory(driver, vm, path, xml, header, compressedpath, + flags, QEMU_ASYNC_JOB_SAVE); if (ret < 0) goto endjob; @@ -3272,6 +3296,7 @@ qemuDomainSaveInternal(virQEMUDriverPtr driver, virDomainPtr dom, cleanup: VIR_FREE(xml); + virQEMUSaveDataFree(header); qemuDomainEventQueue(driver, event); virObjectUnref(caps); return ret; @@ -6195,7 +6220,7 @@ static int ATTRIBUTE_NONNULL(3) ATTRIBUTE_NONNULL(4) qemuDomainSaveImageOpen(virQEMUDriverPtr driver, const char *path, virDomainDefPtr *ret_def, - virQEMUSaveHeaderPtr ret_header, + virQEMUSaveHeaderPtr *ret_header, char **xmlout, bool bypass_cache, virFileWrapperFdPtr *wrapperFd, @@ -6203,8 +6228,8 @@ qemuDomainSaveImageOpen(virQEMUDriverPtr driver, bool unlink_corrupt) { int fd = -1; - virQEMUSaveHeader header; char *xml = NULL; + virQEMUSaveHeaderPtr header = NULL; virDomainDefPtr def = NULL; int oflags = open_write ? O_RDWR : O_RDONLY; virCapsPtr caps = NULL; @@ -6229,7 +6254,10 @@ qemuDomainSaveImageOpen(virQEMUDriverPtr driver, VIR_FILE_WRAPPER_BYPASS_CACHE))) goto error; - if (saferead(fd, &header, sizeof(header)) != sizeof(header)) { + if (VIR_ALLOC(header) < 0) + goto error; + + if (saferead(fd, header, sizeof(*header)) != sizeof(*header)) { if (unlink_corrupt) { if (VIR_CLOSE(fd) < 0 || unlink(path) < 0) { virReportSystemError(errno, @@ -6245,11 +6273,11 @@ qemuDomainSaveImageOpen(virQEMUDriverPtr driver, goto error; } - if (memcmp(header.magic, QEMU_SAVE_MAGIC, sizeof(header.magic)) != 0) { + if (memcmp(header->magic, QEMU_SAVE_MAGIC, sizeof(header->magic)) != 0) { const char *msg = _("image magic is incorrect"); - if (memcmp(header.magic, QEMU_SAVE_PARTIAL, - sizeof(header.magic)) == 0) { + if (memcmp(header->magic, QEMU_SAVE_PARTIAL, + sizeof(header->magic)) == 0) { msg = _("save image is incomplete"); if (unlink_corrupt) { if (VIR_CLOSE(fd) < 0 || unlink(path) < 0) { @@ -6266,28 +6294,28 @@ qemuDomainSaveImageOpen(virQEMUDriverPtr driver, goto error; } - if (header.version > QEMU_SAVE_VERSION) { + if (header->version > QEMU_SAVE_VERSION) { /* convert endianess and try again */ - bswap_header(&header); + bswap_header(header); } - if (header.version > QEMU_SAVE_VERSION) { + if (header->version > QEMU_SAVE_VERSION) { virReportError(VIR_ERR_OPERATION_FAILED, _("image version is not supported (%d > %d)"), - header.version, QEMU_SAVE_VERSION); + header->version, QEMU_SAVE_VERSION); goto error; } - if (header.data_len <= 0) { + if (header->data_len <= 0) { virReportError(VIR_ERR_OPERATION_FAILED, - _("invalid XML length: %d"), header.data_len); + _("invalid XML length: %d"), header->data_len); goto error; } - if (VIR_ALLOC_N(xml, header.data_len) < 0) + if (VIR_ALLOC_N(xml, header->data_len) < 0) goto error; - if (saferead(fd, xml, header.data_len) != header.data_len) { + if (saferead(fd, xml, header->data_len) != header->data_len) { virReportError(VIR_ERR_OPERATION_FAILED, "%s", _("failed to read XML")); goto error; @@ -6314,6 +6342,7 @@ qemuDomainSaveImageOpen(virQEMUDriverPtr driver, error: virDomainDefFree(def); VIR_FREE(xml); + virQEMUSaveDataFree(header); VIR_FORCE_CLOSE(fd); virObjectUnref(caps); @@ -6325,7 +6354,7 @@ qemuDomainSaveImageStartVM(virConnectPtr conn, virQEMUDriverPtr driver, virDomainObjPtr vm, int *fd, - const virQEMUSaveHeader *header, + virQEMUSaveHeaderPtr header, const char *path, bool start_paused, qemuDomainAsyncJob asyncJob) @@ -6451,7 +6480,7 @@ qemuDomainRestoreFlags(virConnectPtr conn, const char *newxml = dxml; int fd = -1; int ret = -1; - virQEMUSaveHeader header; + virQEMUSaveHeaderPtr header = NULL; virFileWrapperFdPtr wrapperFd = NULL; bool hook_taint = false; @@ -6508,9 +6537,9 @@ qemuDomainRestoreFlags(virConnectPtr conn, def = NULL; if (flags & VIR_DOMAIN_SAVE_RUNNING) - header.was_running = 1; + header->was_running = 1; else if (flags & VIR_DOMAIN_SAVE_PAUSED) - header.was_running = 0; + header->was_running = 0; if (hook_taint) { priv = vm->privateData; @@ -6520,7 +6549,7 @@ qemuDomainRestoreFlags(virConnectPtr conn, if (qemuProcessBeginJob(driver, vm, VIR_DOMAIN_JOB_OPERATION_RESTORE) < 0) goto cleanup; - ret = qemuDomainSaveImageStartVM(conn, driver, vm, &fd, &header, path, + ret = qemuDomainSaveImageStartVM(conn, driver, vm, &fd, header, path, false, QEMU_ASYNC_JOB_START); if (virFileWrapperFdClose(wrapperFd) < 0) VIR_WARN("Failed to close %s", path); @@ -6531,6 +6560,7 @@ qemuDomainRestoreFlags(virConnectPtr conn, virDomainDefFree(def); VIR_FORCE_CLOSE(fd); VIR_FREE(xml); + virQEMUSaveDataFree(header); VIR_FREE(xmlout); virFileWrapperFdFree(wrapperFd); if (vm && ret < 0) @@ -6555,7 +6585,7 @@ qemuDomainSaveImageGetXMLDesc(virConnectPtr conn, const char *path, char *ret = NULL; virDomainDefPtr def = NULL; int fd = -1; - virQEMUSaveHeader header; + virQEMUSaveHeaderPtr header = NULL; /* We only take subset of virDomainDefFormat flags. */ virCheckFlags(VIR_DOMAIN_XML_SECURE, NULL); @@ -6572,6 +6602,7 @@ qemuDomainSaveImageGetXMLDesc(virConnectPtr conn, const char *path, ret = qemuDomainDefFormatXML(driver, def, flags); cleanup: + virQEMUSaveDataFree(header); virDomainDefFree(def); VIR_FORCE_CLOSE(fd); return ret; @@ -6586,9 +6617,9 @@ qemuDomainSaveImageDefineXML(virConnectPtr conn, const char *path, virDomainDefPtr def = NULL; virDomainDefPtr newdef = NULL; int fd = -1; - virQEMUSaveHeader header; char *xml = NULL; size_t len; + virQEMUSaveHeaderPtr header = NULL; int state = -1; virCheckFlags(VIR_DOMAIN_SAVE_RUNNING | @@ -6609,14 +6640,14 @@ qemuDomainSaveImageDefineXML(virConnectPtr conn, const char *path, goto cleanup; if (STREQ(xml, dxml) && - (state < 0 || state == header.was_running)) { + (state < 0 || state == header->was_running)) { /* no change to the XML */ ret = 0; goto cleanup; } if (state >= 0) - header.was_running = state; + header->was_running = state; if (!(newdef = qemuDomainSaveImageUpdateDef(driver, def, dxml))) goto cleanup; @@ -6631,12 +6662,12 @@ qemuDomainSaveImageDefineXML(virConnectPtr conn, const char *path, goto cleanup; len = strlen(xml) + 1; - if (len > header.data_len) { + if (len > header->data_len) { virReportError(VIR_ERR_OPERATION_FAILED, "%s", _("new xml too large to fit in file")); goto cleanup; } - if (VIR_EXPAND_N(xml, len, header.data_len - len) < 0) + if (VIR_EXPAND_N(xml, len, header->data_len - len) < 0) goto cleanup; if (lseek(fd, 0, SEEK_SET) != 0) { @@ -6657,6 +6688,7 @@ qemuDomainSaveImageDefineXML(virConnectPtr conn, const char *path, virDomainDefFree(newdef); VIR_FORCE_CLOSE(fd); VIR_FREE(xml); + virQEMUSaveDataFree(header); return ret; } @@ -6677,7 +6709,7 @@ qemuDomainObjRestore(virConnectPtr conn, int ret = -1; char *xml = NULL; char *xmlout = NULL; - virQEMUSaveHeader header; + virQEMUSaveHeaderPtr header = NULL; virFileWrapperFdPtr wrapperFd = NULL; fd = qemuDomainSaveImageOpen(driver, path, &def, &header, &xml, @@ -6728,13 +6760,14 @@ qemuDomainObjRestore(virConnectPtr conn, virDomainObjAssignDef(vm, def, true, NULL); def = NULL; - ret = qemuDomainSaveImageStartVM(conn, driver, vm, &fd, &header, path, + ret = qemuDomainSaveImageStartVM(conn, driver, vm, &fd, header, path, start_paused, asyncJob); if (virFileWrapperFdClose(wrapperFd) < 0) VIR_WARN("Failed to close %s", path); cleanup: VIR_FREE(xml); + virQEMUSaveDataFree(header); VIR_FREE(xmlout); virDomainDefFree(def); VIR_FORCE_CLOSE(fd); @@ -14300,6 +14333,7 @@ qemuDomainSnapshotCreateActiveExternal(virConnectPtr conn, virQEMUDriverConfigPtr cfg = NULL; int compressed; char *compressedpath = NULL; + virQEMUSaveHeaderPtr header = NULL; /* If quiesce was requested, then issue a freeze command, and a * counterpart thaw command when it is actually sent to agent. @@ -14371,9 +14405,12 @@ qemuDomainSnapshotCreateActiveExternal(virConnectPtr conn, if (!(xml = qemuDomainDefFormatLive(driver, vm->def, true, true))) goto cleanup; - if ((ret = qemuDomainSaveMemory(driver, vm, snap->def->file, xml, - compressed, compressedpath, resume, - 0, QEMU_ASYNC_JOB_SNAPSHOT)) < 0) + if (!(header = virQEMUSaveDataNew(xml, resume, compressed))) + goto cleanup; + + if ((ret = qemuDomainSaveMemory(driver, vm, snap->def->file, xml, header, + compressedpath, 0, + QEMU_ASYNC_JOB_SNAPSHOT)) < 0) goto cleanup; /* the memory image was created, remove it on errors */ @@ -14441,6 +14478,7 @@ qemuDomainSnapshotCreateActiveExternal(virConnectPtr conn, ret = -1; } + virQEMUSaveDataFree(header); VIR_FREE(xml); VIR_FREE(compressedpath); virObjectUnref(cfg); -- 2.13.0

On Wed, Jun 07, 2017 at 10:37:32AM +0200, Jiri Denemark wrote:
This is a preparation for creating a new virQEMUSaveData structure which will encapsulate all save image header data.
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> ---
Notes: Version 2: - new patch (separated from the original 06/15)
src/qemu/qemu_driver.c | 128 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 83 insertions(+), 45 deletions(-)
Reviewed-by: Pavel Hrdina <phrdina@redhat.com>

The function is supposed to update the save image header after a successful migration to the save image file. Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- Notes: Version 2: - new patch (separated from the original 06/15) src/qemu/qemu_driver.c | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index be6bedd6a..b51a41641 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -2877,6 +2877,25 @@ qemuDomainSaveHeader(int fd, const char *path, const char *xml, } +static int +virQEMUSaveDataFinish(virQEMUSaveHeaderPtr header, + int *fd, + const char *path) +{ + memcpy(header->magic, QEMU_SAVE_MAGIC, sizeof(header->magic)); + + if (safewrite(*fd, header, sizeof(*header)) != sizeof(*header) || + VIR_CLOSE(*fd) < 0) { + virReportSystemError(errno, + _("failed to write header to domain save file '%s'"), + path); + return -1; + } + + return 0; +} + + static virCommandPtr qemuCompressGetCommand(virQEMUSaveFormat compression) { @@ -3149,21 +3168,10 @@ qemuDomainSaveMemory(virQEMUDriverPtr driver, if (virFileWrapperFdClose(wrapperFd) < 0) goto cleanup; - if ((fd = qemuOpenFile(driver, vm, path, O_WRONLY, NULL, NULL)) < 0) + if ((fd = qemuOpenFile(driver, vm, path, O_WRONLY, NULL, NULL)) < 0 || + virQEMUSaveDataFinish(header, &fd, path) < 0) goto cleanup; - memcpy(header->magic, QEMU_SAVE_MAGIC, sizeof(header->magic)); - - if (safewrite(fd, &header, sizeof(header)) != sizeof(header)) { - virReportSystemError(errno, _("unable to write %s"), path); - goto cleanup; - } - - if (VIR_CLOSE(fd) < 0) { - virReportSystemError(errno, _("unable to close %s"), path); - goto cleanup; - } - ret = 0; cleanup: -- 2.13.0

On Wed, Jun 07, 2017 at 10:37:33AM +0200, Jiri Denemark wrote:
The function is supposed to update the save image header after a successful migration to the save image file.
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> ---
Notes: Version 2: - new patch (separated from the original 06/15)
src/qemu/qemu_driver.c | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-)
Reviewed-by: Pavel Hrdina <phrdina@redhat.com>

The function is now called virQEMUSaveDataWrite and it is now doing everything it needs to save both the save image header and domain XML to a file. Be it a new file or an existing file in which a user wants to change the domain XML. Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- Notes: Version 2: - new patch (separated from the original 06/15) - VIR_EXPAND_N is no longer used to pad domain XML with zeros src/qemu/qemu_driver.c | 98 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 60 insertions(+), 38 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index b51a41641..5f5215b3d 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -2832,7 +2832,7 @@ virQEMUSaveDataFree(virQEMUSaveHeaderPtr header) static virQEMUSaveHeaderPtr -virQEMUSaveDataNew(char *domXML, +virQEMUSaveDataNew(char *domXML ATTRIBUTE_UNUSED, bool running, int compressed) { @@ -2845,34 +2845,66 @@ virQEMUSaveDataNew(char *domXML, header->version = QEMU_SAVE_VERSION; header->was_running = running ? 1 : 0; header->compressed = compressed; - header->data_len = strlen(domXML) + 1; return header; } -/* return -errno on failure, or 0 on success */ +/* virQEMUSaveDataWrite: + * + * Writes libvirt's header (including domain XML) into a saved image of a + * running domain. If @header has data_len filled in (because it was previously + * read from the file), the function will make sure the new data will fit + * within data_len. + * + * Returns -1 on failure, or 0 on success. + */ static int -qemuDomainSaveHeader(int fd, const char *path, const char *xml, - virQEMUSaveHeaderPtr header) +virQEMUSaveDataWrite(virQEMUSaveHeaderPtr header, + const char *xml, + int fd, + const char *path) { - int ret = 0; + size_t len; + int ret = -1; + size_t zerosLen = 0; + char *zeros = NULL; + + len = strlen(xml) + 1; + + if (header->data_len > 0) { + if (len > header->data_len) { + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("new xml too large to fit in file")); + goto cleanup; + } + + zerosLen = header->data_len - len; + if (VIR_ALLOC_N(zeros, zerosLen) < 0) + goto cleanup; + } else { + header->data_len = len; + } if (safewrite(fd, header, sizeof(*header)) != sizeof(*header)) { - ret = -errno; - virReportError(VIR_ERR_OPERATION_FAILED, - _("failed to write header to domain save file '%s'"), - path); - goto endjob; + virReportSystemError(errno, + _("failed to write header to domain save file '%s'"), + path); + goto cleanup; } - if (safewrite(fd, xml, header->data_len) != header->data_len) { - ret = -errno; - virReportError(VIR_ERR_OPERATION_FAILED, - _("failed to write xml to '%s'"), path); - goto endjob; + if (safewrite(fd, xml, header->data_len) != header->data_len || + safewrite(fd, zeros, zerosLen) != zerosLen) { + virReportSystemError(errno, + _("failed to write domain xml to '%s'"), + path); + goto cleanup; } - endjob: + + ret = 0; + + cleanup: + VIR_FREE(zeros); return ret; } @@ -3146,8 +3178,7 @@ qemuDomainSaveMemory(virQEMUDriverPtr driver, if (!(wrapperFd = virFileWrapperFdNew(&fd, path, wrapperFlags))) goto cleanup; - /* Write header to file, followed by XML */ - if (qemuDomainSaveHeader(fd, path, domXML, header) < 0) + if (virQEMUSaveDataWrite(header, domXML, fd, path) < 0) goto cleanup; /* Perform the migration */ @@ -6626,7 +6657,6 @@ qemuDomainSaveImageDefineXML(virConnectPtr conn, const char *path, virDomainDefPtr newdef = NULL; int fd = -1; char *xml = NULL; - size_t len; virQEMUSaveHeaderPtr header = NULL; int state = -1; @@ -6662,30 +6692,22 @@ qemuDomainSaveImageDefineXML(virConnectPtr conn, const char *path, VIR_FREE(xml); - xml = qemuDomainDefFormatXML(driver, newdef, - VIR_DOMAIN_XML_INACTIVE | - VIR_DOMAIN_XML_SECURE | - VIR_DOMAIN_XML_MIGRATABLE); - if (!xml) - goto cleanup; - len = strlen(xml) + 1; - - if (len > header->data_len) { - virReportError(VIR_ERR_OPERATION_FAILED, "%s", - _("new xml too large to fit in file")); - goto cleanup; - } - if (VIR_EXPAND_N(xml, len, header->data_len - len) < 0) + if (!(xml = qemuDomainDefFormatXML(driver, newdef, + VIR_DOMAIN_XML_INACTIVE | + VIR_DOMAIN_XML_SECURE | + VIR_DOMAIN_XML_MIGRATABLE))) goto cleanup; if (lseek(fd, 0, SEEK_SET) != 0) { virReportSystemError(errno, _("cannot seek in '%s'"), path); goto cleanup; } - if (safewrite(fd, &header, sizeof(header)) != sizeof(header) || - safewrite(fd, xml, len) != len || - VIR_CLOSE(fd) < 0) { - virReportSystemError(errno, _("failed to write xml to '%s'"), path); + + if (virQEMUSaveDataWrite(header, xml, fd, path) < 0) + goto cleanup; + + if (VIR_CLOSE(fd) < 0) { + virReportSystemError(errno, _("failed to write header data to '%s'"), path); goto cleanup; } -- 2.13.0

On Wed, Jun 07, 2017 at 10:37:34AM +0200, Jiri Denemark wrote:
The function is now called virQEMUSaveDataWrite and it is now doing everything it needs to save both the save image header and domain XML to a file. Be it a new file or an existing file in which a user wants to change the domain XML.
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> ---
Notes: Version 2: - new patch (separated from the original 06/15) - VIR_EXPAND_N is no longer used to pad domain XML with zeros
src/qemu/qemu_driver.c | 98 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 60 insertions(+), 38 deletions(-)
Reviewed-by: Pavel Hrdina <phrdina@redhat.com>

The new structure encapsulates save image header and associated data (domain XML). Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- Notes: Version 2: - new patch (separated from the original 06/15) src/qemu/qemu_driver.c | 165 ++++++++++++++++++++++++++----------------------- 1 file changed, 86 insertions(+), 79 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 5f5215b3d..dba0d4025 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -2811,6 +2811,14 @@ struct _virQEMUSaveHeader { uint32_t unused[15]; }; +typedef struct _virQEMUSaveData virQEMUSaveData; +typedef virQEMUSaveData *virQEMUSaveDataPtr; +struct _virQEMUSaveData { + virQEMUSaveHeader header; + char *xml; +}; + + static inline void bswap_header(virQEMUSaveHeaderPtr hdr) { @@ -2822,31 +2830,39 @@ bswap_header(virQEMUSaveHeaderPtr hdr) static void -virQEMUSaveDataFree(virQEMUSaveHeaderPtr header) +virQEMUSaveDataFree(virQEMUSaveDataPtr data) { - if (!header) + if (!data) return; - VIR_FREE(header); + VIR_FREE(data->xml); + VIR_FREE(data); } -static virQEMUSaveHeaderPtr -virQEMUSaveDataNew(char *domXML ATTRIBUTE_UNUSED, +/** + * This function steals @domXML on success. + */ +static virQEMUSaveDataPtr +virQEMUSaveDataNew(char *domXML, bool running, int compressed) { - virQEMUSaveHeaderPtr header = NULL; + virQEMUSaveDataPtr data = NULL; + virQEMUSaveHeaderPtr header; - if (VIR_ALLOC(header) < 0) + if (VIR_ALLOC(data) < 0) return NULL; + VIR_STEAL_PTR(data->xml, domXML); + + header = &data->header; memcpy(header->magic, QEMU_SAVE_PARTIAL, sizeof(header->magic)); header->version = QEMU_SAVE_VERSION; header->was_running = running ? 1 : 0; header->compressed = compressed; - return header; + return data; } @@ -2860,17 +2876,17 @@ virQEMUSaveDataNew(char *domXML ATTRIBUTE_UNUSED, * Returns -1 on failure, or 0 on success. */ static int -virQEMUSaveDataWrite(virQEMUSaveHeaderPtr header, - const char *xml, +virQEMUSaveDataWrite(virQEMUSaveDataPtr data, int fd, const char *path) { + virQEMUSaveHeaderPtr header = &data->header; size_t len; int ret = -1; size_t zerosLen = 0; char *zeros = NULL; - len = strlen(xml) + 1; + len = strlen(data->xml) + 1; if (header->data_len > 0) { if (len > header->data_len) { @@ -2893,7 +2909,7 @@ virQEMUSaveDataWrite(virQEMUSaveHeaderPtr header, goto cleanup; } - if (safewrite(fd, xml, header->data_len) != header->data_len || + if (safewrite(fd, data->xml, header->data_len) != header->data_len || safewrite(fd, zeros, zerosLen) != zerosLen) { virReportSystemError(errno, _("failed to write domain xml to '%s'"), @@ -2910,10 +2926,12 @@ virQEMUSaveDataWrite(virQEMUSaveHeaderPtr header, static int -virQEMUSaveDataFinish(virQEMUSaveHeaderPtr header, +virQEMUSaveDataFinish(virQEMUSaveDataPtr data, int *fd, const char *path) { + virQEMUSaveHeaderPtr header = &data->header; + memcpy(header->magic, QEMU_SAVE_MAGIC, sizeof(header->magic)); if (safewrite(*fd, header, sizeof(*header)) != sizeof(*header) || @@ -3142,8 +3160,7 @@ static int qemuDomainSaveMemory(virQEMUDriverPtr driver, virDomainObjPtr vm, const char *path, - const char *domXML, - virQEMUSaveHeaderPtr header, + virQEMUSaveDataPtr data, const char *compressedpath, unsigned int flags, qemuDomainAsyncJob asyncJob) @@ -3178,7 +3195,7 @@ qemuDomainSaveMemory(virQEMUDriverPtr driver, if (!(wrapperFd = virFileWrapperFdNew(&fd, path, wrapperFlags))) goto cleanup; - if (virQEMUSaveDataWrite(header, domXML, fd, path) < 0) + if (virQEMUSaveDataWrite(data, fd, path) < 0) goto cleanup; /* Perform the migration */ @@ -3200,7 +3217,7 @@ qemuDomainSaveMemory(virQEMUDriverPtr driver, goto cleanup; if ((fd = qemuOpenFile(driver, vm, path, O_WRONLY, NULL, NULL)) < 0 || - virQEMUSaveDataFinish(header, &fd, path) < 0) + virQEMUSaveDataFinish(data, &fd, path) < 0) goto cleanup; ret = 0; @@ -3232,7 +3249,7 @@ qemuDomainSaveInternal(virQEMUDriverPtr driver, virDomainPtr dom, virObjectEventPtr event = NULL; qemuDomainObjPrivatePtr priv = vm->privateData; virCapsPtr caps; - virQEMUSaveHeaderPtr header = NULL; + virQEMUSaveDataPtr data = NULL; if (!(caps = virQEMUDriverGetCapabilities(driver, false))) goto cleanup; @@ -3298,10 +3315,11 @@ qemuDomainSaveInternal(virQEMUDriverPtr driver, virDomainPtr dom, goto endjob; } - if (!(header = virQEMUSaveDataNew(xml, was_running, compressed))) + if (!(data = virQEMUSaveDataNew(xml, was_running, compressed))) goto endjob; + xml = NULL; - ret = qemuDomainSaveMemory(driver, vm, path, xml, header, compressedpath, + ret = qemuDomainSaveMemory(driver, vm, path, data, compressedpath, flags, QEMU_ASYNC_JOB_SAVE); if (ret < 0) goto endjob; @@ -3335,7 +3353,7 @@ qemuDomainSaveInternal(virQEMUDriverPtr driver, virDomainPtr dom, cleanup: VIR_FREE(xml); - virQEMUSaveDataFree(header); + virQEMUSaveDataFree(data); qemuDomainEventQueue(driver, event); virObjectUnref(caps); return ret; @@ -6244,8 +6262,7 @@ qemuDomainSaveImageUpdateDef(virQEMUDriverPtr driver, * @driver: qemu driver data * @path: path of the save image * @ret_def: returns domain definition created from the XML stored in the image - * @ret_header: returns structure filled with data from the image header - * @xmlout: returns the XML from the image file (may be NULL) + * @ret_data: returns structure filled with data from the image header * @bypass_cache: bypass cache when opening the file * @wrapperFd: returns the file wrapper structure * @open_write: open the file for writing (for updates) @@ -6259,16 +6276,15 @@ static int ATTRIBUTE_NONNULL(3) ATTRIBUTE_NONNULL(4) qemuDomainSaveImageOpen(virQEMUDriverPtr driver, const char *path, virDomainDefPtr *ret_def, - virQEMUSaveHeaderPtr *ret_header, - char **xmlout, + virQEMUSaveDataPtr *ret_data, bool bypass_cache, virFileWrapperFdPtr *wrapperFd, bool open_write, bool unlink_corrupt) { int fd = -1; - char *xml = NULL; - virQEMUSaveHeaderPtr header = NULL; + virQEMUSaveDataPtr data = NULL; + virQEMUSaveHeaderPtr header; virDomainDefPtr def = NULL; int oflags = open_write ? O_RDWR : O_RDONLY; virCapsPtr caps = NULL; @@ -6293,9 +6309,10 @@ qemuDomainSaveImageOpen(virQEMUDriverPtr driver, VIR_FILE_WRAPPER_BYPASS_CACHE))) goto error; - if (VIR_ALLOC(header) < 0) + if (VIR_ALLOC(data) < 0) goto error; + header = &data->header; if (saferead(fd, header, sizeof(*header)) != sizeof(*header)) { if (unlink_corrupt) { if (VIR_CLOSE(fd) < 0 || unlink(path) < 0) { @@ -6347,32 +6364,27 @@ qemuDomainSaveImageOpen(virQEMUDriverPtr driver, if (header->data_len <= 0) { virReportError(VIR_ERR_OPERATION_FAILED, - _("invalid XML length: %d"), header->data_len); + _("invalid header data length: %d"), header->data_len); goto error; } - if (VIR_ALLOC_N(xml, header->data_len) < 0) + if (VIR_ALLOC_N(data->xml, header->data_len) < 0) goto error; - if (saferead(fd, xml, header->data_len) != header->data_len) { + if (saferead(fd, data->xml, header->data_len) != header->data_len) { virReportError(VIR_ERR_OPERATION_FAILED, - "%s", _("failed to read XML")); + "%s", _("failed to read domain XML")); goto error; } /* Create a domain from this XML */ - if (!(def = virDomainDefParseString(xml, caps, driver->xmlopt, NULL, + if (!(def = virDomainDefParseString(data->xml, caps, driver->xmlopt, NULL, VIR_DOMAIN_DEF_PARSE_INACTIVE | VIR_DOMAIN_DEF_PARSE_SKIP_VALIDATE))) goto error; - if (xmlout) - *xmlout = xml; - else - VIR_FREE(xml); - *ret_def = def; - *ret_header = header; + *ret_data = data; virObjectUnref(caps); @@ -6380,8 +6392,7 @@ qemuDomainSaveImageOpen(virQEMUDriverPtr driver, error: virDomainDefFree(def); - VIR_FREE(xml); - virQEMUSaveDataFree(header); + virQEMUSaveDataFree(data); VIR_FORCE_CLOSE(fd); virObjectUnref(caps); @@ -6393,7 +6404,7 @@ qemuDomainSaveImageStartVM(virConnectPtr conn, virQEMUDriverPtr driver, virDomainObjPtr vm, int *fd, - virQEMUSaveHeaderPtr header, + virQEMUSaveDataPtr data, const char *path, bool start_paused, qemuDomainAsyncJob asyncJob) @@ -6405,6 +6416,7 @@ qemuDomainSaveImageStartVM(virConnectPtr conn, virCommandPtr cmd = NULL; char *errbuf = NULL; virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); + virQEMUSaveHeaderPtr header = &data->header; if ((header->version == 2) && (header->compressed != QEMU_SAVE_FORMAT_RAW)) { @@ -6514,12 +6526,11 @@ qemuDomainRestoreFlags(virConnectPtr conn, qemuDomainObjPrivatePtr priv = NULL; virDomainDefPtr def = NULL; virDomainObjPtr vm = NULL; - char *xml = NULL; char *xmlout = NULL; const char *newxml = dxml; int fd = -1; int ret = -1; - virQEMUSaveHeaderPtr header = NULL; + virQEMUSaveDataPtr data = NULL; virFileWrapperFdPtr wrapperFd = NULL; bool hook_taint = false; @@ -6530,7 +6541,7 @@ qemuDomainRestoreFlags(virConnectPtr conn, virNWFilterReadLockFilterUpdates(); - fd = qemuDomainSaveImageOpen(driver, path, &def, &header, &xml, + fd = qemuDomainSaveImageOpen(driver, path, &def, &data, (flags & VIR_DOMAIN_SAVE_BYPASS_CACHE) != 0, &wrapperFd, false, false); if (fd < 0) @@ -6546,7 +6557,7 @@ qemuDomainRestoreFlags(virConnectPtr conn, VIR_HOOK_QEMU_OP_RESTORE, VIR_HOOK_SUBOP_BEGIN, NULL, - dxml ? dxml : xml, + dxml ? dxml : data->xml, &xmlout)) < 0) goto cleanup; @@ -6576,9 +6587,9 @@ qemuDomainRestoreFlags(virConnectPtr conn, def = NULL; if (flags & VIR_DOMAIN_SAVE_RUNNING) - header->was_running = 1; + data->header.was_running = 1; else if (flags & VIR_DOMAIN_SAVE_PAUSED) - header->was_running = 0; + data->header.was_running = 0; if (hook_taint) { priv = vm->privateData; @@ -6588,7 +6599,7 @@ qemuDomainRestoreFlags(virConnectPtr conn, if (qemuProcessBeginJob(driver, vm, VIR_DOMAIN_JOB_OPERATION_RESTORE) < 0) goto cleanup; - ret = qemuDomainSaveImageStartVM(conn, driver, vm, &fd, header, path, + ret = qemuDomainSaveImageStartVM(conn, driver, vm, &fd, data, path, false, QEMU_ASYNC_JOB_START); if (virFileWrapperFdClose(wrapperFd) < 0) VIR_WARN("Failed to close %s", path); @@ -6598,8 +6609,7 @@ qemuDomainRestoreFlags(virConnectPtr conn, cleanup: virDomainDefFree(def); VIR_FORCE_CLOSE(fd); - VIR_FREE(xml); - virQEMUSaveDataFree(header); + virQEMUSaveDataFree(data); VIR_FREE(xmlout); virFileWrapperFdFree(wrapperFd); if (vm && ret < 0) @@ -6624,12 +6634,12 @@ qemuDomainSaveImageGetXMLDesc(virConnectPtr conn, const char *path, char *ret = NULL; virDomainDefPtr def = NULL; int fd = -1; - virQEMUSaveHeaderPtr header = NULL; + virQEMUSaveDataPtr data = NULL; /* We only take subset of virDomainDefFormat flags. */ virCheckFlags(VIR_DOMAIN_XML_SECURE, NULL); - fd = qemuDomainSaveImageOpen(driver, path, &def, &header, NULL, + fd = qemuDomainSaveImageOpen(driver, path, &def, &data, false, NULL, false, false); if (fd < 0) @@ -6641,7 +6651,7 @@ qemuDomainSaveImageGetXMLDesc(virConnectPtr conn, const char *path, ret = qemuDomainDefFormatXML(driver, def, flags); cleanup: - virQEMUSaveDataFree(header); + virQEMUSaveDataFree(data); virDomainDefFree(def); VIR_FORCE_CLOSE(fd); return ret; @@ -6656,8 +6666,7 @@ qemuDomainSaveImageDefineXML(virConnectPtr conn, const char *path, virDomainDefPtr def = NULL; virDomainDefPtr newdef = NULL; int fd = -1; - char *xml = NULL; - virQEMUSaveHeaderPtr header = NULL; + virQEMUSaveDataPtr data = NULL; int state = -1; virCheckFlags(VIR_DOMAIN_SAVE_RUNNING | @@ -6668,7 +6677,7 @@ qemuDomainSaveImageDefineXML(virConnectPtr conn, const char *path, else if (flags & VIR_DOMAIN_SAVE_PAUSED) state = 0; - fd = qemuDomainSaveImageOpen(driver, path, &def, &header, &xml, + fd = qemuDomainSaveImageOpen(driver, path, &def, &data, false, NULL, true, false); if (fd < 0) @@ -6677,25 +6686,25 @@ qemuDomainSaveImageDefineXML(virConnectPtr conn, const char *path, if (virDomainSaveImageDefineXMLEnsureACL(conn, def) < 0) goto cleanup; - if (STREQ(xml, dxml) && - (state < 0 || state == header->was_running)) { + if (STREQ(data->xml, dxml) && + (state < 0 || state == data->header.was_running)) { /* no change to the XML */ ret = 0; goto cleanup; } if (state >= 0) - header->was_running = state; + data->header.was_running = state; if (!(newdef = qemuDomainSaveImageUpdateDef(driver, def, dxml))) goto cleanup; - VIR_FREE(xml); + VIR_FREE(data->xml); - if (!(xml = qemuDomainDefFormatXML(driver, newdef, - VIR_DOMAIN_XML_INACTIVE | - VIR_DOMAIN_XML_SECURE | - VIR_DOMAIN_XML_MIGRATABLE))) + if (!(data->xml = qemuDomainDefFormatXML(driver, newdef, + VIR_DOMAIN_XML_INACTIVE | + VIR_DOMAIN_XML_SECURE | + VIR_DOMAIN_XML_MIGRATABLE))) goto cleanup; if (lseek(fd, 0, SEEK_SET) != 0) { @@ -6703,7 +6712,7 @@ qemuDomainSaveImageDefineXML(virConnectPtr conn, const char *path, goto cleanup; } - if (virQEMUSaveDataWrite(header, xml, fd, path) < 0) + if (virQEMUSaveDataWrite(data, fd, path) < 0) goto cleanup; if (VIR_CLOSE(fd) < 0) { @@ -6717,8 +6726,7 @@ qemuDomainSaveImageDefineXML(virConnectPtr conn, const char *path, virDomainDefFree(def); virDomainDefFree(newdef); VIR_FORCE_CLOSE(fd); - VIR_FREE(xml); - virQEMUSaveDataFree(header); + virQEMUSaveDataFree(data); return ret; } @@ -6737,12 +6745,11 @@ qemuDomainObjRestore(virConnectPtr conn, qemuDomainObjPrivatePtr priv = vm->privateData; int fd = -1; int ret = -1; - char *xml = NULL; char *xmlout = NULL; - virQEMUSaveHeaderPtr header = NULL; + virQEMUSaveDataPtr data = NULL; virFileWrapperFdPtr wrapperFd = NULL; - fd = qemuDomainSaveImageOpen(driver, path, &def, &header, &xml, + fd = qemuDomainSaveImageOpen(driver, path, &def, &data, bypass_cache, &wrapperFd, false, true); if (fd < 0) { if (fd == -3) @@ -6756,7 +6763,7 @@ qemuDomainObjRestore(virConnectPtr conn, if ((hookret = virHookCall(VIR_HOOK_DRIVER_QEMU, def->name, VIR_HOOK_QEMU_OP_RESTORE, VIR_HOOK_SUBOP_BEGIN, - NULL, xml, &xmlout)) < 0) + NULL, data->xml, &xmlout)) < 0) goto cleanup; if (hookret == 0 && !virStringIsEmpty(xmlout)) { @@ -6790,14 +6797,13 @@ qemuDomainObjRestore(virConnectPtr conn, virDomainObjAssignDef(vm, def, true, NULL); def = NULL; - ret = qemuDomainSaveImageStartVM(conn, driver, vm, &fd, header, path, + ret = qemuDomainSaveImageStartVM(conn, driver, vm, &fd, data, path, start_paused, asyncJob); if (virFileWrapperFdClose(wrapperFd) < 0) VIR_WARN("Failed to close %s", path); cleanup: - VIR_FREE(xml); - virQEMUSaveDataFree(header); + virQEMUSaveDataFree(data); VIR_FREE(xmlout); virDomainDefFree(def); VIR_FORCE_CLOSE(fd); @@ -14363,7 +14369,7 @@ qemuDomainSnapshotCreateActiveExternal(virConnectPtr conn, virQEMUDriverConfigPtr cfg = NULL; int compressed; char *compressedpath = NULL; - virQEMUSaveHeaderPtr header = NULL; + virQEMUSaveDataPtr data = NULL; /* If quiesce was requested, then issue a freeze command, and a * counterpart thaw command when it is actually sent to agent. @@ -14435,10 +14441,11 @@ qemuDomainSnapshotCreateActiveExternal(virConnectPtr conn, if (!(xml = qemuDomainDefFormatLive(driver, vm->def, true, true))) goto cleanup; - if (!(header = virQEMUSaveDataNew(xml, resume, compressed))) + if (!(data = virQEMUSaveDataNew(xml, resume, compressed))) goto cleanup; + xml = NULL; - if ((ret = qemuDomainSaveMemory(driver, vm, snap->def->file, xml, header, + if ((ret = qemuDomainSaveMemory(driver, vm, snap->def->file, data, compressedpath, 0, QEMU_ASYNC_JOB_SNAPSHOT)) < 0) goto cleanup; @@ -14508,7 +14515,7 @@ qemuDomainSnapshotCreateActiveExternal(virConnectPtr conn, ret = -1; } - virQEMUSaveDataFree(header); + virQEMUSaveDataFree(data); VIR_FREE(xml); VIR_FREE(compressedpath); virObjectUnref(cfg); -- 2.13.0

On Wed, Jun 07, 2017 at 10:37:35AM +0200, Jiri Denemark wrote:
The new structure encapsulates save image header and associated data (domain XML).
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> ---
Notes: Version 2: - new patch (separated from the original 06/15)
src/qemu/qemu_driver.c | 165 ++++++++++++++++++++++++++----------------------- 1 file changed, 86 insertions(+), 79 deletions(-)
Reviewed-by: Pavel Hrdina <phrdina@redhat.com>

The code will be used by snapshots and domain save/restore code to store additional data for a saved running domain. It is analogous to migration cookies, but simple and one way only. Signed-off-by: Jiri Denemark <jdenemar@redhat.com> Reviewed-by: Pavel Hrdina <phrdina@redhat.com> --- Notes: Version 2: - no change po/POTFILES.in | 1 + src/Makefile.am | 1 + src/conf/virsavecookie.c | 144 +++++++++++++++++++++++++++++++++++++++++++++++ src/conf/virsavecookie.h | 62 ++++++++++++++++++++ src/libvirt_private.syms | 7 +++ 5 files changed, 215 insertions(+) create mode 100644 src/conf/virsavecookie.c create mode 100644 src/conf/virsavecookie.h diff --git a/po/POTFILES.in b/po/POTFILES.in index 923d64727..275df1f29 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -47,6 +47,7 @@ src/conf/virdomainobjlist.c src/conf/virnetworkobj.c src/conf/virnodedeviceobj.c src/conf/virnwfilterobj.c +src/conf/virsavecookie.c src/conf/virsecretobj.c src/conf/virstorageobj.c src/cpu/cpu.c diff --git a/src/Makefile.am b/src/Makefile.am index 0b7cfc89a..eae32dc58 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -402,6 +402,7 @@ DOMAIN_CONF_SOURCES = \ conf/domain_conf.c conf/domain_conf.h \ conf/domain_audit.c conf/domain_audit.h \ conf/domain_nwfilter.c conf/domain_nwfilter.h \ + conf/virsavecookie.c conf/virsavecookie.h \ conf/snapshot_conf.c conf/snapshot_conf.h \ conf/numa_conf.c conf/numa_conf.h \ conf/virdomainobjlist.c conf/virdomainobjlist.h diff --git a/src/conf/virsavecookie.c b/src/conf/virsavecookie.c new file mode 100644 index 000000000..502c04d0f --- /dev/null +++ b/src/conf/virsavecookie.c @@ -0,0 +1,144 @@ +/** + * virsavecookie.c: Save cookie handling + * + * Copyright (C) 2017 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include "virerror.h" +#include "virlog.h" +#include "virobject.h" +#include "virbuffer.h" +#include "virxml.h" +#include "virsavecookie.h" + +#define VIR_FROM_THIS VIR_FROM_CONF + +VIR_LOG_INIT("conf.savecookie"); + + +static int +virSaveCookieParseNode(xmlXPathContextPtr ctxt, + virObjectPtr *obj, + virSaveCookieCallbacksPtr saveCookie) +{ + *obj = NULL; + + if (!xmlStrEqual(ctxt->node->name, BAD_CAST "cookie")) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("XML does not contain expected 'cookie' element")); + return -1; + } + + if (!saveCookie || !saveCookie->parse) + return 0; + + return saveCookie->parse(ctxt, obj); +} + + +int +virSaveCookieParse(xmlXPathContextPtr ctxt, + virObjectPtr *obj, + virSaveCookieCallbacksPtr saveCookie) +{ + xmlNodePtr node = ctxt->node; + int ret = -1; + + *obj = NULL; + + if (!(ctxt->node = virXPathNode("./cookie", ctxt))) { + ret = 0; + goto cleanup; + } + + ret = virSaveCookieParseNode(ctxt, obj, saveCookie); + + cleanup: + ctxt->node = node; + return ret; +} + + +int +virSaveCookieParseString(const char *xml, + virObjectPtr *obj, + virSaveCookieCallbacksPtr saveCookie) +{ + xmlDocPtr doc = NULL; + xmlXPathContextPtr ctxt = NULL; + int ret = -1; + + *obj = NULL; + + if (!xml) { + ret = 0; + goto cleanup; + } + + if (!(doc = virXMLParseStringCtxt(xml, _("(save cookie)"), &ctxt))) + goto cleanup; + + ret = virSaveCookieParseNode(ctxt, obj, saveCookie); + + cleanup: + xmlXPathFreeContext(ctxt); + xmlFreeDoc(doc); + return ret; +} + + +int +virSaveCookieFormatBuf(virBufferPtr buf, + virObjectPtr obj, + virSaveCookieCallbacksPtr saveCookie) +{ + if (!obj || !saveCookie || !saveCookie->format) + return 0; + + virBufferAddLit(buf, "<cookie>\n"); + virBufferAdjustIndent(buf, 2); + + if (saveCookie->format(buf, obj) < 0) + return -1; + + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</cookie>\n"); + + return 0; +} + + +char * +virSaveCookieFormat(virObjectPtr obj, + virSaveCookieCallbacksPtr saveCookie) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + + if (virSaveCookieFormatBuf(&buf, obj, saveCookie) < 0) + goto error; + + if (virBufferCheckError(&buf) < 0) + goto error; + + return virBufferContentAndReset(&buf); + + error: + virBufferFreeAndReset(&buf); + return NULL; +} diff --git a/src/conf/virsavecookie.h b/src/conf/virsavecookie.h new file mode 100644 index 000000000..4aed18466 --- /dev/null +++ b/src/conf/virsavecookie.h @@ -0,0 +1,62 @@ +/** + * virsavecookie.h: Save cookie handling + * + * Copyright (C) 2017 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + */ +#ifndef __VIR_SAVE_COOKIE_H__ +# define __VIR_SAVE_COOKIE_H__ + +# include <libxml/xpath.h> + +# include "internal.h" +# include "virobject.h" +# include "virbuffer.h" + + +typedef int (*virSaveCookieParseFunc)(xmlXPathContextPtr ctxt, + virObjectPtr *obj); +typedef int (*virSaveCookieFormatFunc)(virBufferPtr buf, + virObjectPtr obj); + +typedef struct _virSaveCookieCallbacks virSaveCookieCallbacks; +typedef virSaveCookieCallbacks *virSaveCookieCallbacksPtr; +struct _virSaveCookieCallbacks { + virSaveCookieParseFunc parse; + virSaveCookieFormatFunc format; +}; + + +int +virSaveCookieParse(xmlXPathContextPtr ctxt, + virObjectPtr *obj, + virSaveCookieCallbacksPtr saveCookie); + +int +virSaveCookieParseString(const char *xml, + virObjectPtr *obj, + virSaveCookieCallbacksPtr saveCookie); + +int +virSaveCookieFormatBuf(virBufferPtr buf, + virObjectPtr obj, + virSaveCookieCallbacksPtr saveCookie); + +char * +virSaveCookieFormat(virObjectPtr obj, + virSaveCookieCallbacksPtr saveCookie); + +#endif /*__VIR_SAVE_COOKIE_H__ */ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index bde01a3fa..ba4cecee1 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -990,6 +990,13 @@ virNWFilterObjUnlock; virNWFilterObjWantRemoved; +# conf/virsavecookie.h +virSaveCookieFormat; +virSaveCookieFormatBuf; +virSaveCookieParse; +virSaveCookieParseString; + + # conf/virsecretobj.h virSecretLoadAllConfigs; virSecretObjDeleteConfig; -- 2.13.0

virDomainXMLOption gains driver specific callbacks for parsing and formatting save cookies. Signed-off-by: Jiri Denemark <jdenemar@redhat.com> Reviewed-by: Pavel Hrdina <phrdina@redhat.com> --- Notes: Version 2: - no change src/bhyve/bhyve_domain.c | 2 +- src/conf/domain_conf.c | 16 +++++++++++++++- src/conf/domain_conf.h | 7 ++++++- src/conf/snapshot_conf.c | 11 ++++++++++- src/conf/snapshot_conf.h | 2 ++ src/libvirt_private.syms | 1 + src/libxl/libxl_conf.c | 2 +- src/lxc/lxc_conf.c | 2 +- src/openvz/openvz_driver.c | 2 +- src/phyp/phyp_driver.c | 2 +- src/qemu/qemu_capabilities.c | 2 +- src/qemu/qemu_conf.c | 3 ++- src/security/virt-aa-helper.c | 2 +- src/test/test_driver.c | 2 +- src/uml/uml_driver.c | 2 +- src/vbox/vbox_common.c | 2 +- src/vmware/vmware_driver.c | 3 ++- src/vmx/vmx.c | 2 +- src/vz/vz_driver.c | 2 +- src/xen/xen_driver.c | 2 +- src/xenapi/xenapi_driver.c | 2 +- tests/bhyveargv2xmltest.c | 2 +- tests/testutils.c | 2 +- 23 files changed, 54 insertions(+), 21 deletions(-) diff --git a/src/bhyve/bhyve_domain.c b/src/bhyve/bhyve_domain.c index 0a99550af..20c82937b 100644 --- a/src/bhyve/bhyve_domain.c +++ b/src/bhyve/bhyve_domain.c @@ -144,7 +144,7 @@ virBhyveDriverCreateXMLConf(bhyveConnPtr driver) virBhyveDriverDomainDefParserConfig.priv = driver; return virDomainXMLOptionNew(&virBhyveDriverDomainDefParserConfig, &virBhyveDriverPrivateDataCallbacks, - NULL, NULL); + NULL, NULL, NULL); } virDomainDefParserConfig virBhyveDriverDomainDefParserConfig = { diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 53c145f16..2e2e7334a 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -79,6 +79,9 @@ struct _virDomainXMLOption { /* ABI stability callbacks */ virDomainABIStability abi; + + /* Private data for save image stored in snapshot XML */ + virSaveCookieCallbacks saveCookie; }; #define VIR_DOMAIN_DEF_FORMAT_COMMON_FLAGS \ @@ -1054,7 +1057,8 @@ virDomainXMLOptionPtr virDomainXMLOptionNew(virDomainDefParserConfigPtr config, virDomainXMLPrivateDataCallbacksPtr priv, virDomainXMLNamespacePtr xmlns, - virDomainABIStabilityPtr abi) + virDomainABIStabilityPtr abi, + virSaveCookieCallbacksPtr saveCookie) { virDomainXMLOptionPtr xmlopt; @@ -1076,6 +1080,9 @@ virDomainXMLOptionNew(virDomainDefParserConfigPtr config, if (abi) xmlopt->abi = *abi; + if (saveCookie) + xmlopt->saveCookie = *saveCookie; + /* Technically this forbids to use one of Xerox's MAC address prefixes in * our hypervisor drivers. This shouldn't ever be a problem. * @@ -1106,6 +1113,13 @@ virDomainXMLOptionGetNamespace(virDomainXMLOptionPtr xmlopt) } +virSaveCookieCallbacksPtr +virDomainXMLOptionGetSaveCookie(virDomainXMLOptionPtr xmlopt) +{ + return &xmlopt->saveCookie; +} + + void virBlkioDeviceArrayClear(virBlkioDevicePtr devices, int ndevices) diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 446b117b7..1231aeac6 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -55,6 +55,7 @@ # include "virgic.h" # include "virperf.h" # include "virtypedparam.h" +# include "virsavecookie.h" /* forward declarations of all device types, required by * virDomainDeviceDef @@ -2549,7 +2550,11 @@ struct _virDomainABIStability { virDomainXMLOptionPtr virDomainXMLOptionNew(virDomainDefParserConfigPtr config, virDomainXMLPrivateDataCallbacksPtr priv, virDomainXMLNamespacePtr xmlns, - virDomainABIStabilityPtr abi); + virDomainABIStabilityPtr abi, + virSaveCookieCallbacksPtr saveCookie); + +virSaveCookieCallbacksPtr +virDomainXMLOptionGetSaveCookie(virDomainXMLOptionPtr xmlopt); void virDomainNetGenerateMAC(virDomainXMLOptionPtr xmlopt, virMacAddrPtr mac); diff --git a/src/conf/snapshot_conf.c b/src/conf/snapshot_conf.c index e3bba985d..6330f7d1f 100644 --- a/src/conf/snapshot_conf.c +++ b/src/conf/snapshot_conf.c @@ -102,6 +102,7 @@ void virDomainSnapshotDefFree(virDomainSnapshotDefPtr def) virDomainSnapshotDiskDefClear(&def->disks[i]); VIR_FREE(def->disks); virDomainDefFree(def->dom); + virObjectUnref(def->cookie); VIR_FREE(def); } @@ -214,6 +215,7 @@ virDomainSnapshotDefParse(xmlXPathContextPtr ctxt, char *memorySnapshot = NULL; char *memoryFile = NULL; bool offline = !!(flags & VIR_DOMAIN_SNAPSHOT_PARSE_OFFLINE); + virSaveCookieCallbacksPtr saveCookie = virDomainXMLOptionGetSaveCookie(xmlopt); if (VIR_ALLOC(def) < 0) goto cleanup; @@ -365,6 +367,9 @@ virDomainSnapshotDefParse(xmlXPathContextPtr ctxt, def->current = active != 0; } + if (!offline && virSaveCookieParse(ctxt, &def->cookie, saveCookie) < 0) + goto cleanup; + ret = def; cleanup: @@ -691,7 +696,7 @@ char * virDomainSnapshotDefFormat(const char *domain_uuid, virDomainSnapshotDefPtr def, virCapsPtr caps, - virDomainXMLOptionPtr xmlopt ATTRIBUTE_UNUSED, + virDomainXMLOptionPtr xmlopt, unsigned int flags, int internal) { @@ -751,6 +756,10 @@ virDomainSnapshotDefFormat(const char *domain_uuid, virBufferAddLit(&buf, "</domain>\n"); } + if (virSaveCookieFormatBuf(&buf, def->cookie, + virDomainXMLOptionGetSaveCookie(xmlopt)) < 0) + goto error; + if (internal) virBufferAsprintf(&buf, "<active>%d</active>\n", def->current); diff --git a/src/conf/snapshot_conf.h b/src/conf/snapshot_conf.h index 2ce526fa6..1d663c77b 100644 --- a/src/conf/snapshot_conf.h +++ b/src/conf/snapshot_conf.h @@ -76,6 +76,8 @@ struct _virDomainSnapshotDef { virDomainDefPtr dom; + virObjectPtr cookie; + /* Internal use. */ bool current; /* At most one snapshot in the list should have this set */ }; diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index ba4cecee1..003db6b65 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -535,6 +535,7 @@ virDomainWatchdogActionTypeToString; virDomainWatchdogModelTypeFromString; virDomainWatchdogModelTypeToString; virDomainXMLOptionGetNamespace; +virDomainXMLOptionGetSaveCookie; virDomainXMLOptionNew; diff --git a/src/libxl/libxl_conf.c b/src/libxl/libxl_conf.c index 886dc629f..04d9dd1bd 100644 --- a/src/libxl/libxl_conf.c +++ b/src/libxl/libxl_conf.c @@ -2255,5 +2255,5 @@ libxlCreateXMLConf(void) { return virDomainXMLOptionNew(&libxlDomainDefParserConfig, &libxlDomainXMLPrivateDataCallbacks, - NULL, NULL); + NULL, NULL, NULL); } diff --git a/src/lxc/lxc_conf.c b/src/lxc/lxc_conf.c index ff975decc..92a82a476 100644 --- a/src/lxc/lxc_conf.c +++ b/src/lxc/lxc_conf.c @@ -216,7 +216,7 @@ lxcDomainXMLConfInit(void) return virDomainXMLOptionNew(&virLXCDriverDomainDefParserConfig, &virLXCDriverPrivateDataCallbacks, &virLXCDriverDomainXMLNamespace, - NULL); + NULL, NULL); } diff --git a/src/openvz/openvz_driver.c b/src/openvz/openvz_driver.c index 8e305a85c..a1485fc88 100644 --- a/src/openvz/openvz_driver.c +++ b/src/openvz/openvz_driver.c @@ -1483,7 +1483,7 @@ static virDrvOpenStatus openvzConnectOpen(virConnectPtr conn, goto cleanup; if (!(driver->xmlopt = virDomainXMLOptionNew(&openvzDomainDefParserConfig, - NULL, NULL, NULL))) + NULL, NULL, NULL, NULL))) goto cleanup; if (openvzLoadDomains(driver) < 0) diff --git a/src/phyp/phyp_driver.c b/src/phyp/phyp_driver.c index 4465ac862..9121581fe 100644 --- a/src/phyp/phyp_driver.c +++ b/src/phyp/phyp_driver.c @@ -1202,7 +1202,7 @@ phypConnectOpen(virConnectPtr conn, goto failure; if (!(phyp_driver->xmlopt = virDomainXMLOptionNew(&virPhypDriverDomainDefParserConfig, - NULL, NULL, NULL))) + NULL, NULL, NULL, NULL))) goto failure; conn->privateData = phyp_driver; diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index 03c55853f..7f2249259 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -5026,7 +5026,7 @@ virQEMUCapsInitQMPCommandRun(virQEMUCapsInitQMPCommandPtr cmd, goto ignore; } - if (!(xmlopt = virDomainXMLOptionNew(NULL, NULL, NULL, NULL)) || + if (!(xmlopt = virDomainXMLOptionNew(NULL, NULL, NULL, NULL, NULL)) || !(cmd->vm = virDomainObjNew(xmlopt))) goto cleanup; diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index d8b88386d..d02e776b0 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -910,7 +910,8 @@ virQEMUDriverCreateXMLConf(virQEMUDriverPtr driver) return virDomainXMLOptionNew(&virQEMUDriverDomainDefParserConfig, &virQEMUDriverPrivateDataCallbacks, &virQEMUDriverDomainXMLNamespace, - &virQEMUDriverDomainABIStability); + &virQEMUDriverDomainABIStability, + NULL); } diff --git a/src/security/virt-aa-helper.c b/src/security/virt-aa-helper.c index 48201d5b8..97436e5dc 100644 --- a/src/security/virt-aa-helper.c +++ b/src/security/virt-aa-helper.c @@ -667,7 +667,7 @@ get_definition(vahControl * ctl, const char *xmlStr) goto exit; } - if (!(ctl->xmlopt = virDomainXMLOptionNew(NULL, NULL, NULL, NULL))) { + if (!(ctl->xmlopt = virDomainXMLOptionNew(NULL, NULL, NULL, NULL, NULL))) { vah_error(ctl, 0, _("Failed to create XML config object")); goto exit; } diff --git a/src/test/test_driver.c b/src/test/test_driver.c index 8cecc6add..11e7fd880 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -414,7 +414,7 @@ testDriverNew(void) goto error; } - if (!(ret->xmlopt = virDomainXMLOptionNew(NULL, NULL, &ns, NULL)) || + if (!(ret->xmlopt = virDomainXMLOptionNew(NULL, NULL, &ns, NULL, NULL)) || !(ret->eventState = virObjectEventStateNew()) || !(ret->ifaces = virInterfaceObjListNew()) || !(ret->domains = virDomainObjListNew()) || diff --git a/src/uml/uml_driver.c b/src/uml/uml_driver.c index 58ab033c8..080fea47d 100644 --- a/src/uml/uml_driver.c +++ b/src/uml/uml_driver.c @@ -533,7 +533,7 @@ umlStateInitialize(bool privileged, goto out_of_memory; if (!(uml_driver->xmlopt = virDomainXMLOptionNew(¨DriverDomainDefParserConfig, - &privcb, NULL, NULL))) + &privcb, NULL, NULL, NULL))) goto error; if ((uml_driver->inotifyFD = inotify_init()) < 0) { diff --git a/src/vbox/vbox_common.c b/src/vbox/vbox_common.c index 1a90d00aa..c46e71bcf 100644 --- a/src/vbox/vbox_common.c +++ b/src/vbox/vbox_common.c @@ -142,7 +142,7 @@ vboxDriverObjNew(void) if (!(driver->caps = vboxCapsInit()) || !(driver->xmlopt = virDomainXMLOptionNew(&vboxDomainDefParserConfig, - NULL, NULL, NULL))) + NULL, NULL, NULL, NULL))) goto cleanup; return driver; diff --git a/src/vmware/vmware_driver.c b/src/vmware/vmware_driver.c index 9e369e67b..0ee1c5bb9 100644 --- a/src/vmware/vmware_driver.c +++ b/src/vmware/vmware_driver.c @@ -114,7 +114,8 @@ vmwareDomainXMLConfigInit(void) virDomainXMLPrivateDataCallbacks priv = { .alloc = vmwareDataAllocFunc, .free = vmwareDataFreeFunc }; - return virDomainXMLOptionNew(&vmwareDomainDefParserConfig, &priv, NULL, NULL); + return virDomainXMLOptionNew(&vmwareDomainDefParserConfig, &priv, + NULL, NULL, NULL); } static virDrvOpenStatus diff --git a/src/vmx/vmx.c b/src/vmx/vmx.c index 3289a2002..96507f10f 100644 --- a/src/vmx/vmx.c +++ b/src/vmx/vmx.c @@ -591,7 +591,7 @@ virDomainXMLOptionPtr virVMXDomainXMLConfInit(void) { return virDomainXMLOptionNew(&virVMXDomainDefParserConfig, NULL, - &virVMXDomainXMLNamespace, NULL); + &virVMXDomainXMLNamespace, NULL, NULL); } char * diff --git a/src/vz/vz_driver.c b/src/vz/vz_driver.c index e6bb20182..7aa0c4c48 100644 --- a/src/vz/vz_driver.c +++ b/src/vz/vz_driver.c @@ -328,7 +328,7 @@ vzDriverObjNew(void) if (!(driver->caps = vzBuildCapabilities()) || !(driver->xmlopt = virDomainXMLOptionNew(&vzDomainDefParserConfig, &vzDomainXMLPrivateDataCallbacksPtr, - NULL, NULL)) || + NULL, NULL, NULL)) || !(driver->domains = virDomainObjListNew()) || !(driver->domainEventState = virObjectEventStateNew()) || (vzInitVersion(driver) < 0) || diff --git a/src/xen/xen_driver.c b/src/xen/xen_driver.c index f81ee20ad..8e7bc350c 100644 --- a/src/xen/xen_driver.c +++ b/src/xen/xen_driver.c @@ -401,7 +401,7 @@ virDomainXMLOptionPtr xenDomainXMLConfInit(void) { return virDomainXMLOptionNew(&xenDomainDefParserConfig, - NULL, NULL, NULL); + NULL, NULL, NULL, NULL); } diff --git a/src/xenapi/xenapi_driver.c b/src/xenapi/xenapi_driver.c index 380c3a1de..fb462cd3a 100644 --- a/src/xenapi/xenapi_driver.c +++ b/src/xenapi/xenapi_driver.c @@ -200,7 +200,7 @@ xenapiConnectOpen(virConnectPtr conn, virConnectAuthPtr auth, } if (!(privP->xmlopt = virDomainXMLOptionNew(&xenapiDomainDefParserConfig, - NULL, NULL, NULL))) { + NULL, NULL, NULL, NULL))) { xenapiSessionErrorHandler(conn, VIR_ERR_INTERNAL_ERROR, _("Failed to create XML conf object")); goto error; diff --git a/tests/bhyveargv2xmltest.c b/tests/bhyveargv2xmltest.c index 9db3750ac..6a5685115 100644 --- a/tests/bhyveargv2xmltest.c +++ b/tests/bhyveargv2xmltest.c @@ -131,7 +131,7 @@ mymain(void) return EXIT_FAILURE; if ((driver.xmlopt = virDomainXMLOptionNew(NULL, NULL, - NULL, NULL)) == NULL) + NULL, NULL, NULL)) == NULL) return EXIT_FAILURE; # define DO_TEST_FULL(name, flags) \ diff --git a/tests/testutils.c b/tests/testutils.c index 4b8cf79ef..f45596997 100644 --- a/tests/testutils.c +++ b/tests/testutils.c @@ -1136,7 +1136,7 @@ virDomainXMLOptionPtr virTestGenericDomainXMLConfInit(void) { return virDomainXMLOptionNew(&virTestGenericDomainDefParserConfig, &virTestGenericPrivateDataCallbacks, - NULL, NULL); + NULL, NULL, NULL); } -- 2.13.0

This patch implements a new save cookie object and callbacks for qemu driver. The actual useful content will be added in the object later. Signed-off-by: Jiri Denemark <jdenemar@redhat.com> Reviewed-by: Pavel Hrdina <phrdina@redhat.com> --- Notes: Version 2: - no change src/qemu/qemu_conf.c | 2 +- src/qemu/qemu_domain.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++++-- src/qemu/qemu_domain.h | 9 ++++++ 3 files changed, 86 insertions(+), 4 deletions(-) diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index d02e776b0..73c33d678 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -911,7 +911,7 @@ virQEMUDriverCreateXMLConf(virQEMUDriverPtr driver) &virQEMUDriverPrivateDataCallbacks, &virQEMUDriverDomainXMLNamespace, &virQEMUDriverDomainABIStability, - NULL); + &virQEMUDriverDomainSaveCookie); } diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 814232164..80e9fea98 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -122,11 +122,13 @@ struct _qemuDomainLogContext { }; static virClassPtr qemuDomainLogContextClass; +static virClassPtr qemuDomainSaveCookieClass; static void qemuDomainLogContextDispose(void *obj); +static void qemuDomainSaveCookieDispose(void *obj); static int -qemuDomainLogContextOnceInit(void) +qemuDomainOnceInit(void) { if (!(qemuDomainLogContextClass = virClassNew(virClassForObject(), "qemuDomainLogContext", @@ -134,10 +136,16 @@ qemuDomainLogContextOnceInit(void) qemuDomainLogContextDispose))) return -1; + if (!(qemuDomainSaveCookieClass = virClassNew(virClassForObject(), + "qemuDomainSaveCookie", + sizeof(qemuDomainSaveCookie), + qemuDomainSaveCookieDispose))) + return -1; + return 0; } -VIR_ONCE_GLOBAL_INIT(qemuDomainLogContext) +VIR_ONCE_GLOBAL_INIT(qemuDomain) static void qemuDomainLogContextDispose(void *obj) @@ -4554,7 +4562,7 @@ qemuDomainLogContextPtr qemuDomainLogContextNew(virQEMUDriverPtr driver, virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); qemuDomainLogContextPtr ctxt = NULL; - if (qemuDomainLogContextInitialize() < 0) + if (qemuDomainInitialize() < 0) goto cleanup; if (!(ctxt = virObjectNew(qemuDomainLogContextClass))) @@ -9105,3 +9113,68 @@ qemuDomainGetStorageSourceByDevstr(const char *devstr, VIR_FREE(target); return src; } + + +static void +qemuDomainSaveCookieDispose(void *obj) +{ + qemuDomainSaveCookiePtr cookie = obj; + + VIR_DEBUG("cookie=%p", cookie); +} + + +qemuDomainSaveCookiePtr +qemuDomainSaveCookieNew(virDomainObjPtr vm ATTRIBUTE_UNUSED) +{ + qemuDomainSaveCookiePtr cookie = NULL; + + if (qemuDomainInitialize() < 0) + goto error; + + if (!(cookie = virObjectNew(qemuDomainSaveCookieClass))) + goto error; + + VIR_DEBUG("Save cookie %p", cookie); + + return cookie; + + error: + virObjectUnref(cookie); + return NULL; +} + + +static int +qemuDomainSaveCookieParse(xmlXPathContextPtr ctxt ATTRIBUTE_UNUSED, + virObjectPtr *obj) +{ + qemuDomainSaveCookiePtr cookie = NULL; + + if (qemuDomainInitialize() < 0) + goto error; + + if (!(cookie = virObjectNew(qemuDomainSaveCookieClass))) + goto error; + + *obj = (virObjectPtr) cookie; + return 0; + + error: + virObjectUnref(cookie); + return -1; +} + + +static int +qemuDomainSaveCookieFormat(virBufferPtr buf ATTRIBUTE_UNUSED, + virObjectPtr obj ATTRIBUTE_UNUSED) +{ + return 0; +} + + +virSaveCookieCallbacks virQEMUDriverDomainSaveCookie = { + .parse = qemuDomainSaveCookieParse, + .format = qemuDomainSaveCookieFormat, +}; diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index d0e2e0628..cc2e21bdf 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -416,6 +416,14 @@ struct qemuProcessEvent { typedef struct _qemuDomainLogContext qemuDomainLogContext; typedef qemuDomainLogContext *qemuDomainLogContextPtr; +typedef struct _qemuDomainSaveCookie qemuDomainSaveCookie; +typedef qemuDomainSaveCookie *qemuDomainSaveCookiePtr; +struct _qemuDomainSaveCookie { + virObject parent; +}; + +qemuDomainSaveCookiePtr qemuDomainSaveCookieNew(virDomainObjPtr vm); + const char *qemuDomainAsyncJobPhaseToString(qemuDomainAsyncJob job, int phase); int qemuDomainAsyncJobPhaseFromString(qemuDomainAsyncJob job, @@ -638,6 +646,7 @@ extern virDomainXMLPrivateDataCallbacks virQEMUDriverPrivateDataCallbacks; extern virDomainXMLNamespace virQEMUDriverDomainXMLNamespace; extern virDomainDefParserConfig virQEMUDriverDomainDefParserConfig; extern virDomainABIStability virQEMUDriverDomainABIStability; +extern virSaveCookieCallbacks virQEMUDriverDomainSaveCookie; int qemuDomainUpdateDeviceList(virQEMUDriverPtr driver, virDomainObjPtr vm, int asyncJob); -- 2.13.0

The following patches will add an actual content in the cookie and use the data when restoring a domain. Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- Notes: Version 2: - renamed virQEMUSaveHeader.cookie as cookieOffset - renamed virQEMUSaveDataNew's cookie parameter as cookieObj - store zeros at the end of header data rather than between domain XML and cookie docs/formatsnapshot.html.in | 6 +++ docs/schemas/domainsnapshot.rng | 7 +++ src/qemu/qemu_driver.c | 96 ++++++++++++++++++++++++++++++++++++----- 3 files changed, 99 insertions(+), 10 deletions(-) diff --git a/docs/formatsnapshot.html.in b/docs/formatsnapshot.html.in index c3ab516fa..5e8e21c8a 100644 --- a/docs/formatsnapshot.html.in +++ b/docs/formatsnapshot.html.in @@ -235,6 +235,12 @@ at the time of the snapshot (<span class="since">since 0.9.5</span>). Readonly. </dd> + <dt><code>cookie</code></dt> + <dd>Save image cookie containing additional data libvirt may need to + properly restore a domain from an active snapshot when such data + cannot be stored directly in the <code>domain</code> to maintain + compatibility with older libvirt or hypervisor. Readonly. + </dd> </dl> <h2><a name="example">Examples</a></h2> diff --git a/docs/schemas/domainsnapshot.rng b/docs/schemas/domainsnapshot.rng index 4ab1b828f..268088709 100644 --- a/docs/schemas/domainsnapshot.rng +++ b/docs/schemas/domainsnapshot.rng @@ -90,6 +90,13 @@ </element> </element> </optional> + <optional> + <element name='cookie'> + <zeroOrMore> + <ref name='customElement'/> + </zeroOrMore> + </element> + </optional> </interleave> </element> </define> diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index dba0d4025..5de9513aa 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -2808,7 +2808,8 @@ struct _virQEMUSaveHeader { uint32_t data_len; uint32_t was_running; uint32_t compressed; - uint32_t unused[15]; + uint32_t cookieOffset; + uint32_t unused[14]; }; typedef struct _virQEMUSaveData virQEMUSaveData; @@ -2816,6 +2817,7 @@ typedef virQEMUSaveData *virQEMUSaveDataPtr; struct _virQEMUSaveData { virQEMUSaveHeader header; char *xml; + char *cookie; }; @@ -2826,6 +2828,7 @@ bswap_header(virQEMUSaveHeaderPtr hdr) hdr->data_len = bswap_32(hdr->data_len); hdr->was_running = bswap_32(hdr->was_running); hdr->compressed = bswap_32(hdr->compressed); + hdr->cookieOffset = bswap_32(hdr->cookieOffset); } @@ -2836,6 +2839,7 @@ virQEMUSaveDataFree(virQEMUSaveDataPtr data) return; VIR_FREE(data->xml); + VIR_FREE(data->cookie); VIR_FREE(data); } @@ -2845,8 +2849,10 @@ virQEMUSaveDataFree(virQEMUSaveDataPtr data) */ static virQEMUSaveDataPtr virQEMUSaveDataNew(char *domXML, + qemuDomainSaveCookiePtr cookieObj, bool running, - int compressed) + int compressed, + virDomainXMLOptionPtr xmlopt) { virQEMUSaveDataPtr data = NULL; virQEMUSaveHeaderPtr header; @@ -2856,6 +2862,11 @@ virQEMUSaveDataNew(char *domXML, VIR_STEAL_PTR(data->xml, domXML); + if (cookieObj && + !(data->cookie = virSaveCookieFormat((virObjectPtr) cookieObj, + virDomainXMLOptionGetSaveCookie(xmlopt)))) + goto error; + header = &data->header; memcpy(header->magic, QEMU_SAVE_PARTIAL, sizeof(header->magic)); header->version = QEMU_SAVE_VERSION; @@ -2863,6 +2874,10 @@ virQEMUSaveDataNew(char *domXML, header->compressed = compressed; return data; + + error: + virQEMUSaveDataFree(data); + return NULL; } @@ -2882,11 +2897,17 @@ virQEMUSaveDataWrite(virQEMUSaveDataPtr data, { virQEMUSaveHeaderPtr header = &data->header; size_t len; + size_t xml_len; + size_t cookie_len = 0; int ret = -1; size_t zerosLen = 0; char *zeros = NULL; - len = strlen(data->xml) + 1; + xml_len = strlen(data->xml) + 1; + if (data->cookie) + cookie_len = strlen(data->cookie) + 1; + + len = xml_len + cookie_len; if (header->data_len > 0) { if (len > header->data_len) { @@ -2902,6 +2923,9 @@ virQEMUSaveDataWrite(virQEMUSaveDataPtr data, header->data_len = len; } + if (data->cookie) + header->cookieOffset = xml_len; + if (safewrite(fd, header, sizeof(*header)) != sizeof(*header)) { virReportSystemError(errno, _("failed to write header to domain save file '%s'"), @@ -2909,14 +2933,28 @@ virQEMUSaveDataWrite(virQEMUSaveDataPtr data, goto cleanup; } - if (safewrite(fd, data->xml, header->data_len) != header->data_len || - safewrite(fd, zeros, zerosLen) != zerosLen) { + if (safewrite(fd, data->xml, xml_len) != xml_len) { virReportSystemError(errno, _("failed to write domain xml to '%s'"), path); goto cleanup; } + if (data->cookie && + safewrite(fd, data->cookie, cookie_len) != cookie_len) { + virReportSystemError(errno, + _("failed to write cookie to '%s'"), + path); + goto cleanup; + } + + if (safewrite(fd, zeros, zerosLen) != zerosLen) { + virReportSystemError(errno, + _("failed to write padding to '%s'"), + path); + goto cleanup; + } + ret = 0; cleanup: @@ -3250,6 +3288,7 @@ qemuDomainSaveInternal(virQEMUDriverPtr driver, virDomainPtr dom, qemuDomainObjPrivatePtr priv = vm->privateData; virCapsPtr caps; virQEMUSaveDataPtr data = NULL; + qemuDomainSaveCookiePtr cookie = NULL; if (!(caps = virQEMUDriverGetCapabilities(driver, false))) goto cleanup; @@ -3315,7 +3354,11 @@ qemuDomainSaveInternal(virQEMUDriverPtr driver, virDomainPtr dom, goto endjob; } - if (!(data = virQEMUSaveDataNew(xml, was_running, compressed))) + if (!(cookie = qemuDomainSaveCookieNew(vm))) + goto endjob; + + if (!(data = virQEMUSaveDataNew(xml, cookie, was_running, compressed, + driver->xmlopt))) goto endjob; xml = NULL; @@ -3352,6 +3395,7 @@ qemuDomainSaveInternal(virQEMUDriverPtr driver, virDomainPtr dom, qemuDomainRemoveInactive(driver, vm); cleanup: + virObjectUnref(cookie); VIR_FREE(xml); virQEMUSaveDataFree(data); qemuDomainEventQueue(driver, event); @@ -6288,6 +6332,8 @@ qemuDomainSaveImageOpen(virQEMUDriverPtr driver, virDomainDefPtr def = NULL; int oflags = open_write ? O_RDWR : O_RDONLY; virCapsPtr caps = NULL; + size_t xml_len; + size_t cookie_len; if (bypass_cache) { int directFlag = virFileDirectFdFlag(); @@ -6368,15 +6414,33 @@ qemuDomainSaveImageOpen(virQEMUDriverPtr driver, goto error; } - if (VIR_ALLOC_N(data->xml, header->data_len) < 0) + if (header->cookieOffset) + xml_len = header->cookieOffset; + else + xml_len = header->data_len; + + cookie_len = header->data_len - xml_len; + + if (VIR_ALLOC_N(data->xml, xml_len) < 0) goto error; - if (saferead(fd, data->xml, header->data_len) != header->data_len) { + if (saferead(fd, data->xml, xml_len) != xml_len) { virReportError(VIR_ERR_OPERATION_FAILED, "%s", _("failed to read domain XML")); goto error; } + if (cookie_len > 0) { + if (VIR_ALLOC_N(data->cookie, cookie_len) < 0) + goto error; + + if (saferead(fd, data->cookie, cookie_len) != cookie_len) { + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("failed to read cookie")); + goto error; + } + } + /* Create a domain from this XML */ if (!(def = virDomainDefParseString(data->xml, caps, driver->xmlopt, NULL, VIR_DOMAIN_DEF_PARSE_INACTIVE | @@ -6417,6 +6481,11 @@ qemuDomainSaveImageStartVM(virConnectPtr conn, char *errbuf = NULL; virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); virQEMUSaveHeaderPtr header = &data->header; + qemuDomainSaveCookiePtr cookie = NULL; + + if (virSaveCookieParseString(data->cookie, (virObjectPtr *) &cookie, + virDomainXMLOptionGetSaveCookie(driver->xmlopt)) < 0) + goto cleanup; if ((header->version == 2) && (header->compressed != QEMU_SAVE_FORMAT_RAW)) { @@ -6507,6 +6576,7 @@ qemuDomainSaveImageStartVM(virConnectPtr conn, ret = 0; cleanup: + virObjectUnref(cookie); virCommandFree(cmd); VIR_FREE(errbuf); if (qemuSecurityRestoreSavedStateLabel(driver->securityManager, @@ -13559,6 +13629,9 @@ qemuDomainSnapshotCreateActiveInternal(virConnectPtr conn, if (ret < 0) goto cleanup; + if (!(snap->def->cookie = (virObjectPtr) qemuDomainSaveCookieNew(vm))) + goto cleanup; + if (flags & VIR_DOMAIN_SNAPSHOT_CREATE_HALT) { event = virDomainEventLifecycleNewFromObj(vm, VIR_DOMAIN_EVENT_STOPPED, VIR_DOMAIN_EVENT_STOPPED_FROM_SNAPSHOT); @@ -14438,10 +14511,13 @@ qemuDomainSnapshotCreateActiveExternal(virConnectPtr conn, "snapshot", false)) < 0) goto cleanup; - if (!(xml = qemuDomainDefFormatLive(driver, vm->def, true, true))) + if (!(xml = qemuDomainDefFormatLive(driver, vm->def, true, true)) || + !(snap->def->cookie = (virObjectPtr) qemuDomainSaveCookieNew(vm))) goto cleanup; - if (!(data = virQEMUSaveDataNew(xml, resume, compressed))) + if (!(data = virQEMUSaveDataNew(xml, + (qemuDomainSaveCookiePtr) snap->def->cookie, + resume, compressed, driver->xmlopt))) goto cleanup; xml = NULL; -- 2.13.0

On Wed, Jun 07, 2017 at 10:37:39AM +0200, Jiri Denemark wrote:
The following patches will add an actual content in the cookie and use the data when restoring a domain.
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> ---
Notes: Version 2: - renamed virQEMUSaveHeader.cookie as cookieOffset - renamed virQEMUSaveDataNew's cookie parameter as cookieObj - store zeros at the end of header data rather than between domain XML and cookie
docs/formatsnapshot.html.in | 6 +++ docs/schemas/domainsnapshot.rng | 7 +++ src/qemu/qemu_driver.c | 96 ++++++++++++++++++++++++++++++++++++----- 3 files changed, 99 insertions(+), 10 deletions(-)
Reviewed-by: Pavel Hrdina <phrdina@redhat.com>

When starting a domain we update the guest CPU definition to match what QEMU actually provided (since it is allowed to add or removed some features unless check='full' is specified). Let's store the original CPU in domain private data so that we can use it to provide a backward compatible domain XML. Signed-off-by: Jiri Denemark <jdenemar@redhat.com> Reviewed-by: Pavel Hrdina <phrdina@redhat.com> --- Notes: Version 2: - no change src/libvirt_private.syms | 2 ++ src/qemu/qemu_domain.c | 7 +++++++ src/qemu/qemu_domain.h | 4 ++++ src/qemu/qemu_process.c | 13 +++++++++++-- 4 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 003db6b65..b6c828fc2 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -77,9 +77,11 @@ virCPUDefCopyModelFilter; virCPUDefCopyWithoutModel; virCPUDefFormat; virCPUDefFormatBuf; +virCPUDefFormatBufFull; virCPUDefFree; virCPUDefFreeFeatures; virCPUDefFreeModel; +virCPUDefIsEqual; virCPUDefParseXML; virCPUDefStealModel; virCPUDefUpdateFeature; diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 80e9fea98..ba8079ba0 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -1726,6 +1726,8 @@ qemuDomainObjPrivateFree(void *data) VIR_FREE(priv->migTLSAlias); qemuDomainMasterKeyFree(priv); + virCPUDefFree(priv->origCPU); + VIR_FREE(priv); } @@ -1881,6 +1883,8 @@ qemuDomainObjPrivateXMLFormat(virBufferPtr buf, virBufferEscapeString(buf, "<channelTargetDir path='%s'/>\n", priv->channelTargetDir); + virCPUDefFormatBufFull(buf, priv->origCPU, NULL, false); + return 0; } @@ -2149,6 +2153,9 @@ qemuDomainObjPrivateXMLParse(xmlXPathContextPtr ctxt, if (qemuDomainSetPrivatePathsOld(driver, vm) < 0) goto error; + if (virCPUDefParseXML(ctxt, "./cpu", VIR_CPU_TYPE_GUEST, &priv->origCPU) < 0) + goto error; + return 0; error: diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index cc2e21bdf..f6dad4378 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -293,6 +293,10 @@ struct _qemuDomainObjPrivate { /* Used when fetching/storing the current 'tls-creds' migration setting */ /* (not to be saved in our private XML). */ char *migTLSAlias; + + /* CPU def used to start the domain when it differs from the one actually + * provided by QEMU. */ + virCPUDefPtr origCPU; }; # define QEMU_DOMAIN_PRIVATE(vm) \ diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 32ba8e373..c06349474 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -3915,6 +3915,7 @@ qemuProcessUpdateLiveGuestCPU(virQEMUDriverPtr driver, qemuDomainObjPrivatePtr priv = vm->privateData; int rc; int ret = -1; + virCPUDefPtr orig = NULL; if (ARCH_IS_X86(def->os.arch)) { if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) < 0) @@ -3945,10 +3946,17 @@ qemuProcessUpdateLiveGuestCPU(virQEMUDriverPtr driver, if (qemuProcessVerifyCPUFeatures(def, cpu) < 0) goto cleanup; - if ((rc = virCPUUpdateLive(def->os.arch, def->cpu, cpu, disabled)) < 0) + if (!(orig = virCPUDefCopy(def->cpu))) goto cleanup; - else if (rc == 0) + + if ((rc = virCPUUpdateLive(def->os.arch, def->cpu, cpu, disabled)) < 0) { + goto cleanup; + } else if (rc == 0) { + if (!virCPUDefIsEqual(def->cpu, orig, false)) + VIR_STEAL_PTR(priv->origCPU, orig); + def->cpu->check = VIR_CPU_CHECK_FULL; + } } ret = 0; @@ -3956,6 +3964,7 @@ qemuProcessUpdateLiveGuestCPU(virQEMUDriverPtr driver, cleanup: virCPUDataFree(cpu); virCPUDataFree(disabled); + virCPUDefFree(orig); return ret; } -- 2.13.0

The destination host may not be able to start a domain using the live updated CPU definition because either libvirt or QEMU may not be new enough. Thus we need to send the original guest CPU definition. Signed-off-by: Jiri Denemark <jdenemar@redhat.com> Reviewed-by: Pavel Hrdina <phrdina@redhat.com> --- Notes: Version 2: - no change src/qemu/qemu_domain.c | 61 ++++++++++++++++++++++++++++++++++++++--------- src/qemu/qemu_domain.h | 1 + src/qemu/qemu_driver.c | 13 ++++++---- src/qemu/qemu_migration.c | 5 ++-- 4 files changed, 63 insertions(+), 17 deletions(-) diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index ba8079ba0..0da44f066 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -4216,11 +4216,13 @@ qemuDomainDefCopy(virQEMUDriverPtr driver, return ret; } -int -qemuDomainDefFormatBuf(virQEMUDriverPtr driver, - virDomainDefPtr def, - unsigned int flags, - virBuffer *buf) + +static int +qemuDomainDefFormatBufInternal(virQEMUDriverPtr driver, + virDomainDefPtr def, + virCPUDefPtr origCPU, + unsigned int flags, + virBuffer *buf) { int ret = -1; virDomainDefPtr copy = NULL; @@ -4341,6 +4343,16 @@ qemuDomainDefFormatBuf(virQEMUDriverPtr driver, if (qemuDomainChrDefDropDefaultPath(def->channels[i], driver) < 0) goto cleanup; } + + /* Replace the CPU definition updated according to QEMU with the one + * used for starting the domain. The updated def will be sent + * separately for backward compatibility. + */ + if (origCPU) { + virCPUDefFree(def->cpu); + if (!(def->cpu = virCPUDefCopy(origCPU))) + goto cleanup; + } } format: @@ -4354,13 +4366,26 @@ qemuDomainDefFormatBuf(virQEMUDriverPtr driver, return ret; } -char *qemuDomainDefFormatXML(virQEMUDriverPtr driver, - virDomainDefPtr def, - unsigned int flags) + +int +qemuDomainDefFormatBuf(virQEMUDriverPtr driver, + virDomainDefPtr def, + unsigned int flags, + virBufferPtr buf) +{ + return qemuDomainDefFormatBufInternal(driver, def, NULL, flags, buf); +} + + +static char * +qemuDomainDefFormatXMLInternal(virQEMUDriverPtr driver, + virDomainDefPtr def, + virCPUDefPtr origCPU, + unsigned int flags) { virBuffer buf = VIR_BUFFER_INITIALIZER; - if (qemuDomainDefFormatBuf(driver, def, flags, &buf) < 0) { + if (qemuDomainDefFormatBufInternal(driver, def, origCPU, flags, &buf) < 0) { virBufferFreeAndReset(&buf); return NULL; } @@ -4374,26 +4399,40 @@ char *qemuDomainDefFormatXML(virQEMUDriverPtr driver, return virBufferContentAndReset(&buf); } + +char * +qemuDomainDefFormatXML(virQEMUDriverPtr driver, + virDomainDefPtr def, + unsigned int flags) +{ + return qemuDomainDefFormatXMLInternal(driver, def, NULL, flags); +} + + char *qemuDomainFormatXML(virQEMUDriverPtr driver, virDomainObjPtr vm, unsigned int flags) { virDomainDefPtr def; + qemuDomainObjPrivatePtr priv = vm->privateData; + virCPUDefPtr origCPU = NULL; if ((flags & VIR_DOMAIN_XML_INACTIVE) && vm->newDef) { def = vm->newDef; } else { def = vm->def; + origCPU = priv->origCPU; if (virDomainObjIsActive(vm)) flags &= ~VIR_DOMAIN_XML_UPDATE_CPU; } - return qemuDomainDefFormatXML(driver, def, flags); + return qemuDomainDefFormatXMLInternal(driver, def, origCPU, flags); } char * qemuDomainDefFormatLive(virQEMUDriverPtr driver, virDomainDefPtr def, + virCPUDefPtr origCPU, bool inactive, bool compatible) { @@ -4404,7 +4443,7 @@ qemuDomainDefFormatLive(virQEMUDriverPtr driver, if (compatible) flags |= VIR_DOMAIN_XML_MIGRATABLE; - return qemuDomainDefFormatXML(driver, def, flags); + return qemuDomainDefFormatXMLInternal(driver, def, origCPU, flags); } diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index f6dad4378..08c0b32f1 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -515,6 +515,7 @@ char *qemuDomainFormatXML(virQEMUDriverPtr driver, char *qemuDomainDefFormatLive(virQEMUDriverPtr driver, virDomainDefPtr def, + virCPUDefPtr origCPU, bool inactive, bool compatible); diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 5de9513aa..5766ece80 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -3344,9 +3344,9 @@ qemuDomainSaveInternal(virQEMUDriverPtr driver, virDomainPtr dom, virDomainDefFree(def); goto endjob; } - xml = qemuDomainDefFormatLive(driver, def, true, true); + xml = qemuDomainDefFormatLive(driver, def, NULL, true, true); } else { - xml = qemuDomainDefFormatLive(driver, vm->def, true, true); + xml = qemuDomainDefFormatLive(driver, vm->def, priv->origCPU, true, true); } if (!xml) { virReportError(VIR_ERR_OPERATION_FAILED, @@ -14511,7 +14511,8 @@ qemuDomainSnapshotCreateActiveExternal(virConnectPtr conn, "snapshot", false)) < 0) goto cleanup; - if (!(xml = qemuDomainDefFormatLive(driver, vm->def, true, true)) || + if (!(xml = qemuDomainDefFormatLive(driver, vm->def, priv->origCPU, + true, true)) || !(snap->def->cookie = (virObjectPtr) qemuDomainSaveCookieNew(vm))) goto cleanup; @@ -14622,6 +14623,7 @@ qemuDomainSnapshotCreateXML(virDomainPtr domain, bool align_match = true; virQEMUDriverConfigPtr cfg = NULL; virCapsPtr caps = NULL; + qemuDomainObjPrivatePtr priv; virCheckFlags(VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE | VIR_DOMAIN_SNAPSHOT_CREATE_CURRENT | @@ -14739,6 +14741,8 @@ qemuDomainSnapshotCreateXML(virDomainPtr domain, qemuDomainObjSetAsyncJobMask(vm, QEMU_JOB_NONE); + priv = vm->privateData; + if (redefine) { if (virDomainSnapshotRedefinePrep(domain, vm, &def, &snap, driver->xmlopt, @@ -14747,7 +14751,8 @@ qemuDomainSnapshotCreateXML(virDomainPtr domain, } else { /* Easiest way to clone inactive portion of vm->def is via * conversion in and back out of xml. */ - if (!(xml = qemuDomainDefFormatLive(driver, vm->def, true, true)) || + if (!(xml = qemuDomainDefFormatLive(driver, vm->def, priv->origCPU, + true, true)) || !(def->dom = virDomainDefParseString(xml, caps, driver->xmlopt, NULL, VIR_DOMAIN_DEF_PARSE_INACTIVE | VIR_DOMAIN_DEF_PARSE_SKIP_VALIDATE))) diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index 3c0d7e957..2cd862875 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -2059,9 +2059,10 @@ qemuMigrationBeginPhase(virQEMUDriverPtr driver, if (!qemuDomainDefCheckABIStability(driver, vm->def, def)) goto cleanup; - rv = qemuDomainDefFormatLive(driver, def, false, true); + rv = qemuDomainDefFormatLive(driver, def, NULL, false, true); } else { - rv = qemuDomainDefFormatLive(driver, vm->def, false, true); + rv = qemuDomainDefFormatLive(driver, vm->def, priv->origCPU, + false, true); } cleanup: -- 2.13.0

When persistent migration of a transient domain is requested but no custom XML is passed to the migration API we would just let the destination daemon make a persistent definition from the live definition itself. This is not a problem now, but once the destination daemon starts replacing the original CPU definition with the one from migration cookie before starting a domain, it would need to add more ugly hacks to reverse the operation. Let's just always send the persistent definition in the cookie to make things a bit cleaner. Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- Notes: Version 2: - new patch (separated from the original 13/15) src/qemu/qemu_migration.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index 2cd862875..40564ac63 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -3644,8 +3644,9 @@ qemuMigrationRun(virQEMUDriverPtr driver, if (!(persistDef = qemuMigrationPrepareDef(driver, persist_xml, NULL, NULL))) goto cleanup; - } else if (vm->newDef) { - if (!(persistDef = qemuDomainDefCopy(driver, vm->newDef, + } else { + virDomainDefPtr def = vm->newDef ? vm->newDef : vm->def; + if (!(persistDef = qemuDomainDefCopy(driver, def, VIR_DOMAIN_XML_SECURE | VIR_DOMAIN_XML_MIGRATABLE))) goto cleanup; -- 2.13.0

On Wed, Jun 07, 2017 at 10:37:42AM +0200, Jiri Denemark wrote:
When persistent migration of a transient domain is requested but no custom XML is passed to the migration API we would just let the destination daemon make a persistent definition from the live definition itself. This is not a problem now, but once the destination daemon starts replacing the original CPU definition with the one from migration cookie before starting a domain, it would need to add more ugly hacks to reverse the operation. Let's just always send the persistent definition in the cookie to make things a bit cleaner.
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> ---
Notes: Version 2: - new patch (separated from the original 13/15)
src/qemu/qemu_migration.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
Reviewed-by: Pavel Hrdina <phrdina@redhat.com>

On Wed, Jun 07, 2017 at 12:56:52 +0200, Pavel Hrdina wrote:
On Wed, Jun 07, 2017 at 10:37:42AM +0200, Jiri Denemark wrote:
When persistent migration of a transient domain is requested but no custom XML is passed to the migration API we would just let the destination daemon make a persistent definition from the live definition itself. This is not a problem now, but once the destination daemon starts replacing the original CPU definition with the one from migration cookie before starting a domain, it would need to add more ugly hacks to reverse the operation. Let's just always send the persistent definition in the cookie to make things a bit cleaner.
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> ---
Notes: Version 2: - new patch (separated from the original 13/15)
src/qemu/qemu_migration.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
Reviewed-by: Pavel Hrdina <phrdina@redhat.com>
Thanks for the reviews and forcing me to overcome my laziness :-) Jirka

Since the domain XML send during migration uses the original guest CPU definition but we still want the destination to enforce ABI if it is new enough, we send the live updated CPU definition in a migration cookie. Signed-off-by: Jiri Denemark <jdenemar@redhat.com> Reviewed-by: Pavel Hrdina <phrdina@redhat.com> --- Notes: Version 2: - persistent def related hunk moved to a separate patch src/qemu/qemu_migration.c | 6 +++++- src/qemu/qemu_migration_cookie.c | 31 ++++++++++++++++++++++++++++++- src/qemu/qemu_migration_cookie.h | 5 +++++ 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index 40564ac63..134c76c5e 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -2020,6 +2020,9 @@ qemuMigrationBeginPhase(virQEMUDriverPtr driver, vm->newDef && !qemuDomainVcpuHotplugIsInOrder(vm->newDef))) cookieFlags |= QEMU_MIGRATION_COOKIE_CPU_HOTPLUG; + if (priv->origCPU) + cookieFlags |= QEMU_MIGRATION_COOKIE_CPU; + if (!(mig = qemuMigrationEatCookie(driver, vm, NULL, 0, 0))) goto cleanup; @@ -2644,7 +2647,8 @@ qemuMigrationPrepareAny(virQEMUDriverPtr driver, QEMU_MIGRATION_COOKIE_LOCKSTATE | QEMU_MIGRATION_COOKIE_NBD | QEMU_MIGRATION_COOKIE_MEMORY_HOTPLUG | - QEMU_MIGRATION_COOKIE_CPU_HOTPLUG))) + QEMU_MIGRATION_COOKIE_CPU_HOTPLUG | + QEMU_MIGRATION_COOKIE_CPU))) goto cleanup; if (STREQ_NULLABLE(protocol, "rdma") && diff --git a/src/qemu/qemu_migration_cookie.c b/src/qemu/qemu_migration_cookie.c index 559b1f0c1..af0ac0341 100644 --- a/src/qemu/qemu_migration_cookie.c +++ b/src/qemu/qemu_migration_cookie.c @@ -48,7 +48,8 @@ VIR_ENUM_IMPL(qemuMigrationCookieFlag, "nbd", "statistics", "memory-hotplug", - "cpu-hotplug"); + "cpu-hotplug", + "cpu"); static void @@ -109,6 +110,7 @@ qemuMigrationCookieFree(qemuMigrationCookiePtr mig) VIR_FREE(mig->lockState); VIR_FREE(mig->lockDriver); VIR_FREE(mig->jobInfo); + virCPUDefFree(mig->cpu); VIR_FREE(mig); } @@ -519,6 +521,22 @@ qemuMigrationCookieAddStatistics(qemuMigrationCookiePtr mig, } +static int +qemuMigrationCookieAddCPU(qemuMigrationCookiePtr mig, + virDomainObjPtr vm) +{ + if (mig->cpu) + return 0; + + if (!(mig->cpu = virCPUDefCopy(vm->def->cpu))) + return -1; + + mig->flags |= QEMU_MIGRATION_COOKIE_CPU; + + return 0; +} + + static void qemuMigrationCookieGraphicsXMLFormat(virBufferPtr buf, qemuMigrationCookieGraphicsPtr grap) @@ -755,6 +773,9 @@ qemuMigrationCookieXMLFormat(virQEMUDriverPtr driver, if (mig->flags & QEMU_MIGRATION_COOKIE_STATS && mig->jobInfo) qemuMigrationCookieStatisticsXMLFormat(buf, mig->jobInfo); + if (mig->flags & QEMU_MIGRATION_COOKIE_CPU && mig->cpu) + virCPUDefFormatBufFull(buf, mig->cpu, NULL, false); + virBufferAdjustIndent(buf, -2); virBufferAddLit(buf, "</qemu-migration>\n"); return 0; @@ -1198,6 +1219,10 @@ qemuMigrationCookieXMLParse(qemuMigrationCookiePtr mig, (!(mig->jobInfo = qemuMigrationCookieStatisticsXMLParse(ctxt)))) goto error; + if (flags & QEMU_MIGRATION_COOKIE_CPU && + virCPUDefParseXML(ctxt, "./cpu[1]", VIR_CPU_TYPE_GUEST, &mig->cpu) < 0) + goto error; + virObjectUnref(caps); return 0; @@ -1274,6 +1299,10 @@ qemuMigrationBakeCookie(qemuMigrationCookiePtr mig, if (flags & QEMU_MIGRATION_COOKIE_CPU_HOTPLUG) mig->flagsMandatory |= QEMU_MIGRATION_COOKIE_CPU_HOTPLUG; + if (flags & QEMU_MIGRATION_COOKIE_CPU && + qemuMigrationCookieAddCPU(mig, dom) < 0) + return -1; + if (!(*cookieout = qemuMigrationCookieXMLFormatStr(driver, mig))) return -1; diff --git a/src/qemu/qemu_migration_cookie.h b/src/qemu/qemu_migration_cookie.h index b009d5bf5..e5f3d75a9 100644 --- a/src/qemu/qemu_migration_cookie.h +++ b/src/qemu/qemu_migration_cookie.h @@ -28,6 +28,7 @@ typedef enum { QEMU_MIGRATION_COOKIE_FLAG_STATS, QEMU_MIGRATION_COOKIE_FLAG_MEMORY_HOTPLUG, QEMU_MIGRATION_COOKIE_FLAG_CPU_HOTPLUG, + QEMU_MIGRATION_COOKIE_FLAG_CPU, QEMU_MIGRATION_COOKIE_FLAG_LAST } qemuMigrationCookieFlags; @@ -43,6 +44,7 @@ typedef enum { QEMU_MIGRATION_COOKIE_STATS = (1 << QEMU_MIGRATION_COOKIE_FLAG_STATS), QEMU_MIGRATION_COOKIE_MEMORY_HOTPLUG = (1 << QEMU_MIGRATION_COOKIE_FLAG_MEMORY_HOTPLUG), QEMU_MIGRATION_COOKIE_CPU_HOTPLUG = (1 << QEMU_MIGRATION_COOKIE_FLAG_CPU_HOTPLUG), + QEMU_MIGRATION_COOKIE_CPU = (1 << QEMU_MIGRATION_COOKIE_FLAG_CPU), } qemuMigrationCookieFeatures; typedef struct _qemuMigrationCookieGraphics qemuMigrationCookieGraphics; @@ -122,6 +124,9 @@ struct _qemuMigrationCookie { /* If (flags & QEMU_MIGRATION_COOKIE_STATS) */ qemuDomainJobInfoPtr jobInfo; + + /* If flags & QEMU_MIGRATION_COOKIE_CPU */ + virCPUDefPtr cpu; }; -- 2.13.0

Since the domain XML saved in a snapshot or saved image uses the original guest CPU definition but we still want to enforce ABI when restoring the domain if libvirt and QEMU are new enough, we save the live updated CPU definition in a save cookie. Signed-off-by: Jiri Denemark <jdenemar@redhat.com> Reviewed-by: Pavel Hrdina <phrdina@redhat.com> --- Notes: Version 2: - no change src/qemu/qemu_domain.c | 22 +++++++++++++++++++--- src/qemu/qemu_domain.h | 2 ++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 0da44f066..ec43d06d7 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -9167,12 +9167,15 @@ qemuDomainSaveCookieDispose(void *obj) qemuDomainSaveCookiePtr cookie = obj; VIR_DEBUG("cookie=%p", cookie); + + virCPUDefFree(cookie->cpu); } qemuDomainSaveCookiePtr qemuDomainSaveCookieNew(virDomainObjPtr vm ATTRIBUTE_UNUSED) { + qemuDomainObjPrivatePtr priv = vm->privateData; qemuDomainSaveCookiePtr cookie = NULL; if (qemuDomainInitialize() < 0) @@ -9181,7 +9184,10 @@ qemuDomainSaveCookieNew(virDomainObjPtr vm ATTRIBUTE_UNUSED) if (!(cookie = virObjectNew(qemuDomainSaveCookieClass))) goto error; - VIR_DEBUG("Save cookie %p", cookie); + if (priv->origCPU && !(cookie->cpu = virCPUDefCopy(vm->def->cpu))) + goto error; + + VIR_DEBUG("Save cookie %p, cpu=%p", cookie, cookie->cpu); return cookie; @@ -9203,6 +9209,10 @@ qemuDomainSaveCookieParse(xmlXPathContextPtr ctxt ATTRIBUTE_UNUSED, if (!(cookie = virObjectNew(qemuDomainSaveCookieClass))) goto error; + if (virCPUDefParseXML(ctxt, "./cpu[1]", VIR_CPU_TYPE_GUEST, + &cookie->cpu) < 0) + goto error; + *obj = (virObjectPtr) cookie; return 0; @@ -9213,9 +9223,15 @@ qemuDomainSaveCookieParse(xmlXPathContextPtr ctxt ATTRIBUTE_UNUSED, static int -qemuDomainSaveCookieFormat(virBufferPtr buf ATTRIBUTE_UNUSED, - virObjectPtr obj ATTRIBUTE_UNUSED) +qemuDomainSaveCookieFormat(virBufferPtr buf, + virObjectPtr obj) { + qemuDomainSaveCookiePtr cookie = (qemuDomainSaveCookiePtr) obj; + + if (cookie->cpu && + virCPUDefFormatBufFull(buf, cookie->cpu, NULL, false) < 0) + return -1; + return 0; } diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index 08c0b32f1..44c466cb7 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -424,6 +424,8 @@ typedef struct _qemuDomainSaveCookie qemuDomainSaveCookie; typedef qemuDomainSaveCookie *qemuDomainSaveCookiePtr; struct _qemuDomainSaveCookie { virObject parent; + + virCPUDefPtr cpu; }; qemuDomainSaveCookiePtr qemuDomainSaveCookieNew(virDomainObjPtr vm); -- 2.13.0

If QEMU is new enough and we have the live updated CPU definition in either save or migration cookie, we can use it to enforce ABI. The original guest CPU from domain XML will be stored in private data. Signed-off-by: Jiri Denemark <jdenemar@redhat.com> Reviewed-by: Pavel Hrdina <phrdina@redhat.com> --- Notes: Version 2: - no change src/qemu/qemu_domain.c | 40 ++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_domain.h | 5 +++++ src/qemu/qemu_driver.c | 30 ++++++++++++++++++++++++------ src/qemu/qemu_migration.c | 2 +- src/qemu/qemu_process.c | 24 ++++++++++++++++++++++-- src/qemu/qemu_process.h | 2 ++ 6 files changed, 94 insertions(+), 9 deletions(-) diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index ec43d06d7..2aa3aaa47 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -9240,3 +9240,43 @@ virSaveCookieCallbacks virQEMUDriverDomainSaveCookie = { .parse = qemuDomainSaveCookieParse, .format = qemuDomainSaveCookieFormat, }; + + +/** + * qemuDomainUpdateCPU: + * @vm: domain which is being started + * @cpu: CPU updated when the domain was running previously (before migration, + * snapshot, or save) + * @origCPU: where to store the original CPU from vm->def in case @cpu was + * used instead + * + * Replace the CPU definition with the updated one when QEMU is new enough to + * allow us to check extra features it is about to enable or disable when + * starting a domain. The original CPU is stored in @origCPU. + * + * Returns 0 on success, -1 on error. + */ +int +qemuDomainUpdateCPU(virDomainObjPtr vm, + virCPUDefPtr cpu, + virCPUDefPtr *origCPU) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + + *origCPU = NULL; + + if (!cpu || !vm->def->cpu || + !virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_QUERY_CPU_MODEL_EXPANSION) || + virCPUDefIsEqual(vm->def->cpu, cpu, false)) + return 0; + + if (!(cpu = virCPUDefCopy(cpu))) + return -1; + + VIR_DEBUG("Replacing CPU def with the updated one"); + + *origCPU = vm->def->cpu; + vm->def->cpu = cpu; + + return 0; +} diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index 44c466cb7..ef3a33076 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -919,4 +919,9 @@ char *qemuDomainDiskBackingStoreGetName(virDomainDiskDefPtr disk, virStorageSourcePtr qemuDomainGetStorageSourceByDevstr(const char *devstr, virDomainDefPtr def); +int +qemuDomainUpdateCPU(virDomainObjPtr vm, + virCPUDefPtr cpu, + virCPUDefPtr *origCPU); + #endif /* __QEMU_DOMAIN_H__ */ diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 5766ece80..12bafd636 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -1779,7 +1779,7 @@ static virDomainPtr qemuDomainCreateXML(virConnectPtr conn, goto cleanup; } - if (qemuProcessStart(conn, driver, vm, QEMU_ASYNC_JOB_START, + if (qemuProcessStart(conn, driver, vm, NULL, QEMU_ASYNC_JOB_START, NULL, -1, NULL, NULL, VIR_NETDEV_VPORT_PROFILE_OP_CREATE, start_flags) < 0) { @@ -6506,8 +6506,8 @@ qemuDomainSaveImageStartVM(virConnectPtr conn, } } - if (qemuProcessStart(conn, driver, vm, asyncJob, - "stdio", *fd, path, NULL, + if (qemuProcessStart(conn, driver, vm, cookie ? cookie->cpu : NULL, + asyncJob, "stdio", *fd, path, NULL, VIR_NETDEV_VPORT_PROFILE_OP_RESTORE, VIR_QEMU_PROCESS_START_PAUSED) == 0) restored = true; @@ -7124,7 +7124,7 @@ qemuDomainObjStart(virConnectPtr conn, } } - ret = qemuProcessStart(conn, driver, vm, asyncJob, + ret = qemuProcessStart(conn, driver, vm, NULL, asyncJob, NULL, -1, NULL, NULL, VIR_NETDEV_VPORT_PROFILE_OP_CREATE, start_flags); virDomainAuditStart(vm, "booted", ret >= 0); @@ -15294,6 +15294,8 @@ qemuDomainRevertToSnapshot(virDomainSnapshotPtr snapshot, virCapsPtr caps = NULL; bool was_running = false; bool was_stopped = false; + qemuDomainSaveCookiePtr cookie; + virCPUDefPtr origCPU = NULL; virCheckFlags(VIR_DOMAIN_SNAPSHOT_REVERT_RUNNING | VIR_DOMAIN_SNAPSHOT_REVERT_PAUSED | @@ -15399,6 +15401,8 @@ qemuDomainRevertToSnapshot(virDomainSnapshotPtr snapshot, goto endjob; } + cookie = (qemuDomainSaveCookiePtr) snap->def->cookie; + switch ((virDomainState) snap->def->state) { case VIR_DOMAIN_RUNNING: case VIR_DOMAIN_PAUSED: @@ -15410,6 +15414,15 @@ qemuDomainRevertToSnapshot(virDomainSnapshotPtr snapshot, * to have finer control. */ if (virDomainObjIsActive(vm)) { /* Transitions 5, 6, 8, 9 */ + /* Replace the CPU in config and put the original one in priv + * once we're done. + */ + if (cookie && cookie->cpu && config->cpu) { + origCPU = config->cpu; + if (!(config->cpu = virCPUDefCopy(cookie->cpu))) + goto endjob; + } + /* Check for ABI compatibility. We need to do this check against * the migratable XML or it will always fail otherwise */ if (config && @@ -15469,8 +15482,11 @@ qemuDomainRevertToSnapshot(virDomainSnapshotPtr snapshot, * failed loadvm attempt? */ goto endjob; } - if (config) + if (config) { virDomainObjAssignDef(vm, config, false, NULL); + virCPUDefFree(priv->origCPU); + VIR_STEAL_PTR(priv->origCPU, origCPU); + } } else { /* Transitions 2, 3 */ load: @@ -15479,6 +15495,7 @@ qemuDomainRevertToSnapshot(virDomainSnapshotPtr snapshot, virDomainObjAssignDef(vm, config, false, NULL); rc = qemuProcessStart(snapshot->domain->conn, driver, vm, + cookie ? cookie->cpu : NULL, QEMU_ASYNC_JOB_START, NULL, -1, NULL, snap, VIR_NETDEV_VPORT_PROFILE_OP_CREATE, VIR_QEMU_PROCESS_START_PAUSED); @@ -15572,7 +15589,7 @@ qemuDomainRevertToSnapshot(virDomainSnapshotPtr snapshot, start_flags |= paused ? VIR_QEMU_PROCESS_START_PAUSED : 0; qemuDomainEventQueue(driver, event); - rc = qemuProcessStart(snapshot->domain->conn, driver, vm, + rc = qemuProcessStart(snapshot->domain->conn, driver, vm, NULL, QEMU_ASYNC_JOB_START, NULL, -1, NULL, NULL, VIR_NETDEV_VPORT_PROFILE_OP_CREATE, start_flags); @@ -15644,6 +15661,7 @@ qemuDomainRevertToSnapshot(virDomainSnapshotPtr snapshot, virObjectUnref(caps); virObjectUnref(cfg); virNWFilterUnlockFilterUpdates(); + virCPUDefFree(origCPU); return ret; } diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index 134c76c5e..505e61e5f 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -2681,7 +2681,7 @@ qemuMigrationPrepareAny(virQEMUDriverPtr driver, goto stopjob; } - if (qemuProcessInit(driver, vm, QEMU_ASYNC_JOB_MIGRATION_IN, + if (qemuProcessInit(driver, vm, mig->cpu, QEMU_ASYNC_JOB_MIGRATION_IN, true, VIR_QEMU_PROCESS_START_AUTODESTROY) < 0) goto stopjob; stopProcess = true; diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index c06349474..4a66f0d5d 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -3946,6 +3946,13 @@ qemuProcessUpdateLiveGuestCPU(virQEMUDriverPtr driver, if (qemuProcessVerifyCPUFeatures(def, cpu) < 0) goto cleanup; + /* Don't update the CPU if we already did so when starting a domain + * during migration, restore or snapshot revert. */ + if (priv->origCPU) { + ret = 0; + goto cleanup; + } + if (!(orig = virCPUDefCopy(def->cpu))) goto cleanup; @@ -4864,6 +4871,7 @@ qemuProcessStartValidate(virQEMUDriverPtr driver, int qemuProcessInit(virQEMUDriverPtr driver, virDomainObjPtr vm, + virCPUDefPtr updatedCPU, qemuDomainAsyncJob asyncJob, bool migration, unsigned int flags) @@ -4872,6 +4880,7 @@ qemuProcessInit(virQEMUDriverPtr driver, virCapsPtr caps = NULL; qemuDomainObjPrivatePtr priv = vm->privateData; int stopFlags; + virCPUDefPtr origCPU = NULL; int ret = -1; VIR_DEBUG("vm=%p name=%s id=%d migration=%d", @@ -4896,6 +4905,9 @@ qemuProcessInit(virQEMUDriverPtr driver, vm->def->os.machine))) goto cleanup; + if (qemuDomainUpdateCPU(vm, updatedCPU, &origCPU) < 0) + goto cleanup; + if (qemuProcessStartValidate(driver, vm, priv->qemuCaps, caps, flags) < 0) goto cleanup; @@ -4928,11 +4940,14 @@ qemuProcessInit(virQEMUDriverPtr driver, if (qemuDomainSetPrivatePaths(driver, vm) < 0) goto stop; + + VIR_STEAL_PTR(priv->origCPU, origCPU); } ret = 0; cleanup: + virCPUDefFree(origCPU); virObjectUnref(cfg); virObjectUnref(caps); return ret; @@ -5963,6 +5978,7 @@ int qemuProcessStart(virConnectPtr conn, virQEMUDriverPtr driver, virDomainObjPtr vm, + virCPUDefPtr updatedCPU, qemuDomainAsyncJob asyncJob, const char *migrateFrom, int migrateFd, @@ -5993,7 +6009,8 @@ qemuProcessStart(virConnectPtr conn, if (!migrateFrom && !snapshot) flags |= VIR_QEMU_PROCESS_START_NEW; - if (qemuProcessInit(driver, vm, asyncJob, !!migrateFrom, flags) < 0) + if (qemuProcessInit(driver, vm, updatedCPU, + asyncJob, !!migrateFrom, flags) < 0) goto cleanup; if (migrateFrom) { @@ -6072,7 +6089,8 @@ qemuProcessCreatePretendCmd(virConnectPtr conn, flags |= VIR_QEMU_PROCESS_START_PRETEND; flags |= VIR_QEMU_PROCESS_START_NEW; - if (qemuProcessInit(driver, vm, QEMU_ASYNC_JOB_NONE, !!migrateURI, flags) < 0) + if (qemuProcessInit(driver, vm, NULL, QEMU_ASYNC_JOB_NONE, + !!migrateURI, flags) < 0) goto cleanup; if (qemuProcessPrepareDomain(conn, driver, vm, flags) < 0) @@ -6476,6 +6494,8 @@ void qemuProcessStop(virQEMUDriverPtr driver, /* clean up migration data */ VIR_FREE(priv->migTLSAlias); + virCPUDefFree(priv->origCPU); + priv->origCPU = NULL; /* clear previously used namespaces */ virBitmapFree(priv->namespaces); diff --git a/src/qemu/qemu_process.h b/src/qemu/qemu_process.h index 830d8cef8..c38310b47 100644 --- a/src/qemu/qemu_process.h +++ b/src/qemu/qemu_process.h @@ -75,6 +75,7 @@ typedef enum { int qemuProcessStart(virConnectPtr conn, virQEMUDriverPtr driver, virDomainObjPtr vm, + virCPUDefPtr updatedCPU, qemuDomainAsyncJob asyncJob, const char *migrateFrom, int stdin_fd, @@ -93,6 +94,7 @@ virCommandPtr qemuProcessCreatePretendCmd(virConnectPtr conn, int qemuProcessInit(virQEMUDriverPtr driver, virDomainObjPtr vm, + virCPUDefPtr updatedCPU, qemuDomainAsyncJob asyncJob, bool migration, unsigned int flags); -- 2.13.0
participants (2)
-
Jiri Denemark
-
Pavel Hrdina