qemu-s390x
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH v2 2/2] s390x/cpumodel: Introduce dynamic feature groups


From: David Hildenbrand
Subject: [PATCH v2 2/2] s390x/cpumodel: Introduce dynamic feature groups
Date: Mon, 25 Nov 2019 18:20:31 +0100

For a specific CPU model, we have a lot of feature variability depending on
- The microcode version of the HW
- The hypervisor we're running on (LPAR vs. KVM vs. z/VM)
- The hypervisor version we're running on
- The KVM version
- KVM module parameters (especially, "nested=1")
- The accelerator

Our default models are migration safe, however can only be changed
between QEMU releases (glued to QEMU machine). This somewhat collides
with the feature variability we have. E.g., the z13 model will not run
under TCG. There is the demand from higher levels in the stack to "have the
best CPU model possible on a given accelerator, firmware and HW", which
should especially include all features that fix security issues.
Especially, if we have a new feature due to a security flaw, we want to
have a way to backport this feature to older QEMU versions and a way to
automatically enable it when asked.

This is where dynamic CPU feature groups come into play. They allow to
first disable all features that would be enabled as default for a QEMU
machine, to then enable a dynamic set of features (depending on the
CPU definition, the accelerator, and the host).

Get the best possible feature set (e.g., excluding deprecated features) for
a CPU definition in the configuration
    -cpu z14,all-features=off,recommended-features=on

Get the maximum possible feature set (e.g., including deprecated
features) for a CPU definition in the configuration ("everything that
could be enabled"):
    -cpu z14,all-features=off,available-features=on

Get all valid features for a CPU definition:
    -cpu z14,all-features=on

The idea of using feature flags for this use case instead of introducing
new models was brought up by Eduardo Habkost.

The best possible features will then, for example, include nested
virtualization ("SIE" feature) when KVM+HW support is enabled, or fixes via
microcode updates (e.g., spectre) - something we cannot have in the
default models of older QEMU machines.

As soon as dynamic feature groups are used, the CPU model becomes
migration-unsafe. Upper layers can expand these models to migration-safe
and static variants, allowing them to be migrated.

Signed-off-by: David Hildenbrand <address@hidden>
---
 target/s390x/cpu_features.c |  29 ++++++++
 target/s390x/cpu_features.h |  14 ++++
 target/s390x/cpu_models.c   | 127 ++++++++++++++++++++++++++++++++++++
 3 files changed, 170 insertions(+)

diff --git a/target/s390x/cpu_features.c b/target/s390x/cpu_features.c
index 9f817e3cfa..77deabf375 100644
--- a/target/s390x/cpu_features.c
+++ b/target/s390x/cpu_features.c
@@ -166,6 +166,35 @@ void s390_feat_bitmap_to_ascii(const S390FeatBitmap 
features, void *opaque,
     };
 }
 
