[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1424183053-4310-11-git-send-email-mimu@linux.vnet.ibm.com>
Date: Tue, 17 Feb 2015 15:24:08 +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>,
Michael Mueller <mimu@...ux.vnet.ibm.com>
Subject: [RFC PATCH v2 10/15] cpu-model/s390: 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_test_facility(), facility bit probe function for QEMU land
- s390_probe_mode, test if running in probe mode
Signed-off-by: Michael Mueller <mimu@...ux.vnet.ibm.com>
---
target-s390x/cpu-models.c | 464 ++++++++++++++++++++++++++++++++++++++++++++++
target-s390x/cpu-models.h | 27 +++
target-s390x/cpu.c | 17 +-
target-s390x/kvm.c | 4 +-
4 files changed, 510 insertions(+), 2 deletions(-)
diff --git a/target-s390x/cpu-models.c b/target-s390x/cpu-models.c
index 8d2c2e2..9f40998 100644
--- a/target-s390x/cpu-models.c
+++ b/target-s390x/cpu-models.c
@@ -12,6 +12,7 @@
#include "qemu-common.h"
#include "cpu-models.h"
+#include "qemu/error-report.h"
#define S390_FAC_NAME(n, _cpu_id) glue(glue(glue(FAC, n), _), _cpu_id)
@@ -90,8 +91,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);
@@ -167,3 +201,433 @@ 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);
+ unsigned int i;
+
+ 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
+ */
+ for (i = 0; i < S390_FAC_LIST_SIZE_UINT64; i++) {
+ cc->fac_list[i] = cc->proc->fac_list[i] &
+ (qemu_s390_fac_list_mask[i] | parm->prop->fac_list_mask[i]);
+ }
+
+ /*
+ * Finally, mark the cpu class inactive if not all resulting/desired
+ * facilities are offered by the host.
+ * (RFL & MFL) != RFL
+ */
+ for (i = 0; i < S390_ARCH_FAC_LIST_SIZE_UINT64 &&
+ cc->is_active[parm->accel]; i++) {
+ if ((cc->fac_list[i] & parm->prop->fac_list[i]) != cc->fac_list[i]) {
+ 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->mach->order > parm->host_cc->mach->order) {
+ 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 - initialize the cpu classes for a given accelerator
+ * type, mark the default cpu model and define aliases
+ * @accel: the accelerator id
+ * @prop: the addresd to a machine property structure initialized with
+ * machine related properties of the hosting machine
+ *
+ * Returns: 0 in case of success
+ * -EINVAL in case no valid default model was identified
+ */
+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);
+
+ /* 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 - indicates if the all cpu classes and
+ * their properties have been initialized
+ *
+ * Returns: true in case they are initialized
+ * false in case they are not iniialized
+ */
+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();
+}
+
+static inline uint64_t big_endian_bit(unsigned long nr)
+{
+ return 1ul << (BITS_PER_LONG - (nr % BITS_PER_LONG));
+};
+
+static inline int test_facility(unsigned long nr, uint64_t *fac_list)
+{
+ uint64_t *ptr;
+
+ if (nr >= S390_ARCH_FAC_LIST_SIZE_BYTE * BITS_PER_BYTE) {
+ return 0;
+ }
+ ptr = fac_list + (nr / BITS_PER_LONG);
+
+ return !!(*ptr & big_endian_bit(nr));
+}
+
+/**
+ * s390_test_facility - test if given facility bit is set facility list
+ * of given cpu class
+ * @class: address of cpu class to test
+ * @nr: bit number to test
+ *
+ * Returns: true in case it is set
+ * false in case it is not set
+ */
+bool s390_test_facility(S390CPUClass *cc, unsigned long nr)
+{
+ if (cc) {
+ return test_facility(nr, cc->fac_list) ? true : false;
+ }
+ return false;
+}
+
+/**
+ * s390_probe_mode - indicates that the current machine was initialized
+ * in probe mode
+ *
+ * Returns: true if in probe mode
+ * false if in standard mode
+ */
+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 76b3456..be94667 100644
--- a/target-s390x/cpu-models.h
+++ b/target-s390x/cpu-models.h
@@ -28,12 +28,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);
@@ -84,6 +104,13 @@ 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_test_facility(S390CPUClass *cc, unsigned long nr);
+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 f30856e..d27832b 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 c518489..ce2392a 100644
--- a/target-s390x/kvm.c
+++ b/target-s390x/kvm.c
@@ -232,7 +232,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