These functions add support for building new epyc mode topology given smp details like numa nodes, cores, threads and sockets. Subsequent patches will use these functions to build the topology.
The topology details are available in Processor Programming Reference (PPR) for AMD Family 17h Model 01h, Revision B1 Processors. It is available at https://www.amd.com/en/support/tech-docs Signed-off-by: Babu Moger <babu.mo...@amd.com> --- include/hw/i386/topology.h | 174 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 174 insertions(+) diff --git a/include/hw/i386/topology.h b/include/hw/i386/topology.h index 5a61d53f05..6fd4184f07 100644 --- a/include/hw/i386/topology.h +++ b/include/hw/i386/topology.h @@ -62,6 +62,22 @@ typedef struct X86CPUTopoInfo { unsigned nr_threads; } X86CPUTopoInfo; +/* + * Definitions used for building CPUID Leaf 0x8000001D and 0x8000001E + * Please refer to the AMD64 Architecture Programmer’s Manual Volume 3. + * Define the constants to build the cpu topology. Right now, TOPOEXT + * feature is enabled only on EPYC. So, these constants are based on + * EPYC supported configurations. We may need to handle the cases if + * these values change in future. + */ + +/* Maximum core complexes in a node */ +#define MAX_CCX 2 +/* Maximum cores in a core complex */ +#define MAX_CORES_IN_CCX 4 +/* Maximum cores in a node */ +#define MAX_CORES_IN_NODE 8 + /* Return the bit width needed for 'count' IDs */ static unsigned apicid_bitwidth_for_count(unsigned count) @@ -116,6 +132,164 @@ static inline unsigned apicid_pkg_offset(unsigned nr_dies, apicid_die_width(nr_dies); } +/* Bit offset of the CCX_ID field */ +static inline unsigned apicid_ccx_offset(unsigned nr_cores, + unsigned nr_threads) +{ + return apicid_core_offset(nr_threads) + + apicid_core_width(nr_cores); +} + +/* Bit width of the Die_ID field */ +static inline unsigned apicid_ccx_width(unsigned nr_ccxs) +{ + return apicid_bitwidth_for_count(nr_ccxs); +} + +/* Bit offset of the node_id field */ +static inline unsigned apicid_node_offset(unsigned nr_ccxs, + unsigned nr_cores, + unsigned nr_threads) +{ + return apicid_ccx_offset(nr_cores, nr_threads) + + apicid_ccx_width(nr_ccxs); +} + +/* Bit width of the node_id field */ +static inline unsigned apicid_node_width(unsigned nr_nodes) +{ + return apicid_bitwidth_for_count(nr_nodes); +} + +/* Bit offset of the node_id field */ +static inline unsigned apicid_pkg_offset_epyc(unsigned nr_nodes, + unsigned nr_ccxs, + unsigned nr_cores, + unsigned nr_threads) +{ + return apicid_node_offset(nr_ccxs, nr_cores, nr_threads) + + apicid_node_width(nr_nodes); +} + +/* + * Figure out the number of nodes required to build this config. + * Max cores in a nodes is 8 + */ +static inline int nodes_in_pkg(X86CPUTopoInfo *topo_info) +{ + /* + * Create a config with user given (nr_nodes > 1) numa node config, + * else go with a standard configuration + */ + if (topo_info->numa_nodes > 1) { + return DIV_ROUND_UP(topo_info->numa_nodes, topo_info->nr_sockets); + } else { + return DIV_ROUND_UP(topo_info->nr_cores, MAX_CORES_IN_NODE); + } +} + +/* + * Decide the number of cores in a core complex with the given nr_cores using + * following set constants MAX_CCX, MAX_CORES_IN_CCX, MAX_CORES_IN_DIE and + * MAX_NODES_PER_SOCKET. Maintain symmetry as much as possible + * L3 cache is shared across all cores in a core complex. So, this will also + * tell us how many cores are sharing the L3 cache. + */ +static inline int cores_in_ccx(X86CPUTopoInfo *topo_info) +{ + int nodes; + + /* Get the number of nodes required to build this config */ + nodes = nodes_in_pkg(topo_info); + + /* + * Divide the cores accros all the core complexes + * Return rounded up value + */ + return DIV_ROUND_UP(topo_info->nr_cores, nodes * MAX_CCX); +} + +/* + * Make APIC ID for the CPU based on Pkg_ID, Core_ID, SMT_ID + * + * The caller must make sure core_id < nr_cores and smt_id < nr_threads. + */ +static inline apic_id_t x86_apicid_from_topo_ids_epyc(X86CPUTopoInfo *topo_info, + const X86CPUTopoIDs *topo_ids) +{ + unsigned nr_ccxs = MAX_CCX; + unsigned nr_nodes = nodes_in_pkg(topo_info); + unsigned nr_cores = MAX_CORES_IN_CCX; + unsigned nr_threads = topo_info->nr_threads; + + return (topo_ids->pkg_id << apicid_pkg_offset_epyc(nr_nodes, nr_ccxs, + nr_cores, nr_threads)) | + (topo_ids->node_id << apicid_node_offset(nr_ccxs, nr_cores, + nr_threads)) | + (topo_ids->ccx_id << apicid_ccx_offset(nr_cores, nr_threads)) | + (topo_ids->core_id << apicid_core_offset(nr_threads)) | + topo_ids->smt_id; +} + +static inline void x86_topo_ids_from_idx_epyc(X86CPUTopoInfo *topo_info, + unsigned cpu_index, + X86CPUTopoIDs *topo_ids) +{ + unsigned nr_cores = topo_info->nr_cores; + unsigned nr_threads = topo_info->nr_threads; + unsigned core_index = cpu_index / nr_threads % nr_cores; + unsigned ccx_cores = cores_in_ccx(topo_info); + + topo_ids->smt_id = cpu_index % nr_threads; + topo_ids->core_id = core_index % ccx_cores; /* core id inside the ccx */ + topo_ids->ccx_id = (core_index % (ccx_cores * MAX_CCX)) / ccx_cores; + topo_ids->node_id = core_index / (ccx_cores * MAX_CCX); + topo_ids->pkg_id = cpu_index / (nr_cores * nr_threads); +} + +/* + * Calculate thread/core/package IDs for a specific topology, + * based on APIC ID + */ +static inline void x86_topo_ids_from_apicid_epyc(apic_id_t apicid, + X86CPUTopoInfo *topo_info, + X86CPUTopoIDs *topo_ids) +{ + unsigned nr_nodes = nodes_in_pkg(topo_info); + unsigned nr_cores = MAX_CORES_IN_CCX; + unsigned nr_threads = topo_info->nr_threads; + unsigned nr_ccxs = MAX_CCX; + + topo_ids->smt_id = apicid & + ~(0xFFFFFFFFUL << apicid_smt_width(nr_threads)); + + topo_ids->core_id = (apicid >> apicid_core_offset(nr_threads)) & + ~(0xFFFFFFFFUL << apicid_core_width(nr_cores)); + + topo_ids->ccx_id = (apicid >> apicid_ccx_offset(nr_cores, nr_threads)) & + ~(0xFFFFFFFFUL << apicid_ccx_width(nr_ccxs)); + + topo_ids->node_id = (apicid >> apicid_node_offset(nr_ccxs, nr_cores, + nr_threads)) & + ~(0xFFFFFFFFUL << apicid_node_width(nr_nodes)); + + topo_ids->pkg_id = apicid >> apicid_pkg_offset_epyc(nr_nodes, nr_ccxs, + nr_cores, nr_threads); +} + +/* + * Make APIC ID for the CPU 'cpu_index' + * + * 'cpu_index' is a sequential, contiguous ID for the CPU. + */ +static inline apic_id_t x86_apicid_from_cpu_idx_epyc(X86CPUTopoInfo *topo_info, + unsigned cpu_index) +{ + X86CPUTopoIDs topo_ids; + x86_topo_ids_from_idx_epyc(topo_info, cpu_index, &topo_ids); + return x86_apicid_from_topo_ids_epyc(topo_info, &topo_ids); +} + /* Make APIC ID for the CPU based on Pkg_ID, Core_ID, SMT_ID * * The caller must make sure core_id < nr_cores and smt_id < nr_threads.