[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1425300248-40277-12-git-send-email-mimu@linux.vnet.ibm.com>
Date: Mon, 2 Mar 2015 13:44:03 +0100
From: Michael Mueller <mimu@...ux.vnet.ibm.com>
To: qemu-devel@...gnu.org, kvm@...r.kernel.org,
linux-s390@...r.kernel.org, linux-kernel@...r.kernel.org
Cc: Gleb Natapov <gleb@...nel.org>, Alexander Graf <agraf@...e.de>,
Christian Borntraeger <borntraeger@...ibm.com>,
"Jason J. Herne" <jjherne@...ux.vnet.ibm.com>,
Cornelia Huck <cornelia.huck@...ibm.com>,
Paolo Bonzini <pbonzini@...hat.com>,
Andreas Faerber <afaerber@...e.de>,
Richard Henderson <rth@...ddle.net>,
Eduardo Habkost <ehabkost@...hat.com>,
Michael Mueller <mimu@...ux.vnet.ibm.com>
Subject: [PATCH v3 11/16] target-s390x: Add cpu class initialization routines
This patch provides routines to dynamically update the previously defined
S390 cpu classes in the current host context. The main function performing
this process is s390_setup_cpu_classes(). It takes the current host context
as parameter to setup the classes accordingly. It basically performs the
following sub-tasks:
- Update of cpu classes with accelerator specific host and QEMU properties
- Mark adequate cpu class as default cpu class to be used for cpu model 'host'
- Invalidate cpu classes not supported by this hosting machine
- Define machine type aliases to latest GA number of a processor model
- Define aliases for common cpu model names
- Set cpu model alias 'host' to default cpu class
Forthermore the patch provides the following routines:
- cpu_desc_avail(), s390 specific stub indicating that list_cpus() can run
- s390_cpu_classes_initialized(), test if cpu classes have been initialized
- s390_probe_mode(), indicates if probe mode is active
Signed-off-by: Michael Mueller <mimu@...ux.vnet.ibm.com>
---
target-s390x/cpu-models.c | 458 ++++++++++++++++++++++++++++++++++++++++++++++
target-s390x/cpu-models.h | 26 +++
target-s390x/cpu.c | 17 +-
target-s390x/kvm.c | 4 +-
4 files changed, 503 insertions(+), 2 deletions(-)
diff --git a/target-s390x/cpu-models.c b/target-s390x/cpu-models.c
index 608189d..83e590a 100644
--- a/target-s390x/cpu-models.c
+++ b/target-s390x/cpu-models.c
@@ -13,6 +13,7 @@
#include "qemu-common.h"
#include "cpu-models.h"
#include "gen-facilities.h"
+#include "qemu/error-report.h"
#define S390_PROC_DEF(_name, _cpu_id, _desc) \
static void \
@@ -87,8 +88,41 @@ S390_PROC_DEF("2827-ga1", CPU_S390_2827_GA1, "IBM zEnterprise EC12 GA1")
S390_PROC_DEF("2827-ga2", CPU_S390_2827_GA2, "IBM zEnterprise EC12 GA2")
S390_PROC_DEF("2828-ga1", CPU_S390_2828_GA1, "IBM zEnterprise BC12 GA1")
+/* some types for calls to g_list_foreach() with parameters */
+typedef struct ParmBoolShortShortAccel {
+ bool valid;
+ unsigned short type;
+ union {
+ unsigned short class;
+ unsigned short gen;
+ unsigned short ga;
+ };
+ AccelId accel;
+} ParmBoolShortShortAccel;
+
+typedef struct ParmAddrAddrAccel {
+ S390MachineProps *prop;
+ S390CPUClass *host_cc;
+ AccelId accel;
+} ParmAddrAddrAccel;
+
static GSList *s390_cpu_aliases;
+/* compare order of two cpu classes for ascending sort */
+gint s390_cpu_class_asc_order_compare(gconstpointer a, gconstpointer b)
+{
+ S390CPUClass *cc_a = S390_CPU_CLASS((ObjectClass *) a);
+ S390CPUClass *cc_b = S390_CPU_CLASS((ObjectClass *) b);
+
+ if (cc_a->mach.order < cc_b->mach.order) {
+ return -1;
+ }
+ if (cc_a->mach.order > cc_b->mach.order) {
+ return 1;
+ }
+ return 0;
+}
+
static gint s390_cpu_compare_class_name(gconstpointer a, gconstpointer b)
{
const char *aname = object_class_get_name((ObjectClass *) a);
@@ -176,3 +210,427 @@ int set_s390_cpu_alias(const char *name, const char *model)
return 0;
}
+/* return machine class for specific machine type */
+static void s390_machine_class_test_cpu_class(gpointer data, gpointer user_data)
+{
+ S390CPUClass *cc = S390_CPU_CLASS((ObjectClass *) data);
+ ParmBoolShortShortAccel *parm = user_data;
+
+ if (parm->valid || !cc->proc.type || parm->type != cc->proc.type) {
+ return;
+ }
+
+ parm->class = cc->mach.class;
+ parm->valid = true;
+}
+
+/* return machine class by machine type */
+static unsigned short machine_class(unsigned short type, void *user_data)
+{
+ GSList *list = object_class_get_list(TYPE_S390_CPU, false);
+ ParmBoolShortShortAccel parm_class, *parm = user_data;
+
+ if (parm->type != type) {
+ parm->class = 0;
+ }
+ if (!parm->class) {
+ parm_class.type = type;
+ parm_class.class = 0;
+ parm_class.valid = false;
+ g_slist_foreach(list, (GFunc) s390_machine_class_test_cpu_class,
+ &parm_class);
+ g_slist_free(list);
+ if (parm_class.valid) {
+ parm->class = parm_class.class;
+ }
+ }
+ parm->type = type;
+
+ return parm->class;
+}
+
+/* return CMOS generation for specific machine type */
+static void s390_machine_class_test_cpu_gen(gpointer data, gpointer user_data)
+{
+ S390CPUClass *cc = S390_CPU_CLASS((ObjectClass *) data);
+ ParmBoolShortShortAccel *parm = user_data;
+
+ if (parm->valid) {
+ return;
+ }
+
+ if (parm->type == cc->proc.type) {
+ parm->gen = cc->proc.gen;
+ parm->valid = true;
+ }
+}
+
+/* return CMOS generation by machine type */
+static uint16_t machine_gen(unsigned short type)
+{
+ GSList *list = object_class_get_list(TYPE_S390_CPU, false);
+ ParmBoolShortShortAccel parm;
+
+ parm.type = type;
+ parm.gen = 0;
+ parm.valid = false;
+ g_slist_foreach(list, (GFunc) s390_machine_class_test_cpu_gen, &parm);
+ g_slist_free(list);
+
+ return parm.gen;
+}
+
+/* mark cpu class, used in host cpu model case */
+static void s390_mark_host_cpu_class(gpointer data, gpointer user_data)
+{
+ S390CPUClass *cc = S390_CPU_CLASS((ObjectClass *) data);
+ ParmAddrAddrAccel *parm = user_data;
+ ParmBoolShortShortAccel parm_tc;
+
+ if (!cc->is_active[parm->accel]) {
+ return;
+ }
+
+ parm_tc.type = 0;
+ parm_tc.class = 0;
+ if (cc->mach.class != machine_class(cpuid_type(parm->prop->cpuid),
+ &parm_tc)) {
+ /* sort out machines that differ from host machine class */
+ return;
+ }
+ if (!parm->host_cc) {
+ /* use first matching machine type */
+ cc->is_host[parm->accel] = true;
+ parm->host_cc = cc;
+ return;
+ }
+ if (cc->proc.gen > machine_gen(cpuid_type(parm->prop->cpuid))) {
+ /* sort out CMOS generations later than hosts generation */
+ cc->is_active[parm->accel] = false;
+ return;
+ }
+ if (cc->mach.order > parm->host_cc->mach.order) {
+ /* select later machine as host */
+ parm->host_cc->is_host[parm->accel] = false;
+ cc->is_host[parm->accel] = true;
+ parm->host_cc = cc;
+ }
+}
+
+/* update a specific cpu model class with host retrieved configuration */
+static void s390_update_cpu_class(gpointer data, gpointer user_data)
+{
+ ObjectClass *oc = data;
+ ParmAddrAddrAccel *parm = user_data;
+ S390CPUClass *cc = S390_CPU_CLASS(oc);
+ uint64_t fac_list[FAC_LIST_CPU_S390_SIZE_UINT64];
+ uint64_t nbits = FAC_LIST_CPU_S390_SIZE_UINT1;
+
+ if (!cc->proc.type) {
+ return;
+ }
+
+ /* Set processor identifier */
+ cc->proc.id = cpuid_id(parm->prop->cpuid);
+
+ /*
+ * Define model specific IBC value in current host context.
+ * IBC was introduced with CMOS version 10 i.e. type 2097.
+ * For older CPUs it is assumed to be 0x000. The BC system
+ * has always the same IBC version as the previous EC system.
+ * If the host supports IBC but not the requested type, it
+ * will be set to the oldest supported value.
+ */
+ if (has_ibc(parm->prop->ibc_range)) {
+ if (cc->proc.gen >= S390_CMOS_G10) {
+ cc->proc.ibc = ((cc->proc.gen - S390_CMOS_G10) << 4);
+ cc->proc.ibc += cc->mach.ga;
+ if (cc->mach.class == S390_BC) {
+ cc->proc.ibc++;
+ }
+ if (cc->proc.ibc < oldest_ibc(parm->prop->ibc_range)) {
+ cc->proc.ibc = oldest_ibc(parm->prop->ibc_range);
+ }
+ if (cc->proc.ibc > newest_ibc(parm->prop->ibc_range)) {
+ cc->proc.ibc = newest_ibc(parm->prop->ibc_range);
+ }
+ } else {
+ cc->proc.ibc = oldest_ibc(parm->prop->ibc_range);
+ }
+ }
+
+ /*
+ * Processor generation and GA level specific facility properties:
+ *
+ * - cc->fac_list (RFL):
+ * resulting facility list to be requested for guest cpus
+ * - cc->proc.fac_list (PFL):
+ * facility list defined per processor generation and GA level
+ *
+ * Machine specific facility properties reported by the host:
+ *
+ * - parm->prop->fac_list (MFL):
+ * host specifc facility list, might be reduced by some facilities
+ * in case the host is backed by z/VM and not a LPAR
+ * - parm->prop->fac_list_mask (MFM):
+ * host specific facility list mask containing facilities
+ *
+ * QEMU defined properties:
+ *
+ * - qemu_s390_fac_list_mask (QFM):
+ * locally defined facilities, they are added to the set of
+ * facilities requested for a guest vcpu. They are visible in
+ * the guest and require qemu side instruction handling
+ *
+ * The calculation for the vcpu specific facility list (RFL) from the
+ * above defined lists/masks works as follows:
+ *
+ * RFL = PFL & (QFM | MFM)
+ *
+ * Set resulting/desired facilities of given cpu class
+ */
+ bitmap_or(cc->fac_list, qemu_s390_fac_list_mask,
+ parm->prop->fac_list_mask, nbits);
+ bitmap_and(cc->fac_list, cc->fac_list, cc->proc.fac_list, nbits);
+
+ /*
+ * Finally, mark the cpu class inactive if not all resulting/desired
+ * facilities are offered by the host.
+ * (RFL & MFL) != RFL
+ */
+ bitmap_and(fac_list, cc->fac_list, parm->prop->fac_list, nbits);
+ if (!bitmap_equal(fac_list, cc->fac_list, nbits)) {
+ cc->is_active[parm->accel] = false;
+ }
+}
+
+/* a cpu class that is newer then the current host */
+static void s390_set_not_supported_cpu_class(gpointer data, gpointer user_data)
+{
+ S390CPUClass *cc = S390_CPU_CLASS((ObjectClass *) data);
+ ParmAddrAddrAccel *parm = user_data;
+
+ if (!cc->is_active[parm->accel]) {
+ return;
+ }
+ if (cc->proc.gen < parm->host_cc->proc.gen) {
+ return;
+ }
+ if (cc->proc.gen == parm->host_cc->proc.gen) {
+ if (cc->mach.class == parm->host_cc->mach.class &&
+ cc->mach.ga <= parm->host_cc->mach.ga) {
+ return;
+ }
+ if (cc->mach.class == S390_EC &&
+ cc->mach.ga > parm->host_cc->mach.ga) {
+ return;
+ }
+ if (cc->mach.class == S390_BC &&
+ cc->mach.ga < parm->host_cc->mach.ga) {
+ return;
+ }
+ }
+ cc->is_active[parm->accel] = false;
+}
+
+/* set alias by type and ga */
+static int set_s390_cpu_alias_by_type_ga(unsigned short type, unsigned short ga)
+{
+ char name[8], model[16];
+
+ snprintf(name, sizeof(name), "%04x", type);
+ snprintf(model, sizeof(model), "%04x-ga%u", type, ga);
+
+ return set_s390_cpu_alias(name, model);
+}
+
+/* set alias if system has latest ga of a type */
+static void s390_set_ga_alias(gpointer data, gpointer user_data)
+{
+ S390CPUClass *cc = S390_CPU_CLASS((ObjectClass *) data);
+ ParmBoolShortShortAccel *parm = user_data;
+
+ if (!cc->is_active[parm->accel]) {
+ return;
+ }
+ if (!parm->type) {
+ parm->type = cc->proc.type;
+ }
+ if (cc->proc.type == parm->type) {
+ parm->ga = cc->mach.ga;
+ return;
+ }
+ set_s390_cpu_alias_by_type_ga(parm->type, parm->ga);
+ parm->type = cc->proc.type;
+ parm->ga = cc->mach.ga;
+}
+
+/* set host marked cpu class as alias to respective class */
+static void s390_set_host_alias_from_cpu_class(gpointer data,
+ gpointer user_data)
+{
+ S390CPUClass *cc = S390_CPU_CLASS((ObjectClass *) data);
+ ParmAddrAddrAccel *parm = user_data;
+
+ char model[16];
+
+ if (!cc->is_active[parm->accel] || !cc->is_host[parm->accel]) {
+ return;
+ }
+ snprintf(model, sizeof(model), "%04x-ga%u", cc->proc.type, cc->mach.ga);
+ set_s390_cpu_alias("host", model);
+}
+
+/**
+ * s390_setup_cpu_classes:
+ * @accel: the accelerator id
+ * @prop: the machine property structure's address
+ *
+ * This function validates the defined cpu classes against the given
+ * machine properties @prop. Only cpu classes that are runnable on the
+ * current host will be set active. In addition the corresponding
+ * cpuid, ibc value and the active set of facilities will be
+ * initialized. When in probe mode, the function can be called for
+ * several accelerators @accel. Though aliases will only be added when
+ * not in probe mode.
+ *
+ * Returns: %0 in case of success -%EINVAL elsewise.
+ *
+ * Since: 2.3
+ */
+int s390_setup_cpu_classes(AccelId accel, S390MachineProps *prop)
+{
+ GSList *list;
+ ParmAddrAddrAccel parm;
+ ParmBoolShortShortAccel parm_alias;
+
+ list = object_class_get_list(TYPE_S390_CPU, false);
+ list = g_slist_sort(list, s390_cpu_class_asc_order_compare);
+
+ /* update cpu classes with accellerator properties */
+ parm.accel = accel;
+ parm.prop = prop;
+ g_slist_foreach(list, (GFunc) s390_update_cpu_class, (gpointer) &parm);
+
+ /* define cpu model "host" */
+ parm.host_cc = NULL;
+ g_slist_foreach(list, (GFunc) s390_mark_host_cpu_class, (gpointer) &parm);
+
+ if (!parm.host_cc) {
+ error_report("Failed to mark host cpu class");
+ return -EINVAL;
+ }
+
+ /* inactivate cpu classes not supported by this host */
+ g_slist_foreach(list, (GFunc) s390_set_not_supported_cpu_class, &parm);
+
+ /* set machine type aliases to latest ga of model (e.g. 2064 -> 2064-ga3) */
+ parm_alias.accel = accel;
+ parm_alias.type = 0;
+ parm_alias.ga = 0;
+ g_slist_foreach(list, (GFunc) s390_set_ga_alias, &parm_alias);
+ set_s390_cpu_alias_by_type_ga(parm_alias.type, parm_alias.ga);
+
+ if (!s390_probe_mode()) {
+ /* set aliases for common model names to machine types */
+ set_s390_cpu_alias("z900", "2064");
+ set_s390_cpu_alias("z800", "2066");
+ set_s390_cpu_alias("z990", "2084");
+ set_s390_cpu_alias("z890", "2086");
+ set_s390_cpu_alias("z9-109", "2094-ga1");
+ set_s390_cpu_alias("z9", "2094");
+ set_s390_cpu_alias("z9-ec", "2094");
+ set_s390_cpu_alias("z9-bc", "2096");
+ set_s390_cpu_alias("z10", "2097");
+ set_s390_cpu_alias("z10-ec", "2097");
+ set_s390_cpu_alias("z10-bc", "2098");
+ set_s390_cpu_alias("z196", "2817");
+ set_s390_cpu_alias("z114", "2818");
+ set_s390_cpu_alias("zEC12", "2827");
+ set_s390_cpu_alias("zBC12", "2828");
+ /* set alias for cpu model "host" at end of list */
+ g_slist_foreach(list, (GFunc) s390_set_host_alias_from_cpu_class,
+ &parm);
+ }
+
+ g_slist_free(list);
+ return 0;
+}
+
+/* list all supported cpu models and alias names */
+void s390_cpu_list_entry(gpointer data, gpointer user_data)
+{
+ ObjectClass *alias_oc, *oc = data;
+ CPUListState *s = user_data;
+ DeviceClass *dc = DEVICE_CLASS(oc);
+ S390CPUClass *cc = S390_CPU_CLASS(oc);
+ const char *typename = object_class_get_name(oc);
+ S390CPUAlias *alias;
+ AccelId accel;
+ GSList *item;
+ char *name;
+
+ if (!kvm_enabled()) {
+ return;
+ }
+ accel = ACCEL_ID_KVM;
+ if (!cc->is_active[accel]) {
+ return;
+ }
+ name = g_strndup(typename, strlen(typename) - strlen("-" TYPE_S390_CPU));
+ (*s->cpu_fprintf)(s->file, "s390 %-10s %s\n", name, dc->desc);
+
+ for (item = s390_cpu_aliases; item != NULL; item = item->next) {
+ alias = (S390CPUAlias *) item->data;
+ alias_oc = s390_cpu_class_by_name(alias->model);
+ if (alias_oc != oc) {
+ continue;
+ }
+ (*s->cpu_fprintf)(s->file, "s390 %-10s (alias for %s)\n",
+ alias->name, name);
+ }
+
+ g_free(name);
+}
+
+/**
+ * s390_cpu_classes_initialized:
+ *
+ * This function indicates if the all cpu classes and their properties
+ * have been initialized.
+ *
+ * Returns: a boolean value.
+ *
+ * Since: 2.3
+ */
+bool s390_cpu_classes_initialized(void)
+{
+ if (kvm_enabled()) {
+ return kvm_s390_cpu_classes_initialized();
+ }
+ return false;
+}
+
+bool cpu_desc_avail(void)
+{
+ return s390_cpu_classes_initialized();
+}
+
+/**
+ * s390_probe_mode:
+ *
+ * This function indicates that cpu class initialization takes place
+ * in probe mode.
+ *
+ * Returns: a boolean value.
+ *
+ * Since: 2.3
+ */
+bool s390_probe_mode(void)
+{
+ if (kvm_enabled() && kvm_s390_probe_mode()) {
+ return true;
+ }
+ return false;
+}
+
diff --git a/target-s390x/cpu-models.h b/target-s390x/cpu-models.h
index b6b57d4..aa81c9b 100644
--- a/target-s390x/cpu-models.h
+++ b/target-s390x/cpu-models.h
@@ -29,12 +29,32 @@
#define S390_DEF_ID 0xdecade
#define S390_DEF_TYPE 0x2064
+/* first s390 CMOS generation supporting IBC */
+#define S390_CMOS_G10 0xa
+
#define cpu_type(x) (((x) >> 0) & 0xffff)
#define cpu_order(x) (((x) >> 16) & 0xffff)
#define cpu_ga(x) (((x) >> 16) & 0xf)
#define cpu_class(x) (((x) >> 20) & 0x3)
#define cpu_generation(x) (((x) >> 24) & 0xff)
+#define cpuid_type(x) (((x) >> 16) & 0xffff)
+#define cpuid_id(x) (((x) >> 32) & 0xffffff)
+#define cpuid_ver(x) (((x) >> 56) & 0xff)
+
+#define type_cpuid(x) ((uint64_t)((x) & 0xffff) << 16)
+#define id_cpuid(x) ((uint64_t)((x) & 0xffffff) << 32)
+#define ver_cpuid(x) ((uint64_t)((x) & 0xff) << 56)
+
+#define oldest_ibc(x) (((uint32_t)(x) >> 16) & 0xfff)
+#define newest_ibc(x) ((uint32_t)(x) & 0xfff)
+#define has_ibc(x) (oldest_ibc(x) != 0)
+
+#define S390_DEF_CPUID \
+ (ver_cpuid(S390_DEF_VERSION) | \
+ id_cpuid(S390_DEF_ID) | \
+ type_cpuid(S390_DEF_TYPE))
+
ObjectClass *s390_cpu_class_by_name(const char *name);
int set_s390_cpu_alias(const char *name, const char *model);
@@ -85,6 +105,12 @@ static inline bool kvm_s390_probe_mode(void)
}
#endif
+int s390_setup_cpu_classes(AccelId accel, S390MachineProps *prop);
+gint s390_cpu_class_asc_order_compare(gconstpointer a, gconstpointer b);
+void s390_cpu_list_entry(gpointer data, gpointer user_data);
+bool s390_cpu_classes_initialized(void);
+bool s390_probe_mode(void);
+
/*
* bits 0-7 : CMOS generation
* bits 8-9 : reserved
diff --git a/target-s390x/cpu.c b/target-s390x/cpu.c
index 1992910..c9200ee 100644
--- a/target-s390x/cpu.c
+++ b/target-s390x/cpu.c
@@ -41,7 +41,22 @@
void s390_cpu_list(FILE *f, fprintf_function cpu_fprintf)
{
#ifdef CONFIG_KVM
- (*cpu_fprintf)(f, "s390 %16s\n", "host");
+ CPUListState s = {
+ .file = f,
+ .cpu_fprintf = cpu_fprintf,
+ };
+ GSList *list;
+
+ if (kvm_enabled() && s390_cpu_classes_initialized()) {
+ list = object_class_get_list(TYPE_S390_CPU, false);
+ list = g_slist_sort(list, s390_cpu_class_asc_order_compare);
+ g_slist_foreach(list, s390_cpu_list_entry, &s);
+ g_slist_free(list);
+ } else {
+#endif
+ (*cpu_fprintf)(f, "s390 host\n");
+#ifdef CONFIG_KVM
+ }
#endif
}
diff --git a/target-s390x/kvm.c b/target-s390x/kvm.c
index 5404137..07d07b2 100644
--- a/target-s390x/kvm.c
+++ b/target-s390x/kvm.c
@@ -233,7 +233,9 @@ static void kvm_s390_setup_cpu_classes(KVMState *s)
S390MachineProps mach;
if (!kvm_s390_get_machine_props(s, &mach)) {
- cpu_classes_initialized = false;
+ if (!s390_setup_cpu_classes(ACCEL_ID_KVM, &mach)) {
+ cpu_classes_initialized = true;
+ }
}
}
--
1.8.3.1
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Powered by blists - more mailing lists