Commit ebdb2036 authored by Thomas Gleixner's avatar Thomas Gleixner
Browse files

x86/cpu: Provide cpu_init/parse_topology()



Topology evaluation is a complete disaster and impenetrable mess. It's
scattered all over the place with some vendor implementations doing early
evaluation and some not. The most horrific part is the permanent
overwriting of smt_max_siblings and __max_die_per_package, instead of
establishing them once on the boot CPU and validating the result on the
APs.

The goals are:

  - One topology evaluation entry point

  - Proper sharing of pointlessly duplicated code

  - Proper structuring of the evaluation logic and preferences.

  - Evaluating important system wide information only once on the boot CPU

  - Making the 0xb/0x1f leaf parsing less convoluted and actually fixing
    the short comings of leaf 0x1f evaluation.

Start to consolidate the topology evaluation code by providing the entry
points for the early boot CPU evaluation and for the final parsing on the
boot CPU and the APs.

Move the trivial pieces into that new code:

   - The initialization of cpuinfo_x86::topo

   - The evaluation of CPUID leaf 1, which presets topo::initial_apicid

   - topo_apicid is set to topo::initial_apicid when invoked from early
     boot. When invoked for the final evaluation on the boot CPU it reads
     the actual APIC ID, which makes apic_get_initial_apicid() obsolete
     once everything is converted over.

Provide a temporary helper function topo_converted() which shields off the
not yet converted CPU vendors from invoking code which would break them.
This shielding covers all vendor CPUs which support SMP, but not the
historical pure UP ones as they only need the topology info init and
eventually the initial APIC initialization.

Provide two new members in cpuinfo_x86::topo to store the maximum number of
SMT siblings and the number of dies per package and add them to the debugfs
readout. These two members will be used to populate this information on the
boot CPU and to validate the APs against it.

Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
Tested-by: default avatarJuergen Gross <jgross@suse.com>
Tested-by: default avatarSohil Mehta <sohil.mehta@intel.com>
Tested-by: default avatarMichael Kelley <mhklinux@outlook.com>
Tested-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Tested-by: default avatarZhang Rui <rui.zhang@intel.com>
Tested-by: default avatarWang Wendy <wendy.wang@intel.com>
Tested-by: default avatarK Prateek Nayak <kprateek.nayak@amd.com>
Acked-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lore.kernel.org/r/20240212153624.581436579@linutronix.de


parent 43d86e3c
Loading
Loading
Loading
Loading
+19 −0
Original line number Diff line number Diff line
@@ -102,6 +102,25 @@ static inline void setup_node_to_cpumask_map(void) { }

#include <asm-generic/topology.h>

/* Topology information */
enum x86_topology_domains {
	TOPO_SMT_DOMAIN,
	TOPO_CORE_DOMAIN,
	TOPO_MODULE_DOMAIN,
	TOPO_TILE_DOMAIN,
	TOPO_DIE_DOMAIN,
	TOPO_DIEGRP_DOMAIN,
	TOPO_PKG_DOMAIN,
	TOPO_MAX_DOMAIN,
};

struct x86_topology_system {
	unsigned int	dom_shifts[TOPO_MAX_DOMAIN];
	unsigned int	dom_size[TOPO_MAX_DOMAIN];
};

extern struct x86_topology_system x86_topo_system;

extern const struct cpumask *cpu_coregroup_mask(int cpu);
extern const struct cpumask *cpu_clustergroup_mask(int cpu);

+2 −1
Original line number Diff line number Diff line
@@ -17,7 +17,8 @@ KMSAN_SANITIZE_common.o := n
# As above, instrumenting secondary CPU boot code causes boot hangs.
KCSAN_SANITIZE_common.o := n

obj-y			:= cacheinfo.o scattered.o topology.o
obj-y			:= cacheinfo.o scattered.o
obj-y			+= topology_common.o topology.o
obj-y			+= common.o
obj-y			+= rdrand.o
obj-y			+= match.o
+7 −17
Original line number Diff line number Diff line
@@ -1591,6 +1591,8 @@ static void __init early_identify_cpu(struct cpuinfo_x86 *c)
		setup_force_cpu_cap(X86_FEATURE_CPUID);
		cpu_parse_early_param();

		cpu_init_topology(c);

		if (this_cpu->c_early_init)
			this_cpu->c_early_init(c);

@@ -1601,6 +1603,7 @@ static void __init early_identify_cpu(struct cpuinfo_x86 *c)
			this_cpu->c_bsp_init(c);
	} else {
		setup_clear_cpu_cap(X86_FEATURE_CPUID);
		cpu_init_topology(c);
	}

	get_cpu_address_sizes(c);
