From a6549068139448cfd38e4b1e602fbaba5ed32a97 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Fri, 19 Jan 2018 02:34:38 +0100 Subject: [PATCH 10/21] spapr: Validate capabilities on migration RH-Author: David Gibson Message-id: <20180119023442.28577-4-dgibson@redhat.com> Patchwork-id: 78669 O-Subject: [RHEL-7.5 qemu-kvm-rhev PATCH 3/7] spapr: Validate capabilities on migration Bugzilla: 1523414 RH-Acked-by: Laurent Vivier RH-Acked-by: Thomas Huth RH-Acked-by: Miroslav Rezanina From: David Gibson Now that the "pseries" machine type implements optional capabilities (well, one so far) there's the possibility of having different capabilities available at either end of a migration. Although arguably a user error, it would be nice to catch this situation and fail as gracefully as we can. This adds code to migrate the capabilities flags. These aren't pulled directly into the destination's configuration since what the user has specified on the destination command line should take precedence. However, they are checked against the destination capabilities. If the source was using a capability which is absent on the destination, we fail the migration, since that could easily cause a guest crash or other bad behaviour. If the source lacked a capability which is present on the destination we warn, but allow the migration to proceed. Signed-off-by: David Gibson Reviewed-by: Greg Kurz (cherry picked from commit be85537d654565e35e359a74b46fc08b7956525c) Adjusted because we don't have 44b1ff31 "migration: pre_save return int" downstream, which means spapr_caps_pre_save() needs a different type. Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1523414 Signed-off-by: David Gibson Signed-off-by: Miroslav Rezanina --- hw/ppc/spapr.c | 6 ++++ hw/ppc/spapr_caps.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++++-- include/hw/ppc/spapr.h | 6 ++++ 3 files changed, 104 insertions(+), 3 deletions(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 2068a1f..846b906 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -1547,6 +1547,11 @@ static int spapr_post_load(void *opaque, int version_id) sPAPRMachineState *spapr = (sPAPRMachineState *)opaque; int err = 0; + err = spapr_caps_post_migration(spapr); + if (err) { + return err; + } + if (!object_dynamic_cast(OBJECT(spapr->ics), TYPE_ICS_KVM)) { CPUState *cs; CPU_FOREACH(cs) { @@ -1713,6 +1718,7 @@ static const VMStateDescription vmstate_spapr = { &vmstate_spapr_ov5_cas, &vmstate_spapr_patb_entry, &vmstate_spapr_pending_events, + &vmstate_spapr_caps, NULL } }; diff --git a/hw/ppc/spapr_caps.c b/hw/ppc/spapr_caps.c index 3b35b91..2bdc202 100644 --- a/hw/ppc/spapr_caps.c +++ b/hw/ppc/spapr_caps.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ #include "qemu/osdep.h" +#include "qemu/error-report.h" #include "qapi/error.h" #include "qapi/visitor.h" #include "sysemu/hw_accel.h" @@ -83,6 +84,92 @@ static sPAPRCapabilities default_caps_with_cpu(sPAPRMachineState *spapr, return caps; } +static bool spapr_caps_needed(void *opaque) +{ + sPAPRMachineState *spapr = opaque; + + return (spapr->forced_caps.mask != 0) || (spapr->forbidden_caps.mask != 0); +} + +/* This has to be called from the top-level spapr post_load, not the + * caps specific one. Otherwise it wouldn't be called when the source + * caps are all defaults, which could still conflict with overridden + * caps on the destination */ +int spapr_caps_post_migration(sPAPRMachineState *spapr) +{ + uint64_t allcaps = 0; + int i; + bool ok = true; + sPAPRCapabilities dstcaps = spapr->effective_caps; + sPAPRCapabilities srccaps; + + srccaps = default_caps_with_cpu(spapr, first_cpu); + srccaps.mask |= spapr->mig_forced_caps.mask; + srccaps.mask &= ~spapr->mig_forbidden_caps.mask; + + for (i = 0; i < ARRAY_SIZE(capability_table); i++) { + sPAPRCapabilityInfo *info = &capability_table[i]; + + allcaps |= info->flag; + + if ((srccaps.mask & info->flag) && !(dstcaps.mask & info->flag)) { + error_report("cap-%s=on in incoming stream, but off in destination", + info->name); + ok = false; + } + + if (!(srccaps.mask & info->flag) && (dstcaps.mask & info->flag)) { + warn_report("cap-%s=off in incoming stream, but on in destination", + info->name); + } + } + + if (spapr->mig_forced_caps.mask & ~allcaps) { + error_report( + "Unknown capabilities 0x%"PRIx64" enabled in incoming stream", + spapr->mig_forced_caps.mask & ~allcaps); + ok = false; + } + if (spapr->mig_forbidden_caps.mask & ~allcaps) { + warn_report( + "Unknown capabilities 0x%"PRIx64" disabled in incoming stream", + spapr->mig_forbidden_caps.mask & ~allcaps); + } + + return ok ? 0 : -EINVAL; +} + +static void spapr_caps_pre_save(void *opaque) +{ + sPAPRMachineState *spapr = opaque; + + spapr->mig_forced_caps = spapr->forced_caps; + spapr->mig_forbidden_caps = spapr->forbidden_caps; +} + +static int spapr_caps_pre_load(void *opaque) +{ + sPAPRMachineState *spapr = opaque; + + spapr->mig_forced_caps = spapr_caps(0); + spapr->mig_forbidden_caps = spapr_caps(0); + return 0; +} + +const VMStateDescription vmstate_spapr_caps = { + .name = "spapr/caps", + .version_id = 1, + .minimum_version_id = 1, + .needed = spapr_caps_needed, + .pre_save = spapr_caps_pre_save, + .pre_load = spapr_caps_pre_load, + .fields = (VMStateField[]) { + VMSTATE_UINT64(mig_forced_caps.mask, sPAPRMachineState), + VMSTATE_UINT64(mig_forbidden_caps.mask, sPAPRMachineState), + VMSTATE_END_OF_LIST() + }, +}; + void spapr_caps_reset(sPAPRMachineState *spapr) { Error *local_err = NULL; @@ -92,6 +179,11 @@ void spapr_caps_reset(sPAPRMachineState *spapr) /* First compute the actual set of caps we're running with.. */ caps = default_caps_with_cpu(spapr, first_cpu); + /* Remove unnecessary forced/forbidden bits (this will help us + * with migration) */ + spapr->forced_caps.mask &= ~caps.mask; + spapr->forbidden_caps.mask &= caps.mask; + caps.mask |= spapr->forced_caps.mask; caps.mask &= ~spapr->forbidden_caps.mask; @@ -175,9 +267,6 @@ void spapr_caps_validate(sPAPRMachineState *spapr, Error **errp) error_setg(errp, "Some sPAPR capabilities set both on and off"); return; } - - /* Check for any caps incompatible with other caps. Nothing to do - * yet */ } void spapr_caps_add_properties(sPAPRMachineClass *smc, Error **errp) diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index 77dc3fb..f32967a 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -54,6 +54,8 @@ typedef enum { * Capabilities */ +/* These bits go in the migration stream, so they can't be reassigned */ + /* Hardware Transactional Memory */ #define SPAPR_CAP_HTM 0x0000000000000001ULL @@ -144,6 +146,7 @@ struct sPAPRMachineState { const char *icp_type; sPAPRCapabilities forced_caps, forbidden_caps; + sPAPRCapabilities mig_forced_caps, mig_forbidden_caps; sPAPRCapabilities effective_caps; }; @@ -726,6 +729,8 @@ void spapr_do_system_reset_on_cpu(CPUState *cs, run_on_cpu_data arg); /* * Handling of optional capabilities */ +extern const VMStateDescription vmstate_spapr_caps; + static inline sPAPRCapabilities spapr_caps(uint64_t mask) { sPAPRCapabilities caps = { mask }; @@ -740,5 +745,6 @@ static inline bool spapr_has_cap(sPAPRMachineState *spapr, uint64_t cap) void spapr_caps_reset(sPAPRMachineState *spapr); void spapr_caps_validate(sPAPRMachineState *spapr, Error **errp); void spapr_caps_add_properties(sPAPRMachineClass *smc, Error **errp); +int spapr_caps_post_migration(sPAPRMachineState *spapr); #endif /* HW_SPAPR_H */ -- 1.8.3.1