On Thu, Oct 01, 2026 at 22:36:26 -0400, Rorie Reyes wrote:
When migrating an s390x guest to a target s390x host, it is possible the migration will fail ungracefully due to incompatible CPU models.
Add a pre-migration check for the s390x architecture: when the guest uses a custom or host-model CPU, look up the QEMU capabilities from the driver cache and, if the QEMU_CAPS_QUERY_CPU_MODEL_COMPARISON capability is present, invoke a CPU model comparison between the guest and target hypervisor CPU models.
The check runs before qemuProcessInit() so that the guest CPU from the migration cookie is compared before it can be resolved to the destination host model. The comparison result is distinguished by four cases:
- IDENTICAL or SUBSET: guest CPU model is presumed to be compatible with target host's model. - SUPERSET: guest CPU model is newer than target host's model, or target host does not support features guest requires. - INCOMPATIBLE: guest CPU model cannot run on target host. Report the list of features the target is missing (if any). - Probe error: propagated as VIR_ERR_CPU_INCOMPATIBLE from the monitor layer.
Signed-off-by: Rorie Reyes <rreyes@linux.ibm.com> --- src/qemu/qemu_migration.c | 114 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+)
diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c
qemu_migration.c is a wrong place to handle this. We have a code that already checks CPU compatibility for any QEMU start (fresh start, migration, snapshot, restore) and there's no reason for s390 to come up with its own place to deal with this. Also this path would only address migration, while I believe we want similar behavior for snapshots and restore as well. The CPU compatibility code is in qemuProcessUpdateGuestCPU which calls to qemuDomainCheckCPU. So if we refactor qemuDomainCheckCPU in a way I described in my previous reply, we should get migration checks for free. The flags parameter in qemuProcessUpdateGuestCPU says whether it was called during a fresh start or not and can be used to tune the behavior if needed.
index 4a43ab83b0..b77bdd4370 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -3308,6 +3308,90 @@ qemuMigrationDstPrepareAnyBlockDirtyBitmaps(virDomainObj *vm, }
+/** + * qemuMigrationDstCheckCPUModelCompat: + * + * Run a short-lived QMP probe to compare @guestCPU against @hvCPU on the + * destination host. Returns 0 if the guest CPU is compatible (identical + * or a subset of what the host provides), -1 if incompatible. + * + * On incompatibility an error is reported via virReportError before returning + * -1. Probe errors (e.g. the destination QEMU does not recognise a CPU + * property) are reported as VIR_ERR_CPU_INCOMPATIBLE by the monitor layer and + * are propagated as-is. + */ +static int +qemuMigrationDstCheckCPUModelCompat(virQEMUCaps *qemuCaps, + const char *libDir, + uid_t runUid, + gid_t runGid, + virCPUDef *hvCPU, + virCPUDef *guestCPU) +{ + g_autofree char **responsibleProps = NULL; + qemuCPUModelCompatResult cmp = qemuProcessCPUModelComparison(qemuCaps, libDir, + runUid, runGid, + guestCPU, hvCPU, + &responsibleProps); + + switch (cmp) { + case QEMU_CPU_COMPAT_IDENTICAL: + case QEMU_CPU_COMPAT_SUBSET: + /* Guest CPU model is presumed to be compatible with target host's model. */ + return 0; + + case QEMU_CPU_COMPAT_SUPERSET: + /* Either guest CPU model is newer than target host's model, or target + * host does not support features required by guest. */ + virReportError(VIR_ERR_CPU_INCOMPATIBLE, "%s", + _("guest CPU model is a newer generation than the destination host CPU model")); + return -1; + + case QEMU_CPU_COMPAT_INCOMPATIBLE: + /* Guest CPU model cannot run on target host. Report the list of + * features the target is missing (if any). + * + * s390x will report the property 'type' when CPU models are + * incompatible. Remove it from the responsibleProps list so it + * is not reported as a missing feature. */ + if (responsibleProps) { + size_t i = 0; + size_t j = 0; + + while (responsibleProps[i]) { + if (STREQ(responsibleProps[i], "type")) { + g_free(responsibleProps[i++]); + } else { + responsibleProps[j++] = responsibleProps[i++]; + } + } + responsibleProps[j] = NULL;
OK, this is the filtering I talked about in patch 1 and I think we should do it in the json monitor unless we need to see that info somewhere else, which I believe is not the case.
+ + if (responsibleProps[0]) { + g_autofree char *missing_str = g_strjoinv(", ", responsibleProps); + virReportError(VIR_ERR_CPU_INCOMPATIBLE, + _("destination host CPU model is missing features required by the guest CPU model: %1$s"), + missing_str); + return -1; + } + } ...
Jirka