From: Zhao Liu <zhao1....@intel.com> The "-hybrid" format is like: -hybrid socket,sockets=n -hybrid die,dies=n -hybrid cluster,clusters=n -hybrid core,cores=n,type=core_type[,threads=threads] [,clusterid=cluster]
For example: -hybrid socket,sockets=1 -hybrid die,dies=1 -hybrid cluster,clusters=4 -hybrid core,cores=1,coretype="core",threads=2,clusterid=0-1 -hybrid core,cores=1,coretype="core",threads=2,clusterid=0-1,clusterid=3 -hybrid core,cores=4,coretype="atom",threads=1,clusterid=3 In order to support "-hybrid core" can be inserted into the specified cluster, introduce HybridCorePack to pack HybridCore and help to parse "-hybrid core" command. HybridCorePack will be used to build core level topology later. Since hybrid cpu topology allocates memory by g_malloc0(), free the memory when machine finalizes. Signed-off-by: Zhao Liu <zhao1....@intel.com> --- hw/core/machine-topo.c | 259 ++++++++++++++++++++++++++++++++++ hw/core/machine.c | 4 + include/hw/boards.h | 4 + include/hw/cpu/cpu-topology.h | 23 +++ 4 files changed, 290 insertions(+) diff --git a/hw/core/machine-topo.c b/hw/core/machine-topo.c index 8540b473b8db..6e4a9ec1495d 100644 --- a/hw/core/machine-topo.c +++ b/hw/core/machine-topo.c @@ -366,3 +366,262 @@ int machine_parse_hybrid_core_type(MachineState *ms, const char *coretype) return -1; } + +static void parse_hybrid_socket(MachineState *ms, + const HybridSocketOptions *config, + Error **errp) +{ + if (ms->topo.hybrid.sockets != 1) { + error_setg(errp, "socket cannot be set repeatedly"); + return; + } + + /* + * Since sockets = 0 is deprecated CPU topology for -smp, + * now -hybrid returns error directly. + */ + if (!config->sockets) { + error_setg(errp, "Invalid sockets. sockets mustn't be zero"); + return; + } + + ms->topo.hybrid.sockets = config->sockets; + + /* + * Even though we require that the "socket" must be set before the "core", + * in order to simplify the code logic, we do not check the order of the + * "socket" subcommand. Instead, multiply the "sockets" for the cpus and + * max_cpus here as a compensation measure. + */ + ms->topo.cpus *= ms->topo.hybrid.sockets; + ms->topo.max_cpus *= ms->topo.hybrid.sockets; +} + +static void parse_hybrid_die(MachineState *ms, + const HybridDieOptions *config, Error **errp) +{ + MachineClass *mc = MACHINE_GET_CLASS(ms); + + if (ms->topo.hybrid.dies != 1) { + error_setg(errp, "die cannot be set repeatedly"); + return; + } + + /* + * Since dies = 0 is deprecated CPU topology for -smp, + * now -hybrid returns error directly. + */ + if (!config->dies) { + error_setg(errp, "Invalid dies. dies mustn't be zero"); + return; + } + if (!mc->topo_props.dies_supported && config->dies > 1) { + error_setg(errp, "dies not supported by this machine's CPU topology"); + return; + } + + ms->topo.hybrid.dies = config->dies; + + /* + * Even though we require that the "die" must be set before the "core", + * in order to simplify the code logic, we do not check the order of the + * "die" subcommand. Instead, multiply the "dies" for the cpus and + * max_cpus here as a compensation measure. + */ + ms->topo.cpus *= ms->topo.hybrid.dies; + ms->topo.max_cpus *= ms->topo.hybrid.dies; +} + +static void parse_hybrid_cluster(MachineState *ms, + const HybridClusterOptions *config, + Error **errp) +{ + MachineClass *mc = MACHINE_GET_CLASS(ms); + + if (ms->topo.hybrid.cluster_list != NULL) { + error_setg(errp, "The cluster configuration " + "clusters=%" PRIu32 " should be provided before" + "core configuration", config->clusters); + return; + } + + /* + * Since clusters = 0 is deprecated CPU topology for -smp, + * now -hybrid returns error directly. + */ + if (!config->clusters) { + error_setg(errp, "Invalid clusters. clusters mustn't be zero"); + return; + } + + if (!mc->topo_props.clusters_supported && config->clusters > 1) { + error_setg(errp, "clusters not supported by this " + "machine's CPU topology"); + return; + } + + mc->topo_props.has_clusters = true; + + ms->topo.hybrid.clusters = config->clusters; + ms->topo.hybrid.cluster_list = g_malloc0(sizeof(HybridCluster) * + ms->topo.hybrid.clusters); + + for (int i = 0; i < ms->topo.hybrid.clusters; i++) { + QSLIST_INIT(&ms->topo.hybrid.cluster_list[i].core_pack_list); + } +} + +static void insert_core_into_cluster(MachineState *ms, + HybridCluster *cluster, + const HybridCoreOptions *config, + Error **errp) +{ + MachineClass *mc = MACHINE_GET_CLASS(ms); + HybridCorePack *core_pack; + int ret; + + /* + * Since cores = 0 or threads = 0 is deprecated CPU topology for + * -smp, now -hybrid return error directly. + */ + if (!config->cores) { + error_setg(errp, "Invalid cores. cores=%u mustn't be zero", + config->cores); + return; + } + if (config->has_threads && !config->threads) { + error_setg(errp, "Invalid threads. threads=%u mustn't be zero", + config->threads); + return; + } + + core_pack = g_malloc0(sizeof(*core_pack)); + core_pack->core_num = config->cores; + core_pack->core.threads = config->has_threads ? config->threads : 1; + + ret = mc->core_type(ms, config->coretype); + if (!ret) { + error_setg(errp, "Invalid coretype=%s", config->coretype); + } + core_pack->core.core_type = ret; + + QSLIST_INSERT_HEAD(&cluster->core_pack_list, core_pack, node); + cluster->cores += core_pack->core_num; + + /* + * Note here we just multiply by "dies" and "sockets" other that + * "clusters". + * + * This function caculates cluster's topology one by one, and different + * cluster in the same die may have different topology. So we can't + * multiply by "clusters". But for current hybrid topology support, we + * suppose sockets and dies are same. So "sockets" and "dies" should be + * considerred. + */ + ms->topo.cpus += core_pack->core.threads * core_pack->core_num * + ms->topo.hybrid.dies * ms->topo.hybrid.sockets; + /* + * Because of the "-hybrid" format limitation, before all the cores are + * configured, the cpus and max_cpus cannot be obtained. At the same + * time, because of heterogeneous, if user wants to specify the online + * cpus, user must subdivide it into a specific core. So currently, + * temporarily limit max_cpus to be consistent with cpus. + * + * TODO: Consider adding more complete online cpus configuration support + * in the future. + */ + ms->topo.max_cpus = ms->topo.cpus; +} + +static void parse_hybrid_core(MachineState *ms, + const HybridCoreOptions *config, Error **errp) +{ + if (!ms->topo.hybrid.cluster_list) { + if (config->has_clusterid) { + error_setg(errp, "The core configuration clusterid " + "should be provided after" + "cluster configuration"); + return; + } + /* Set 1 cluster per die by default. */ + ms->topo.hybrid.clusters = 1; + ms->topo.hybrid.cluster_list = g_malloc0(sizeof(HybridCluster) * + ms->topo.hybrid.clusters); + QSLIST_INIT(&ms->topo.hybrid.cluster_list[0].core_pack_list); + } + + if (config->has_clusterid) { + for (uint32List *clusterid = config->clusterid; clusterid; + clusterid = clusterid->next) { + if (clusterid->value >= ms->topo.hybrid.clusters) { + error_setg(errp, "Invalid clusterid. " + "clusterid=%" PRIu32 " is out of range " + "(only %" PRIu32 "clusters)", + clusterid->value, ms->topo.hybrid.clusters); + return; + } + insert_core_into_cluster(ms, + &ms->topo.hybrid.cluster_list[clusterid->value], config, errp); + } + } else { + for (int i = 0; i < ms->topo.hybrid.clusters; i++) { + insert_core_into_cluster(ms, + &ms->topo.hybrid.cluster_list[i], config, errp); + } + } +} + +void set_hybrid_options(MachineState *ms, + const HybridOptions *config, + Error **errp) +{ + MachineClass *mc = MACHINE_GET_CLASS(ms); + + if (!mc->topo_props.hybrid_supported) { + error_setg(errp, "hybrid topology not supported " + "by this machine's CPU"); + return; + } + + switch (config->type) { + case HYBRID_OPTIONS_TYPE_SOCKET: + parse_hybrid_socket(ms, &config->u.socket, errp); + break; + case HYBRID_OPTIONS_TYPE_DIE: + parse_hybrid_die(ms, &config->u.die, errp); + break; + case HYBRID_OPTIONS_TYPE_CLUSTER: + parse_hybrid_cluster(ms, &config->u.cluster, errp); + break; + case HYBRID_OPTIONS_TYPE_CORE: + parse_hybrid_core(ms, &config->u.core, errp); + break; + default: + abort(); + } +} + +void machine_free_hybrid_topology(MachineState *ms) +{ + HybridCluster *cluster; + HybridCorePack *core_pack; + HybridCorePack *tmp; + + if (ms->topo.hybrid.clusters) { + for (int i = 0; i < ms->topo.hybrid.clusters; i++) { + cluster = &ms->topo.hybrid.cluster_list[i]; + + /* + * TODO: Temporarily free core_pack_list here. When the + * building of core_list array is supported, it will be + * freeed there. + */ + QSLIST_FOREACH_SAFE(core_pack, &cluster->core_pack_list, + node, tmp) { + QSLIST_REMOVE_HEAD(&cluster->core_pack_list, node); + g_free(core_pack); + } + } + g_free(ms->topo.hybrid.cluster_list); + } +} diff --git a/hw/core/machine.c b/hw/core/machine.c index acc32b3be5f6..f2c6aac4ef94 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -1122,6 +1122,10 @@ static void machine_finalize(Object *obj) g_free(ms->device_memory); g_free(ms->nvdimms_state); g_free(ms->numa_state); + + if (!machine_topo_is_smp(ms)) { + machine_free_hybrid_topology(ms); + } } bool machine_usb(MachineState *machine) diff --git a/include/hw/boards.h b/include/hw/boards.h index 48444ab7275b..09b93c17a245 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -37,6 +37,10 @@ void machine_set_cpu_numa_node(MachineState *machine, void machine_parse_smp_config(MachineState *ms, const SMPConfiguration *config, Error **errp); int machine_parse_hybrid_core_type(MachineState *ms, const char *coretype); +void set_hybrid_options(MachineState *ms, + const HybridOptions *config, + Error **errp); +void machine_free_hybrid_topology(MachineState *ms); /** * machine_class_allow_dynamic_sysbus_dev: Add type to list of valid devices diff --git a/include/hw/cpu/cpu-topology.h b/include/hw/cpu/cpu-topology.h index 829f98d3a73f..eef752d5168d 100644 --- a/include/hw/cpu/cpu-topology.h +++ b/include/hw/cpu/cpu-topology.h @@ -52,15 +52,38 @@ typedef struct HybridCore { unsigned int core_type; } HybridCore; +/** + * HybridCorePack - Wrapper of HybridCore to pack the same + * cores in one cluster. + * + * HybridCorePack is used for `-hybrid` parsing and will help to build + * core_list array. + * + * @core_num: the number of threads in one core. + * @core: the specific core information of current core pack. + * @node: the singly-linked list node of current core pack. This node + * is added to the core_pack_list of a cluster which the current + * core pack belongs to. + */ +typedef struct HybridCorePack { + unsigned int core_num; + HybridCore core; + QSLIST_ENTRY(HybridCorePack) node; +} HybridCorePack; + /** * HybridCluster - hybrid cluster topology defination: * @cores: the number of cores in current cluster. * @core_list: the array includes all the cores that belong to current * cluster. + * @core_pack_list: the list that links all the core packs that belong to + * current cluster. It is used for `-hybrid` parsing and + * will help to build core_list array. */ typedef struct HybridCluster { unsigned int cores; HybridCore *core_list; + QSLIST_HEAD(, HybridCorePack) core_pack_list; } HybridCluster; /** -- 2.34.1