@@ -1748,18 +1751,6 @@ static void generic_identify(struct cpuinfo_x86 *c)

	get_cpu_address_sizes(c);

	if (c->cpuid_level >= 0x00000001) {
		c->topo.initial_apicid = (cpuid_ebx(1) >> 24) & 0xFF;
#ifdef CONFIG_X86_32
# ifdef CONFIG_SMP
		c->topo.apicid = apic->phys_pkg_id(c->topo.initial_apicid, 0);
# else
		c->topo.apicid = c->topo.initial_apicid;
# endif
#endif
		c->topo.pkg_id = c->topo.initial_apicid;
	}

	get_model_name(c); /* Default name */

	/*
@@ -1818,9 +1809,6 @@ static void identify_cpu(struct cpuinfo_x86 *c)
	c->x86_model_id[0] = '\0';  /* Unset */
	c->x86_max_cores = 1;
	c->x86_coreid_bits = 0;
	c->topo.cu_id = 0xff;
	c->topo.llc_id = BAD_APICID;
	c->topo.l2c_id = BAD_APICID;
#ifdef CONFIG_X86_64
	c->x86_clflush_size = 64;
	c->x86_phys_bits = 36;
@@ -1839,6 +1827,8 @@ static void identify_cpu(struct cpuinfo_x86 *c)

	generic_identify(c);

	cpu_parse_topology(c);

	if (this_cpu->c_identify)
		this_cpu->c_identify(c);

@@ -1846,10 +1836,10 @@ static void identify_cpu(struct cpuinfo_x86 *c)
	apply_forced_caps(c);

#ifdef CONFIG_X86_64
	if (!topo_is_converted(c))
		c->topo.apicid = apic->phys_pkg_id(c->topo.initial_apicid, 0);
#endif


	/*
	 * Set default APIC and TSC_DEADLINE MSR fencing flag. AMD and
	 * Hygon will clear it in ->c_init() below.
+6 −0
Original line number Diff line number Diff line
@@ -2,6 +2,11 @@
#ifndef ARCH_X86_CPU_H
#define ARCH_X86_CPU_H

#include <asm/cpu.h>
#include <asm/topology.h>

#include "topology.h"

/* attempt to consolidate cpu attributes */
struct cpu_dev {
	const char	*c_vendor;
@@ -96,4 +101,5 @@ static inline bool spectre_v2_in_eibrs_mode(enum spectre_v2_mitigation mode)
	       mode == SPECTRE_V2_EIBRS_RETPOLINE ||
	       mode == SPECTRE_V2_EIBRS_LFENCE;
}

#endif /* ARCH_X86_CPU_H */
+38 −0
Original line number Diff line number Diff line
@@ -5,6 +5,8 @@
#include <asm/apic.h>
#include <asm/processor.h>

#include "cpu.h"

static int cpu_debug_show(struct seq_file *m, void *p)
{
	unsigned long cpu = (unsigned long)m->private;
@@ -42,12 +44,48 @@ static const struct file_operations dfs_cpu_ops = {
	.release	= single_release,
};

static int dom_debug_show(struct seq_file *m, void *p)
{
	static const char *domain_names[TOPO_MAX_DOMAIN] = {
		[TOPO_SMT_DOMAIN]	= "Thread",
		[TOPO_CORE_DOMAIN]	= "Core",
		[TOPO_MODULE_DOMAIN]	= "Module",
		[TOPO_TILE_DOMAIN]	= "Tile",
		[TOPO_DIE_DOMAIN]	= "Die",
		[TOPO_DIEGRP_DOMAIN]	= "DieGrp",
		[TOPO_PKG_DOMAIN]	= "Package",
	};
	unsigned int dom, nthreads = 1;

	for (dom = 0; dom < TOPO_MAX_DOMAIN; dom++) {
		nthreads *= x86_topo_system.dom_size[dom];
		seq_printf(m, "domain: %-10s shift: %u dom_size: %5u max_threads: %5u\n",
			   domain_names[dom], x86_topo_system.dom_shifts[dom],
			   x86_topo_system.dom_size[dom], nthreads);
	}
	return 0;
}

static int dom_debug_open(struct inode *inode, struct file *file)
{
	return single_open(file, dom_debug_show, inode->i_private);
}

static const struct file_operations dfs_dom_ops = {
	.open		= dom_debug_open,
	.read		= seq_read,
	.llseek		= seq_lseek,
	.release	= single_release,
};

static __init int cpu_init_debugfs(void)
{
	struct dentry *dir, *base = debugfs_create_dir("topo", arch_debugfs_dir);
	unsigned long id;
	char name[24];

	debugfs_create_file("domains", 0444, base, NULL, &dfs_dom_ops);

	dir = debugfs_create_dir("cpus", base);
	for_each_possible_cpu(id) {
		sprintf(name, "%lu", id);
Loading