@@ -234,6 +234,265 @@ static int set_s390_cpu_alias(const char *name, const char *model)
return 0;
}
+/* 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->order < cc_b->order) {
+ return -1;
+ }
+ if (cc_a->order > cc_b->order) {
+ return 1;
+ }
+ 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);
+ struct {
+ unsigned short type;
+ unsigned short class;
+ bool valid;
+ } *arg = user_data;
+
+ if (arg->valid || !cc->type || arg->type != cc->type) {
+ return;
+ }
+
+ arg->class = cc->class;
+ arg->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);
+ struct {
+ unsigned short type;
+ unsigned short class;
+ } *arg = user_data;
+
+ if (arg->type != type) {
+ arg->class = 0;
+ }
+ if (!arg->class) {
+ struct {
+ unsigned short type;
+ unsigned short class;
+ bool valid;
+ } arg_class= {
+ .type = type,
+ .class = 0,
+ .valid = false,
+ };
+ g_slist_foreach(list, (GFunc) s390_machine_class_test_cpu_class, &arg_class);
+ g_slist_free(list);
+ if (arg_class.valid) {
+ arg->class = arg_class.class;
+ }
+ }
+ arg->type = type;
+
+ return arg->class;
+}
+
+/* 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);
+ struct {
+ struct S390HostProps *prop;
+ S390CPUClass *host_cc;
+ } *arg = user_data;
+
+ if (!cc->is_active) {
+ return;
+ }
+
+ struct {
+ unsigned short type;
+ unsigned short class;
+ } arg_class = {
+ .type = 0,
+ .class = 0,
+ };
+ if (cc->class != machine_class(arg->prop->vcpu.type, &arg_class)) {
+ return;
+ }
+ if (!arg->host_cc) {
+ cc->is_host = true;
+ arg->host_cc = cc;
+ return;
+ }
+ if (cc->order > arg->host_cc->order) {
+ arg->host_cc->is_host = false;
+ cc->is_host = true;
+ arg->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;
+ struct S390HostProps *prop = user_data;
+ S390CPUClass *cc = S390_CPU_CLASS(oc);
+ unsigned int i;
+
+ if (!cc->type) {
+ return;
+ }
+
+ /* define model specific mandatory facility set in current host context */
+ cc->kvm_facilities = g_malloc0(MAX_S390_FACILITY_BYTE);
+ if (cc->kvm_facilities) {
+ for (i = 0; i < MAX_S390_FACILITY_ULONG; i++) {
+ cc->kvm_facilities[i] = cc->facilities[i] & prop->host.facility_mask[i];
+ }
+ }
+
+ /* mark cpu class active if all required facility bits are available */
+ for (i = 0; i < MAX_S390_FACILITY_BIT && cc->is_active; i++) {
+ if (test_facility(i, cc->kvm_facilities) && !test_facility(i, prop->vcpu.facilities)) {
+ cc->is_active = false;
+ }
+ }
+}
+
+/* a cpu class that is newer then the current host */
+static void s390_deactive_not_supported_cpu_class(gpointer data, gpointer user_data)
+{
+ S390CPUClass *cc = S390_CPU_CLASS((ObjectClass *) data);
+ struct {
+ S390CPUClass *host_cc;
+ } *arg = user_data;
+
+ if (!cc->is_active) {
+ return;
+ }
+ if (cc->order > arg->host_cc->order) {
+ cc->is_active = 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 sytem has latest ga of a type type */
+static void s390_set_ga_alias_from_cpu_class(gpointer data, gpointer user_data)
+{
+ S390CPUClass *cc = S390_CPU_CLASS((ObjectClass *) data);
+ struct {
+ unsigned short type;
+ unsigned short ga;
+ } *arg = user_data;
+
+ if (!cc->is_active) {
+ return;
+ }
+ if (!arg->type) {
+ arg->type = cc->type;
+ }
+ if (cc->type == arg->type) {
+ arg->ga = cc->ga;
+ return;
+ }
+ set_s390_cpu_alias_by_type_ga(arg->type, arg->ga);
+ arg->type = cc->type;
+ arg->ga = cc->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);
+ char model[16];
+
+ if (!cc->is_active || !cc->is_host) {
+ return;
+ }
+ snprintf(model, sizeof(model), "%04x-ga%u", cc->type, cc->ga);
+ set_s390_cpu_alias("host", model);
+}
+
+/*
+ * apply host properties retrieved from KVM to cpu model classes,
+ * then find cpu model host and define further aliases
+ */
+int s390_setup_cpu_classes(struct S390HostProps *prop)
+{
+ GSList *list;
+
+ list = object_class_get_list(TYPE_S390_CPU, false);
+ list = g_slist_sort(list, s390_cpu_class_asc_order_compare);
+
+ /* update cpu classes with KVM properties */
+ g_slist_foreach(list, (GFunc) s390_update_cpu_class, (gpointer) prop);
+
+ /* define cpu model "host" */
+ struct {
+ struct S390HostProps *prop;
+ S390CPUClass *host_cc;
+ } arg_host = {
+ .prop = prop,
+ .host_cc = NULL,
+ };
+ g_slist_foreach(list, (GFunc) s390_mark_host_cpu_class, (gpointer) &arg_host);
+
+ /* invalidate cpu classes not supported by this host */
+ struct {
+ S390CPUClass *host_cc;
+ } arg_deactivate = {
+ .host_cc = arg_host.host_cc,
+ };
+ g_slist_foreach(list, (GFunc) s390_deactive_not_supported_cpu_class, &arg_deactivate);
+
+ /* set machine type aliases to latest ga of model (e.g. 2064 -> 2064-ga3) */
+ struct {
+ unsigned short type;
+ unsigned short ga;
+ } arg_alias = {
+ .type = 0,
+ .ga = 0,
+ };
+ g_slist_foreach(list, (GFunc) s390_set_ga_alias_from_cpu_class, &arg_alias);
+ set_s390_cpu_alias_by_type_ga(arg_alias.type, arg_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-bc", "2096");
+ set_s390_cpu_alias("z10", "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" */
+ g_slist_foreach(list, (GFunc) s390_set_host_alias_from_cpu_class, NULL);
+
+ g_slist_free(list);
+
+ return 0;
+}
+
/* return host specific properties */
int s390_fetch_kvm_host_props(struct S390HostProps *prop)
{
@@ -50,6 +50,8 @@ struct S390HostProps {
int s390_fetch_kvm_host_props(struct S390HostProps *prop);
int s390_request_kvm_cpu_config(S390CPUClass *cc);
+gint s390_cpu_class_asc_order_compare(gconstpointer a, gconstpointer b);
+int s390_setup_cpu_classes(struct S390HostProps *prop);
ObjectClass *s390_cpu_class_by_name(const char *name);
/*
This patch provides routines to dynamically update the previously defined s390 cpu classes in the current host context. The main function issuing 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 KVM host properties - mark cpu class for cpu model "host" - invalidate cpu classes not supported by this host - set machine type aliases to latest ga of model (e.g. 2064 -> 2064-ga3) - set aliases for common model names to machine types - set alias for cpu model "host" Signed-off-by: Michael Mueller <mimu@linux.vnet.ibm.com> --- target-s390x/cpu-models.c | 259 ++++++++++++++++++++++++++++++++++++++++++++++ target-s390x/cpu-models.h | 2 + 2 files changed, 261 insertions(+)