1
linux/arch/x86/kernel/cpu/topology_ext.c
Thomas Gleixner 63edbaa48a x86/cpu/topology: Add support for the AMD 0x80000026 leaf
On AMD processors that support extended CPUID leaf 0x80000026, use the
extended leaf to parse the topology information. In case of a failure,
fall back to parsing the information from CPUID leaf 0xb.

CPUID leaf 0x80000026 exposes the "CCX" and "CCD (Die)" information on
AMD processors which have been mapped to TOPO_TILE_DOMAIN and
TOPO_DIE_DOMAIN respectively.

Since this information was previously not available via CPUID leaf 0xb
or 0x8000001e, the "die_id", "logical_die_id", "max_die_per_pkg",
"die_cpus", and "die_cpus_list" will differ with this addition on
AMD processors that support extended CPUID leaf 0x80000026 and contain
more than one "CCD (Die)" on the package.

For example, following are the changes in the values reported by
"/sys/kernel/debug/x86/topo/cpus/16" after applying this patch on a 4th
Generation AMD EPYC System (1 x 128C/256T):

  (CPU16 is the first CPU of the second CCD on the package)

		   tip:x86/apic      tip:x86/apic
				     + this patch

  online:              1                  1
  initial_apicid:      80                 80
  apicid:              80                 80
  pkg_id:              0                  0
  die_id:              0                  4       *
  cu_id:               255                255
  core_id:             64                 64
  logical_pkg_id:      0                  0
  logical_die_id:      0                  4       *
  llc_id:              8                  8
  l2c_id:              65535              65535
  amd_node_id:         0                  0
  amd_nodes_per_pkg:   1                  1
  num_threads:         256                256
  num_cores:           128                128
  max_dies_per_pkg:    1                  8       *
  max_threads_per_core:2                  2

[ prateek: commit log, updated comment in topoext_amd.c, changed has_0xb
  to has_topoext, rebased the changes on tip:x86/apic, tested the
  changes on 4th Gen AMD EPYC system ]

[ mingo: tidy up the changelog a bit more ]

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: K Prateek Nayak <kprateek.nayak@amd.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Link: https://lore.kernel.org/r/20240314050432.1710-1-kprateek.nayak@amd.com
2024-03-22 11:22:14 +01:00

146 lines
4.2 KiB
C

