[libvirt] [PATCH 0/4] qemu: Fix CPU model broken by older libvirt

When libvirt older than 3.9.0 reconnected to a running domain started by old libvirt it could have messed up the expansion of host-model by adding features QEMU does not support (such as cmt). This series fixes the reconnection code and adds a hack which fixes CPU definitions which were broken by older libvirt. https://bugzilla.redhat.com/show_bug.cgi?id=1495171 Jiri Denemark (4): qemu: Separate CPU updating code from qemuProcessReconnect conf: Introduce virCPUDefFindFeature qemu: Filter CPU features when using host CPU qemu: Fix CPU model broken by older libvirt src/conf/cpu_conf.c | 40 +++++++++++++++-------- src/conf/cpu_conf.h | 4 +++ src/libvirt_private.syms | 1 + src/qemu/qemu_domain.c | 76 ++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_domain.h | 4 +++ src/qemu/qemu_driver.c | 14 ++++++++ src/qemu/qemu_process.c | 83 ++++++++++++++++++++++++++++++++++-------------- 7 files changed, 186 insertions(+), 36 deletions(-) -- 2.14.2

The new function is called qemuProcessRefreshCPU. Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- src/qemu/qemu_process.c | 66 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 43 insertions(+), 23 deletions(-) diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 0cb023095b..5ed6b68eb8 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -6876,6 +6876,47 @@ qemuProcessRefreshDisks(virQEMUDriverPtr driver, } +static int +qemuProcessRefreshCPU(virQEMUDriverPtr driver, + virDomainObjPtr vm) +{ + virCapsPtr caps = virQEMUDriverGetCapabilities(driver, false); + virCPUDefPtr host = NULL; + int ret = -1; + + if (!virQEMUCapsGuestIsNative(caps->host.arch, vm->def->os.arch) || + !caps->host.cpu || + !vm->def->cpu) + return 0; + + if (!caps) + goto cleanup; + + /* If the domain with a host-model CPU was started by an old libvirt + * (< 2.3) which didn't replace the CPU with a custom one, let's do it now + * since the rest of our code does not really expect a host-model CPU in a + * running domain. + */ + if (vm->def->cpu->mode == VIR_CPU_MODE_HOST_MODEL) { + if (!(host = virCPUCopyMigratable(caps->host.cpu->arch, caps->host.cpu))) + goto cleanup; + + if (virCPUUpdate(vm->def->os.arch, vm->def->cpu, host) < 0) + goto cleanup; + + if (qemuProcessUpdateCPU(driver, vm, QEMU_ASYNC_JOB_NONE) < 0) + goto cleanup; + } + + ret = 0; + + cleanup: + virCPUDefFree(host); + virObjectUnref(caps); + return ret; +} + + struct qemuProcessReconnectData { virConnectPtr conn; virQEMUDriverPtr driver; @@ -7042,29 +7083,8 @@ qemuProcessReconnect(void *opaque) ignore_value(qemuSecurityCheckAllLabel(driver->securityManager, obj->def)); - /* If the domain with a host-model CPU was started by an old libvirt - * (< 2.3) which didn't replace the CPU with a custom one, let's do it now - * since the rest of our code does not really expect a host-model CPU in a - * running domain. - */ - if (virQEMUCapsGuestIsNative(caps->host.arch, obj->def->os.arch) && - caps->host.cpu && - obj->def->cpu && - obj->def->cpu->mode == VIR_CPU_MODE_HOST_MODEL) { - virCPUDefPtr host; - - if (!(host = virCPUCopyMigratable(caps->host.cpu->arch, caps->host.cpu))) - goto error; - - if (virCPUUpdate(obj->def->os.arch, obj->def->cpu, host) < 0) { - virCPUDefFree(host); - goto error; - } - virCPUDefFree(host); - - if (qemuProcessUpdateCPU(driver, obj, QEMU_ASYNC_JOB_NONE) < 0) - goto error; - } + if (qemuProcessRefreshCPU(driver, obj) < 0) + goto error; if (qemuDomainRefreshVcpuInfo(driver, obj, QEMU_ASYNC_JOB_NONE, true) < 0) goto error; -- 2.14.2

On Wed, Oct 11, 2017 at 12:11 PM +0200, Jiri Denemark <jdenemar@redhat.com> wrote:
The new function is called qemuProcessRefreshCPU.
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- src/qemu/qemu_process.c | 66 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 43 insertions(+), 23 deletions(-)
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 0cb023095b..5ed6b68eb8 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -6876,6 +6876,47 @@ qemuProcessRefreshDisks(virQEMUDriverPtr driver, }
+static int +qemuProcessRefreshCPU(virQEMUDriverPtr driver, + virDomainObjPtr vm) +{ + virCapsPtr caps = virQEMUDriverGetCapabilities(driver, false); + virCPUDefPtr host = NULL; + int ret = -1; + + if (!virQEMUCapsGuestIsNative(caps->host.arch, vm->def->os.arch) || + !caps->host.cpu || + !vm->def->cpu) + return 0; + + if (!caps) + goto cleanup;
That's somehow weird... We access 'caps->host.arch'/ 'caps->host.cpu' and after that we're checking for a null pointer?!
+ + /* If the domain with a host-model CPU was started by an old libvirt + * (< 2.3) which didn't replace the CPU with a custom one, let's do it now + * since the rest of our code does not really expect a host-model CPU in a + * running domain. + */ + if (vm->def->cpu->mode == VIR_CPU_MODE_HOST_MODEL) { + if (!(host = virCPUCopyMigratable(caps->host.cpu->arch, caps->host.cpu))) + goto cleanup; + + if (virCPUUpdate(vm->def->os.arch, vm->def->cpu, host) < 0) + goto cleanup; + + if (qemuProcessUpdateCPU(driver, vm, QEMU_ASYNC_JOB_NONE) < 0) + goto cleanup; + } + + ret = 0; + + cleanup: + virCPUDefFree(host); + virObjectUnref(caps); + return ret; +} + + struct qemuProcessReconnectData { virConnectPtr conn; virQEMUDriverPtr driver; @@ -7042,29 +7083,8 @@ qemuProcessReconnect(void *opaque) ignore_value(qemuSecurityCheckAllLabel(driver->securityManager, obj->def));
- /* If the domain with a host-model CPU was started by an old libvirt - * (< 2.3) which didn't replace the CPU with a custom one, let's do it now - * since the rest of our code does not really expect a host-model CPU in a - * running domain. - */ - if (virQEMUCapsGuestIsNative(caps->host.arch, obj->def->os.arch) && - caps->host.cpu && - obj->def->cpu && - obj->def->cpu->mode == VIR_CPU_MODE_HOST_MODEL) { - virCPUDefPtr host; - - if (!(host = virCPUCopyMigratable(caps->host.cpu->arch, caps->host.cpu))) - goto error; - - if (virCPUUpdate(obj->def->os.arch, obj->def->cpu, host) < 0) { - virCPUDefFree(host); - goto error; - } - virCPUDefFree(host); - - if (qemuProcessUpdateCPU(driver, obj, QEMU_ASYNC_JOB_NONE) < 0) - goto error; - } + if (qemuProcessRefreshCPU(driver, obj) < 0) + goto error;
if (qemuDomainRefreshVcpuInfo(driver, obj, QEMU_ASYNC_JOB_NONE, true) < 0) goto error; -- 2.14.2
-- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
-- Beste Grüße / Kind regards Marc Hartmayer IBM Deutschland Research & Development GmbH Vorsitzende des Aufsichtsrats: Martina Koederitz Geschäftsführung: Dirk Wittkopp Sitz der Gesellschaft: Böblingen Registergericht: Amtsgericht Stuttgart, HRB 243294

On Wed, Oct 11, 2017 at 19:42:36 +0200, Marc Hartmayer wrote:
On Wed, Oct 11, 2017 at 12:11 PM +0200, Jiri Denemark <jdenemar@redhat.com> wrote:
The new function is called qemuProcessRefreshCPU.
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- src/qemu/qemu_process.c | 66 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 43 insertions(+), 23 deletions(-)
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 0cb023095b..5ed6b68eb8 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -6876,6 +6876,47 @@ qemuProcessRefreshDisks(virQEMUDriverPtr driver, }
+static int +qemuProcessRefreshCPU(virQEMUDriverPtr driver, + virDomainObjPtr vm) +{ + virCapsPtr caps = virQEMUDriverGetCapabilities(driver, false); + virCPUDefPtr host = NULL; + int ret = -1; + + if (!virQEMUCapsGuestIsNative(caps->host.arch, vm->def->os.arch) || + !caps->host.cpu || + !vm->def->cpu) + return 0; + + if (!caps) + goto cleanup;
That's somehow weird... We access 'caps->host.arch'/ 'caps->host.cpu' and after that we're checking for a null pointer?!
Oops, yes. Consider the following patch squashed in: diff --git i/src/qemu/qemu_process.c w/src/qemu/qemu_process.c index 5ed6b68eb8..8c33af28dd 100644 --- i/src/qemu/qemu_process.c +++ w/src/qemu/qemu_process.c @@ -6884,14 +6884,14 @@ qemuProcessRefreshCPU(virQEMUDriverPtr driver, virCPUDefPtr host = NULL; int ret = -1; + if (!caps) + return -1; + if (!virQEMUCapsGuestIsNative(caps->host.arch, vm->def->os.arch) || !caps->host.cpu || !vm->def->cpu) return 0; - if (!caps) - goto cleanup; - /* If the domain with a host-model CPU was started by an old libvirt * (< 2.3) which didn't replace the CPU with a custom one, let's do it now * since the rest of our code does not really expect a host-model CPU in a

On Thu, Oct 12, 2017 at 09:18:36AM +0200, Jiri Denemark wrote:
On Wed, Oct 11, 2017 at 19:42:36 +0200, Marc Hartmayer wrote:
On Wed, Oct 11, 2017 at 12:11 PM +0200, Jiri Denemark <jdenemar@redhat.com> wrote:
The new function is called qemuProcessRefreshCPU.
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- src/qemu/qemu_process.c | 66 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 43 insertions(+), 23 deletions(-)
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 0cb023095b..5ed6b68eb8 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -6876,6 +6876,47 @@ qemuProcessRefreshDisks(virQEMUDriverPtr driver, }
+static int +qemuProcessRefreshCPU(virQEMUDriverPtr driver, + virDomainObjPtr vm) +{ + virCapsPtr caps = virQEMUDriverGetCapabilities(driver, false); + virCPUDefPtr host = NULL; + int ret = -1; + + if (!virQEMUCapsGuestIsNative(caps->host.arch, vm->def->os.arch) || + !caps->host.cpu || + !vm->def->cpu) + return 0; + + if (!caps) + goto cleanup;
That's somehow weird... We access 'caps->host.arch'/ 'caps->host.cpu' and after that we're checking for a null pointer?!
Oops, yes. Consider the following patch squashed in:
diff --git i/src/qemu/qemu_process.c w/src/qemu/qemu_process.c index 5ed6b68eb8..8c33af28dd 100644 --- i/src/qemu/qemu_process.c +++ w/src/qemu/qemu_process.c @@ -6884,14 +6884,14 @@ qemuProcessRefreshCPU(virQEMUDriverPtr driver, virCPUDefPtr host = NULL; int ret = -1;
+ if (!caps) + return -1; + if (!virQEMUCapsGuestIsNative(caps->host.arch, vm->def->os.arch) || !caps->host.cpu || !vm->def->cpu) return 0;
This will leak the caps reference. Reviewed-by: Pavel Hrdina <phrdina@redhat.com>

Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- src/conf/cpu_conf.c | 40 +++++++++++++++++++++++++++------------- src/conf/cpu_conf.h | 4 ++++ src/libvirt_private.syms | 1 + 3 files changed, 32 insertions(+), 13 deletions(-) diff --git a/src/conf/cpu_conf.c b/src/conf/cpu_conf.c index 76f04c3531..669935acf8 100644 --- a/src/conf/cpu_conf.c +++ b/src/conf/cpu_conf.c @@ -773,24 +773,22 @@ virCPUDefUpdateFeatureInternal(virCPUDefPtr def, int policy, bool update) { - size_t i; + virCPUFeatureDefPtr feat; if (def->type == VIR_CPU_TYPE_HOST) policy = -1; - for (i = 0; i < def->nfeatures; i++) { - if (STREQ(name, def->features[i].name)) { - if (update) { - def->features[i].policy = policy; - return 0; - } - - virReportError(VIR_ERR_INTERNAL_ERROR, - _("CPU feature '%s' specified more than once"), - name); - - return -1; + if ((feat = virCPUDefFindFeature(def, name))) { + if (update) { + feat->policy = policy; + return 0; } + + virReportError(VIR_ERR_INTERNAL_ERROR, + _("CPU feature '%s' specified more than once"), + name); + + return -1; } if (VIR_RESIZE_N(def->features, def->nfeatures_max, @@ -822,6 +820,22 @@ virCPUDefAddFeature(virCPUDefPtr def, return virCPUDefUpdateFeatureInternal(def, name, policy, false); } + +virCPUFeatureDefPtr +virCPUDefFindFeature(virCPUDefPtr def, + const char *name) +{ + size_t i; + + for (i = 0; i < def->nfeatures; i++) { + if (STREQ(name, def->features[i].name)) + return def->features + i; + } + + return NULL; +} + + bool virCPUDefIsEqual(virCPUDefPtr src, virCPUDefPtr dst, diff --git a/src/conf/cpu_conf.h b/src/conf/cpu_conf.h index b1a512b19a..d1983f5d4f 100644 --- a/src/conf/cpu_conf.h +++ b/src/conf/cpu_conf.h @@ -215,6 +215,10 @@ virCPUDefUpdateFeature(virCPUDefPtr cpu, const char *name, int policy); +virCPUFeatureDefPtr +virCPUDefFindFeature(virCPUDefPtr def, + const char *name); + virCPUDefPtr * virCPUDefListParse(const char **xmlCPUs, unsigned int ncpus, diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 26b1ae2850..7e116b19c4 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -75,6 +75,7 @@ virCPUDefCopy; virCPUDefCopyModel; virCPUDefCopyModelFilter; virCPUDefCopyWithoutModel; +virCPUDefFindFeature; virCPUDefFormat; virCPUDefFormatBuf; virCPUDefFormatBufFull; -- 2.14.2

On Wed, Oct 11, 2017 at 12:11 PM +0200, Jiri Denemark <jdenemar@redhat.com> wrote:
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- src/conf/cpu_conf.c | 40 +++++++++++++++++++++++++++------------- src/conf/cpu_conf.h | 4 ++++ src/libvirt_private.syms | 1 + 3 files changed, 32 insertions(+), 13 deletions(-)
diff --git a/src/conf/cpu_conf.c b/src/conf/cpu_conf.c index 76f04c3531..669935acf8 100644 --- a/src/conf/cpu_conf.c +++ b/src/conf/cpu_conf.c @@ -773,24 +773,22 @@ virCPUDefUpdateFeatureInternal(virCPUDefPtr def, int policy, bool update) { - size_t i; + virCPUFeatureDefPtr feat;
if (def->type == VIR_CPU_TYPE_HOST) policy = -1;
- for (i = 0; i < def->nfeatures; i++) { - if (STREQ(name, def->features[i].name)) { - if (update) { - def->features[i].policy = policy; - return 0; - } - - virReportError(VIR_ERR_INTERNAL_ERROR, - _("CPU feature '%s' specified more than once"), - name); - - return -1; + if ((feat = virCPUDefFindFeature(def, name))) { + if (update) { + feat->policy = policy; + return 0; } + + virReportError(VIR_ERR_INTERNAL_ERROR, + _("CPU feature '%s' specified more than once"), + name); + + return -1; }
if (VIR_RESIZE_N(def->features, def->nfeatures_max, @@ -822,6 +820,22 @@ virCPUDefAddFeature(virCPUDefPtr def, return virCPUDefUpdateFeatureInternal(def, name, policy, false); }
+ +virCPUFeatureDefPtr +virCPUDefFindFeature(virCPUDefPtr def, + const char *name) +{ + size_t i; + + for (i = 0; i < def->nfeatures; i++) { + if (STREQ(name, def->features[i].name)) + return def->features + i; ^^^^^^^^^^^^^^^^^^ I prefer return &def->features[i]
+ } + + return NULL; +} + + bool virCPUDefIsEqual(virCPUDefPtr src, virCPUDefPtr dst, diff --git a/src/conf/cpu_conf.h b/src/conf/cpu_conf.h index b1a512b19a..d1983f5d4f 100644 --- a/src/conf/cpu_conf.h +++ b/src/conf/cpu_conf.h @@ -215,6 +215,10 @@ virCPUDefUpdateFeature(virCPUDefPtr cpu, const char *name, int policy);
+virCPUFeatureDefPtr +virCPUDefFindFeature(virCPUDefPtr def, + const char *name); + virCPUDefPtr * virCPUDefListParse(const char **xmlCPUs, unsigned int ncpus, diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 26b1ae2850..7e116b19c4 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -75,6 +75,7 @@ virCPUDefCopy; virCPUDefCopyModel; virCPUDefCopyModelFilter; virCPUDefCopyWithoutModel; +virCPUDefFindFeature; virCPUDefFormat; virCPUDefFormatBuf; virCPUDefFormatBufFull; -- 2.14.2
-- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
-- Beste Grüße / Kind regards Marc Hartmayer IBM Deutschland Research & Development GmbH Vorsitzende des Aufsichtsrats: Martina Koederitz Geschäftsführung: Dirk Wittkopp Sitz der Gesellschaft: Böblingen Registergericht: Amtsgericht Stuttgart, HRB 243294

On Wed, Oct 11, 2017 at 12:11:15PM +0200, Jiri Denemark wrote:
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- src/conf/cpu_conf.c | 40 +++++++++++++++++++++++++++------------- src/conf/cpu_conf.h | 4 ++++ src/libvirt_private.syms | 1 + 3 files changed, 32 insertions(+), 13 deletions(-)
Reviewed-by: Pavel Hrdina <phrdina@redhat.com>

The host CPU definition from host capabilities may contain features unknown to QEMU. Thus whenever we want to use this CPU definition, we have to filter the unknown features. https://bugzilla.redhat.com/show_bug.cgi?id=1495171 Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- src/qemu/qemu_process.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 5ed6b68eb8..8553c5126f 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -6882,6 +6882,7 @@ qemuProcessRefreshCPU(virQEMUDriverPtr driver, { virCapsPtr caps = virQEMUDriverGetCapabilities(driver, false); virCPUDefPtr host = NULL; + virCPUDefPtr cpu = NULL; int ret = -1; if (!virQEMUCapsGuestIsNative(caps->host.arch, vm->def->os.arch) || @@ -6901,7 +6902,13 @@ qemuProcessRefreshCPU(virQEMUDriverPtr driver, if (!(host = virCPUCopyMigratable(caps->host.cpu->arch, caps->host.cpu))) goto cleanup; - if (virCPUUpdate(vm->def->os.arch, vm->def->cpu, host) < 0) + if (!(cpu = virCPUDefCopyWithoutModel(host)) || + virCPUDefCopyModelFilter(cpu, host, false, + virQEMUCapsCPUFilterFeatures, + &caps->host.cpu->arch) < 0) + goto cleanup; + + if (virCPUUpdate(vm->def->os.arch, vm->def->cpu, cpu) < 0) goto cleanup; if (qemuProcessUpdateCPU(driver, vm, QEMU_ASYNC_JOB_NONE) < 0) @@ -6911,6 +6918,7 @@ qemuProcessRefreshCPU(virQEMUDriverPtr driver, ret = 0; cleanup: + virCPUDefFree(cpu); virCPUDefFree(host); virObjectUnref(caps); return ret; -- 2.14.2

On Wed, Oct 11, 2017 at 12:11 PM +0200, Jiri Denemark <jdenemar@redhat.com> wrote:
The host CPU definition from host capabilities may contain features unknown to QEMU. Thus whenever we want to use this CPU definition, we have to filter the unknown features.
https://bugzilla.redhat.com/show_bug.cgi?id=1495171
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- src/qemu/qemu_process.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 5ed6b68eb8..8553c5126f 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -6882,6 +6882,7 @@ qemuProcessRefreshCPU(virQEMUDriverPtr driver, { virCapsPtr caps = virQEMUDriverGetCapabilities(driver, false); virCPUDefPtr host = NULL; + virCPUDefPtr cpu = NULL; int ret = -1;
if (!virQEMUCapsGuestIsNative(caps->host.arch, vm->def->os.arch) || @@ -6901,7 +6902,13 @@ qemuProcessRefreshCPU(virQEMUDriverPtr driver, if (!(host = virCPUCopyMigratable(caps->host.cpu->arch, caps->host.cpu))) goto cleanup;
- if (virCPUUpdate(vm->def->os.arch, vm->def->cpu, host) < 0)
Maybe you could add a comment about what we're doing here... (and why). Since it wasn't that clear that it's needed here.
+ if (!(cpu = virCPUDefCopyWithoutModel(host)) || + virCPUDefCopyModelFilter(cpu, host, false, + virQEMUCapsCPUFilterFeatures, + &caps->host.cpu->arch) < 0) + goto cleanup; + + if (virCPUUpdate(vm->def->os.arch, vm->def->cpu, cpu) < 0) goto cleanup;
if (qemuProcessUpdateCPU(driver, vm, QEMU_ASYNC_JOB_NONE) < 0) @@ -6911,6 +6918,7 @@ qemuProcessRefreshCPU(virQEMUDriverPtr driver, ret = 0;
cleanup: + virCPUDefFree(cpu); virCPUDefFree(host); virObjectUnref(caps); return ret; -- 2.14.2
-- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
-- Beste Grüße / Kind regards Marc Hartmayer IBM Deutschland Research & Development GmbH Vorsitzende des Aufsichtsrats: Martina Koederitz Geschäftsführung: Dirk Wittkopp Sitz der Gesellschaft: Böblingen Registergericht: Amtsgericht Stuttgart, HRB 243294

On Wed, Oct 11, 2017 at 12:11:16PM +0200, Jiri Denemark wrote:
The host CPU definition from host capabilities may contain features unknown to QEMU. Thus whenever we want to use this CPU definition, we have to filter the unknown features.
Might be nice to explicitly mention in the commit message that this fixes the issue while reconnecting to QEMU process started by old libvirt that keeps "host-model" in the status XML. Took me some time to figure that out :).
https://bugzilla.redhat.com/show_bug.cgi?id=1495171
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- src/qemu/qemu_process.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-)
Reviewed-by: Pavel Hrdina <phrdina@redhat.com>

On Tue, Oct 17, 2017 at 12:57:10 +0200, Pavel Hrdina wrote:
On Wed, Oct 11, 2017 at 12:11:16PM +0200, Jiri Denemark wrote:
The host CPU definition from host capabilities may contain features unknown to QEMU. Thus whenever we want to use this CPU definition, we have to filter the unknown features.
Might be nice to explicitly mention in the commit message that this fixes the issue while reconnecting to QEMU process started by old libvirt that keeps "host-model" in the status XML. Took me some time to figure that out :).
Yeah, sorry about it. What about the following commit message? When reconnecting to a domain started with a host-model CPU which was started by old libvirt that did not replace host-model with the real CPU definition, libvirt replaces the host-model CPU with the CPU from capabilities (because this is what the old libvirt did when it started the domain). Without this patch libvirt could use features unknown to QEMU in the CPU definition which replaced the original host-model CPU. Such domain would keep running just fine, but any attempt to migrate it will fail and once the domain is saved or snapshotted, restoring it would fail too. In other words whenever we want to use this CPU definition, we have to filter the unknown features. Jirka

On Tue, Oct 17, 2017 at 01:15:43PM +0200, Jiri Denemark wrote:
On Tue, Oct 17, 2017 at 12:57:10 +0200, Pavel Hrdina wrote:
On Wed, Oct 11, 2017 at 12:11:16PM +0200, Jiri Denemark wrote:
The host CPU definition from host capabilities may contain features unknown to QEMU. Thus whenever we want to use this CPU definition, we have to filter the unknown features.
Might be nice to explicitly mention in the commit message that this fixes the issue while reconnecting to QEMU process started by old libvirt that keeps "host-model" in the status XML. Took me some time to figure that out :).
Yeah, sorry about it. What about the following commit message?
When reconnecting to a domain started with a host-model CPU which was started by old libvirt that did not replace host-model with the real CPU definition, libvirt replaces the host-model CPU with the CPU from capabilities (because this is what the old libvirt did when it started the domain). Without this patch libvirt could use features unknown to QEMU in the CPU definition which replaced the original host-model CPU. Such domain would keep running just fine, but any attempt to migrate it will fail and once the domain is saved or snapshotted, restoring it would fail too.
In other words whenever we want to use this CPU definition, we have to filter the unknown features.
Reviewed-by: Pavel Hrdina <phrdina@redhat.com>

When libvirt older than 3.9.0 reconnected to a running domain started by old libvirt it could have messed up the expansion of host-model by adding features QEMU does not support (such as cmt). Thus whenever we reconnect to a running domain, revert to an active snapshot, or restore a saved domain we need to check the guest CPU model and remove the CPU features unknown to QEMU. We can do this because we know the domain was successfully started, which means the CPU did not contain the features when libvirt started the domain. https://bugzilla.redhat.com/show_bug.cgi?id=1495171 Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- src/qemu/qemu_domain.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_domain.h | 4 +++ src/qemu/qemu_driver.c | 14 +++++++++ src/qemu/qemu_process.c | 9 ++++++ 4 files changed, 103 insertions(+) diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index f621cf7afc..29b6618a56 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -10021,6 +10021,82 @@ qemuDomainUpdateCPU(virDomainObjPtr vm, return 0; } + +/** + * qemuDomainFixupCPUS: + * @vm: domain object + * @origCPU: original CPU used when the domain was started + * + * Libvirt older than 3.9.0 could have messed up the expansion of host-model + * CPU when reconnecting to a running domain by adding features QEMU does not + * support (such as cmt). This API fixes both the actual CPU provided by QEMU + * (stored in the domain object) and the @origCPU used when starting the + * domain. + * + * This is safe even if the original CPU definition used mode='custom' (rather + * than host-model) since we know QEMU was able to start the domain and thus + * the CPU definitions do not contain any features unknown to QEMU. + * + * This function can only be used on an active domain or when restoring a + * domain which was running. + * + * Returns 0 on success, -1 on error. + */ +int +qemuDomainFixupCPUs(virDomainObjPtr vm, + virCPUDefPtr *origCPU) +{ + virCPUDefPtr fixedCPU = NULL; + virCPUDefPtr fixedOrig = NULL; + virArch arch = vm->def->os.arch; + int ret = 0; + + if (!ARCH_IS_X86(arch)) + return 0; + + if (!vm->def->cpu || + vm->def->cpu->mode != VIR_CPU_MODE_CUSTOM || + !vm->def->cpu->model) + return 0; + + /* Missing origCPU means QEMU created exactly the same virtual CPU which + * we asked for or libvirt was too old to mess up the translation from + * host-model. + */ + if (!*origCPU) + return 0; + + if (virCPUDefFindFeature(vm->def->cpu, "cmt") && + (!(fixedCPU = virCPUDefCopyWithoutModel(vm->def->cpu)) || + virCPUDefCopyModelFilter(fixedCPU, vm->def->cpu, false, + virQEMUCapsCPUFilterFeatures, &arch) < 0)) + goto cleanup; + + if (virCPUDefFindFeature(*origCPU, "cmt") && + (!(fixedOrig = virCPUDefCopyWithoutModel(*origCPU)) || + virCPUDefCopyModelFilter(fixedOrig, *origCPU, false, + virQEMUCapsCPUFilterFeatures, &arch) < 0)) + goto cleanup; + + if (fixedCPU) { + virCPUDefFree(vm->def->cpu); + VIR_STEAL_PTR(vm->def->cpu, fixedCPU); + } + + if (fixedOrig) { + virCPUDefFree(*origCPU); + VIR_STEAL_PTR(*origCPU, fixedOrig); + } + + ret = 0; + + cleanup: + virCPUDefFree(fixedCPU); + virCPUDefFree(fixedOrig); + return ret; +} + + char * qemuDomainGetMachineName(virDomainObjPtr vm) { diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index c34cd37fc4..e430302519 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -971,6 +971,10 @@ qemuDomainUpdateCPU(virDomainObjPtr vm, virCPUDefPtr cpu, virCPUDefPtr *origCPU); +int +qemuDomainFixupCPUs(virDomainObjPtr vm, + virCPUDefPtr *origCPU); + char * qemuDomainGetMachineName(virDomainObjPtr vm); diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 842e088519..2715d86615 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -6551,6 +6551,13 @@ qemuDomainSaveImageStartVM(virConnectPtr conn, } } + /* No cookie means libvirt which saved the domain was too old to mess up + * the CPU definitions. + */ + if (cookie && + qemuDomainFixupCPUs(vm, &cookie->cpu) < 0) + goto cleanup; + if (qemuProcessStart(conn, driver, vm, cookie ? cookie->cpu : NULL, asyncJob, "stdio", *fd, path, NULL, VIR_NETDEV_VPORT_PROFILE_OP_RESTORE, @@ -15754,6 +15761,13 @@ qemuDomainRevertToSnapshot(virDomainSnapshotPtr snapshot, if (config) virDomainObjAssignDef(vm, config, false, NULL); + /* No cookie means libvirt which saved the domain was too old to + * mess up the CPU definitions. + */ + if (cookie && + qemuDomainFixupCPUs(vm, &cookie->cpu) < 0) + goto cleanup; + rc = qemuProcessStart(snapshot->domain->conn, driver, vm, cookie ? cookie->cpu : NULL, QEMU_ASYNC_JOB_START, NULL, -1, NULL, snap, diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 8553c5126f..780af5bd78 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -6881,6 +6881,7 @@ qemuProcessRefreshCPU(virQEMUDriverPtr driver, virDomainObjPtr vm) { virCapsPtr caps = virQEMUDriverGetCapabilities(driver, false); + qemuDomainObjPrivatePtr priv = vm->privateData; virCPUDefPtr host = NULL; virCPUDefPtr cpu = NULL; int ret = -1; @@ -6913,6 +6914,14 @@ qemuProcessRefreshCPU(virQEMUDriverPtr driver, if (qemuProcessUpdateCPU(driver, vm, QEMU_ASYNC_JOB_NONE) < 0) goto cleanup; + } else if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_QUERY_CPU_MODEL_EXPANSION)) { + /* We only try to fix CPUs when the libvirt/QEMU combo used to start + * the domain did not know about query-cpu-model-expansion in which + * case the host-model is known to not contain features which QEMU + * doesn't know about. + */ + if (qemuDomainFixupCPUs(vm, &priv->origCPU) < 0) + goto cleanup; } ret = 0; -- 2.14.2

On Wed, Oct 11, 2017 at 12:11:17PM +0200, Jiri Denemark wrote:
When libvirt older than 3.9.0 reconnected to a running domain started by old libvirt it could have messed up the expansion of host-model by adding features QEMU does not support (such as cmt). Thus whenever we reconnect to a running domain, revert to an active snapshot, or restore a saved domain we need to check the guest CPU model and remove the CPU features unknown to QEMU. We can do this because we know the domain was successfully started, which means the CPU did not contain the features when libvirt started the domain.
https://bugzilla.redhat.com/show_bug.cgi?id=1495171
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- src/qemu/qemu_domain.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_domain.h | 4 +++ src/qemu/qemu_driver.c | 14 +++++++++ src/qemu/qemu_process.c | 9 ++++++ 4 files changed, 103 insertions(+)
Reviewed-by: Pavel Hrdina <phrdina@redhat.com>

On Tue, Oct 17, 2017 at 14:34:25 +0200, Pavel Hrdina wrote:
On Wed, Oct 11, 2017 at 12:11:17PM +0200, Jiri Denemark wrote:
When libvirt older than 3.9.0 reconnected to a running domain started by old libvirt it could have messed up the expansion of host-model by adding features QEMU does not support (such as cmt). Thus whenever we reconnect to a running domain, revert to an active snapshot, or restore a saved domain we need to check the guest CPU model and remove the CPU features unknown to QEMU. We can do this because we know the domain was successfully started, which means the CPU did not contain the features when libvirt started the domain.
https://bugzilla.redhat.com/show_bug.cgi?id=1495171
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> --- src/qemu/qemu_domain.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_domain.h | 4 +++ src/qemu/qemu_driver.c | 14 +++++++++ src/qemu/qemu_process.c | 9 ++++++ 4 files changed, 103 insertions(+)
Reviewed-by: Pavel Hrdina <phrdina@redhat.com>
Thanks for the review. I fixed the issues and pushed this series. Jirka
participants (3)
-
Jiri Denemark
-
Marc Hartmayer
-
Pavel Hrdina