+#define DYN_FEAT_GROUP_INIT(_name, _group, _desc)    \
+    [S390_DYN_FEAT_GROUP_ ## _group] = {             \
+        .name = _name,                               \
+        .desc = _desc,                               \
+    }
+
+/*
+ * Dynamic feature groups can change between QEMU versions (or even for the
+ * same version on backports) and depend on the selected CPU definition. Most 
of
+ * them also depend on the selected accelerator and the host ("max" model). 
When
+ * used, they turn every model into a migration-unsafe model. Thus, they will
+ * never appear in expaneded CPU models.
+ *
+ * Indexed by dynamic feature group number.
+ */
+static S390DynFeatGroupDef s390_dyn_feature_groups[] = {
+    /* "all" corresponds to our "full" definitions */
+    DYN_FEAT_GROUP_INIT("all-features", ALL, "Features valid for a CPU 
definition"),
+    /* "recommended" does not include deprecated features. */
+    DYN_FEAT_GROUP_INIT("recommended-features", RECOMMENDED, "Available, 
recommended features supported by the accelerator in the current host for a CPU 
definition"),
+    /* "available" includes deprecated features. */
+    DYN_FEAT_GROUP_INIT("available-features", AVAILABLE, "Available features 
supported by the accelerator in the current host for a CPU definition"),
+};
+
+const S390DynFeatGroupDef *s390_dyn_feat_group_def(S390DynFeatGroup group)
+{
+    return &s390_dyn_feature_groups[group];
+}
+
 #define FEAT_GROUP_INIT(_name, _group, _desc)        \
     {                                                \
         .name = _name,                               \
diff --git a/target/s390x/cpu_features.h b/target/s390x/cpu_features.h
index da695a8346..4a2f418cd3 100644
--- a/target/s390x/cpu_features.h
+++ b/target/s390x/cpu_features.h
@@ -76,7 +76,21 @@ typedef struct {
     S390FeatInit init;      /* used to init feat from generated data */
 } S390FeatGroupDef;
 
+/* Definition of a dynamic CPU feature group */
+typedef struct {
+    const char *name;       /* name exposed to the user */
+    const char *desc;       /* description exposed to the user */
+} S390DynFeatGroupDef;
+
+typedef enum {
+    S390_DYN_FEAT_GROUP_ALL,
+    S390_DYN_FEAT_GROUP_RECOMMENDED,
+    S390_DYN_FEAT_GROUP_AVAILABLE,
+    S390_DYN_FEAT_GROUP_MAX,
+} S390DynFeatGroup;
+
 const S390FeatGroupDef *s390_feat_group_def(S390FeatGroup group);
+const S390DynFeatGroupDef *s390_dyn_feat_group_def(S390DynFeatGroup group);
 
 #define BE_BIT_NR(BIT) (BIT ^ (BITS_PER_LONG - 1))
 
diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c
index e6072fab43..e76a70b177 100644
--- a/target/s390x/cpu_models.c
+++ b/target/s390x/cpu_models.c
@@ -417,6 +417,7 @@ static gint s390_cpu_list_compare(gconstpointer a, 
gconstpointer b)
 
 void s390_cpu_list(void)
 {
+    S390DynFeatGroup dyn_group;
     S390FeatGroup group;
     S390Feat feat;
     GSList *list;
@@ -439,6 +440,14 @@ void s390_cpu_list(void)
 
         qemu_printf("%-20s %-50s\n", def->name, def->desc);
     }
+
+    qemu_printf("\nRecognized dyanmic feature groups:\n");
+    for (dyn_group = 0; dyn_group < S390_DYN_FEAT_GROUP_MAX; dyn_group++) {
+        const S390DynFeatGroupDef *def = s390_dyn_feat_group_def(dyn_group);
+
+        qemu_printf("%-20s %-50s\n", def->name, def->desc);
+    }
+
 }
 
 static S390CPUModel *get_max_cpu_model(Error **errp);
@@ -1081,8 +1090,118 @@ static void set_feature_group(Object *obj, Visitor *v, 
const char *name,
     }
 }
 
+static bool get_dyn_features(S390DynFeatGroup group, const S390CPUDef *def,
+                             S390FeatBitmap features)
+{
+    const S390CPUModel *max_model;
+    Error *local_err = NULL;
+    int i;
+
+    switch (group) {
+    case S390_DYN_FEAT_GROUP_ALL:
+        bitmap_copy(features, def->full_feat, S390_FEAT_MAX);
+        break;
+    case S390_DYN_FEAT_GROUP_RECOMMENDED:
+    case S390_DYN_FEAT_GROUP_AVAILABLE:
+        if (kvm_enabled() && kvm_s390_cpu_models_supported()) {
+            return false;
+        }
+        max_model = get_max_cpu_model(&local_err);
+        if (local_err) {
+            error_report_err(local_err);
+            return false;
+        }
+
+        /*
+         * Start with "full" feature set and mask off features that are not
+         * available in the "max" model.
+         */
+        bitmap_and(features, def->full_feat, max_model->features,
+                   S390_FEAT_MAX);
+
+        if (group == S390_DYN_FEAT_GROUP_RECOMMENDED) {
+            /* Mask off deprecated (and experimental) features. */
+            clear_bit(S390_FEAT_CONDITIONAL_SSKE, features);
+            /*
+             * Make sure we pass consistency checks (relevant mostly
+             * for TCG where the "max" model will result in warnings).
+             */
+            for (i = 0; i < ARRAY_SIZE(cpu_feature_dependencies); i++) {
+                if (!test_bit(cpu_feature_dependencies[i][1], features)) {
+                    clear_bit(cpu_feature_dependencies[i][0], features);
+                }
+            }
+        }
+        break;
+    default:
+        g_assert_not_reached();
+    }
+    return true;
+}
+
+static void get_dyn_feature_group(Object *obj, Visitor *v, const char *name,
+                                  void *opaque, Error **errp)
+{
+    S390DynFeatGroup group = (S390DynFeatGroup) opaque;
+    S390FeatBitmap tmp, features = {};
+    S390CPU *cpu = S390_CPU(obj);
+    bool value;
+
+    if (!cpu->model) {
+        error_setg(errp, "Details about the host CPU model are not available, "
+                   "features cannot be queried.");
+        return;
+    } else if (!get_dyn_features(group, cpu->model->def, features)) {
+        error_setg(errp, "Details about the dynamic feature group '%s' "
+                   "are not available.", name);
+        return;
+    }
+
+    /* a group is enabled if all features are enabled */
+    bitmap_and(tmp, cpu->model->features, features, S390_FEAT_MAX);
+    value = bitmap_equal(tmp, features, S390_FEAT_MAX);
+    visit_type_bool(v, name, &value, errp);
+}
+
+static void set_dyn_feature_group(Object *obj, Visitor *v, const char *name,
+                                  void *opaque, Error **errp)
+{
+    S390DynFeatGroup group = (S390DynFeatGroup) opaque;
+    S390FeatBitmap features = {};
+    DeviceState *dev = DEVICE(obj);
+    S390CPU *cpu = S390_CPU(obj);
+    bool value;
+
+    if (dev->realized) {
+        error_setg(errp, "Attempt to set property '%s' on '%s' after "
+                   "it was realized", name, object_get_typename(obj));
+        return;
+    } else if (!cpu->model) {
+        error_setg(errp, "Details about the host CPU model are not available, "
+                   "features cannot be changed.");
+        return;
+    } else if (!get_dyn_features(group, cpu->model->def, features)) {
+        error_setg(errp, "Details about the dynamic feature group '%s' "
+                   "are not available.", name);
+        return;
+    }
+
+    visit_type_bool(v, name, &value, errp);
+    if (*errp) {
+        return;
+    }
+    if (value) {
+        bitmap_or(cpu->model->features, cpu->model->features, features,
+                  S390_FEAT_MAX);
+    } else {
+        bitmap_andnot(cpu->model->features, cpu->model->features, features,
+                      S390_FEAT_MAX);
+    }
+}
+
 void s390_cpu_model_register_props(Object *obj)
 {
+    S390DynFeatGroup dyn_group;
     S390FeatGroup group;
     S390Feat feat;
 
@@ -1098,6 +1217,14 @@ void s390_cpu_model_register_props(Object *obj)
                             set_feature_group, NULL, (void *) group, NULL);
         object_property_set_description(obj, def->name, def->desc , NULL);
     }
+    for (dyn_group = 0; dyn_group < S390_DYN_FEAT_GROUP_MAX; dyn_group++) {
+        const S390DynFeatGroupDef *def = s390_dyn_feat_group_def(dyn_group);
+
+        object_property_add(obj, def->name, "bool", get_dyn_feature_group,
+                            set_dyn_feature_group, NULL, (void *) dyn_group,
+                            NULL);
+        object_property_set_description(obj, def->name, def->desc , NULL);
+    }
 }
 
 static void s390_cpu_model_initfn(Object *obj)
-- 
2.21.0




reply via email to

[Prev in Thread] Current Thread [Next in Thread]