// SPDX-License-Identifier: GPL-2.0
#include <linux/cpu.h>
#include <asm/apic.h>
#include <asm/memtype.h>
#include <asm/processor.h>
#include "cpu.h"
enum topo_types {
INVALID_TYPE = 0,
SMT_TYPE = 1,
CORE_TYPE = 2,
MAX_TYPE_0B = 3,
MODULE_TYPE = 3,
AMD_CCD_TYPE = 3,
TILE_TYPE = 4,
AMD_SOCKET_TYPE = 4,
MAX_TYPE_80000026 = 5,
DIE_TYPE = 5,
DIEGRP_TYPE = 6,
MAX_TYPE_1F = 7,
};
/*
* Use a lookup table for the case that there are future types > 6 which
* describe an intermediate domain level which does not exist today.
*/
static const unsigned int topo_domain_map_0b_1f[MAX_TYPE_1F] = {
[SMT_TYPE] = TOPO_SMT_DOMAIN,
[CORE_TYPE] = TOPO_CORE_DOMAIN,
[MODULE_TYPE] = TOPO_MODULE_DOMAIN,
[TILE_TYPE] = TOPO_TILE_DOMAIN,
[DIE_TYPE] = TOPO_DIE_DOMAIN,
[DIEGRP_TYPE] = TOPO_DIEGRP_DOMAIN,
};
static const unsigned int topo_domain_map_80000026[MAX_TYPE_80000026] = {
[SMT_TYPE] = TOPO_SMT_DOMAIN,
[CORE_TYPE] = TOPO_CORE_DOMAIN,
[AMD_CCD_TYPE] = TOPO_TILE_DOMAIN,
[AMD_SOCKET_TYPE] = TOPO_DIE_DOMAIN,
};
static inline bool topo_subleaf(struct topo_scan *tscan, u32 leaf, u32 subleaf,
unsigned int *last_dom)
{
unsigned int dom, maxtype;
const unsigned int *map;
struct {
// eax
u32 x2apic_shift : 5, // Number of bits to shift APIC ID right
// for the topology ID at the next level
: 27; // Reserved
// ebx
u32 num_processors : 16, // Number of processors at current level
: 16; // Reserved
// ecx
u32 level : 8, // Current topology level. Same as sub leaf number
type : 8, // Level type. If 0, invalid
: 16; // Reserved
// edx
u32 x2apic_id : 32; // X2APIC ID of the current logical processor
} sl;
switch (leaf) {
case 0x0b: maxtype = MAX_TYPE_0B; map = topo_domain_map_0b_1f; break;
case 0x1f: maxtype = MAX_TYPE_1F; map = topo_domain_map_0b_1f; break;
case 0x80000026: maxtype = MAX_TYPE_80000026; map = topo_domain_map_80000026; break;
default: return false;
}
cpuid_subleaf(leaf, subleaf, &sl);
if (!sl.num_processors || sl.type == INVALID_TYPE)
return false;
if (sl.type >= maxtype) {
pr_err_once("Topology: leaf 0x%x:%d Unknown domain type %u\n",
leaf, subleaf, sl.type);
/*
* It really would have been too obvious to make the domain
* type space sparse and leave a few reserved types between
* the points which might change instead of following the
* usual "this can be fixed in software" principle.
*/
dom = *last_dom + 1;
} else {
dom = map[sl.type];
*last_dom = dom;
}
if (!dom) {
tscan->c->topo.initial_apicid = sl.x2apic_id;
} else if (tscan->c->topo.initial_apicid != sl.x2apic_id) {
pr_warn_once(FW_BUG "CPUID leaf 0x%x subleaf %d APIC ID mismatch %x != %x\n",
leaf, subleaf, tscan->c->topo.initial_apicid, sl.x2apic_id);
}
topology_set_dom(tscan, dom, sl.x2apic_shift, sl.num_processors);
return true;
}
static bool parse_topology_leaf(struct topo_scan *tscan, u32 leaf)
{
unsigned int last_dom;
u32 subleaf;
/* Read all available subleafs and populate the levels */
for (subleaf = 0, last_dom = 0; topo_subleaf(tscan, leaf, subleaf, &last_dom); subleaf++);
/* If subleaf 0 failed to parse, give up */
if (!subleaf)
return false;
/*
* There are machines in the wild which have shift 0 in the subleaf
* 0, but advertise 2 logical processors at that level. They are
* truly SMT.
*/
if (!tscan->dom_shifts[TOPO_SMT_DOMAIN] && tscan->dom_ncpus[TOPO_SMT_DOMAIN] > 1) {
unsigned int sft = get_count_order(tscan->dom_ncpus[TOPO_SMT_DOMAIN]);
pr_warn_once(FW_BUG "CPUID leaf 0x%x subleaf 0 has shift level 0 but %u CPUs. Fixing it up.\n",
leaf, tscan->dom_ncpus[TOPO_SMT_DOMAIN]);
topology_update_dom(tscan, TOPO_SMT_DOMAIN, sft, tscan->dom_ncpus[TOPO_SMT_DOMAIN]);
}
set_cpu_cap(tscan->c, X86_FEATURE_XTOPOLOGY);
return true;
}
bool cpu_parse_topology_ext(struct topo_scan *tscan)
{
/* Intel: Try leaf 0x1F first. */
if (tscan->c->cpuid_level >= 0x1f && parse_topology_leaf(tscan, 0x1f))
return true;
/* AMD: Try leaf 0x80000026 first. */
if (tscan->c->extended_cpuid_level >= 0x80000026 && parse_topology_leaf(tscan, 0x80000026))
return true;
/* Intel/AMD: Fall back to leaf 0xB if available */
return tscan->c->cpuid_level >= 0x0b && parse_topology_leaf(tscan, 0x0b);
}