Blame SOURCES/kvm-spapr-Validate-capabilities-on-migration.patch

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