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 <[email protected]> --- 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 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; + + 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; + } + } + + virReportError(VIR_ERR_CPU_INCOMPATIBLE, + _("guest CPU model '%1$s' is not compatible with host CPU model '%2$s'"), + guestCPU->model ? guestCPU->model : _("unknown"), + hvCPU->model ? hvCPU->model : _("unknown")); + return -1; + + case QEMU_CPU_COMPAT_ERROR: + case QEMU_CPU_COMPAT_LAST: + default: + break; + } + + return -1; +} + + static int qemuMigrationDstPrepareActive(virQEMUDriver *driver, virDomainObj *vm, @@ -3354,6 +3438,36 @@ qemuMigrationDstPrepareActive(virQEMUDriver *driver, startFlags = VIR_QEMU_PROCESS_START_AUTODESTROY; + /* Check CPU model compatibility before starting the guest. + * Snapshot the guest CPU from the migration cookie before qemuProcessInit + * can resolve and replace it with the destination host CPU model. + * This allows libvirt to provide a clear error message and perform + * proper cleanup if the destination host doesn't support the required + * CPU features. + */ + if (ARCH_IS_S390(vm->def->os.arch) && + mig->cpu && + (mig->cpu->mode == VIR_CPU_MODE_CUSTOM || + mig->cpu->mode == VIR_CPU_MODE_HOST_MODEL) && + mig->cpu->model) { + g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver); + g_autoptr(virQEMUCaps) qemuCaps = virQEMUCapsCacheLookup(driver->qemuCapsCache, + vm->def->emulator); + + if (qemuCaps && + virQEMUCapsGet(qemuCaps, QEMU_CAPS_QUERY_CPU_MODEL_COMPARISON)) { + virCPUDef *hvCPU = virQEMUCapsGetHostModel(qemuCaps, + vm->def->virtType, + VIR_QEMU_CAPS_HOST_CPU_REPORTED); + + if (hvCPU && + qemuMigrationDstCheckCPUModelCompat(qemuCaps, + cfg->libDir, cfg->user, cfg->group, + hvCPU, mig->cpu) < 0) + goto error; + } + } + if (qemuProcessInit(driver, vm, mig->cpu, VIR_ASYNC_JOB_MIGRATION_IN, true, startFlags) < 0) goto error; -- 2.48.1
