820 lines · 19.9 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* * x86 CPU caches detection and configuration * * Previous changes * - Venkatesh Pallipadi:		Cache identification through CPUID(0x4) * - Ashok Raj <ashok.raj@intel.com>:	Work with CPU hotplug infrastructure * - Andi Kleen / Andreas Herrmann:	CPUID(0x4) emulation on AMD */ #include <linux/cacheinfo.h>#include <linux/cpu.h>#include <linux/cpuhotplug.h>#include <linux/stop_machine.h> #include <asm/amd/nb.h>#include <asm/cacheinfo.h>#include <asm/cpufeature.h>#include <asm/cpuid/api.h>#include <asm/mtrr.h>#include <asm/smp.h>#include <asm/tlbflush.h> #include "cpu.h" /* Shared last level cache maps */DEFINE_PER_CPU_READ_MOSTLY(cpumask_var_t, cpu_llc_shared_map); /* Shared L2 cache maps */DEFINE_PER_CPU_READ_MOSTLY(cpumask_var_t, cpu_l2c_shared_map); static cpumask_var_t cpu_cacheinfo_mask; /* Kernel controls MTRR and/or PAT MSRs. */unsigned int memory_caching_control __ro_after_init; enum _cache_type {	CTYPE_NULL	= 0,	CTYPE_DATA	= 1,	CTYPE_INST	= 2,	CTYPE_UNIFIED	= 3}; union _cpuid4_leaf_eax {	struct {		enum _cache_type	type			:5;		unsigned int		level			:3;		unsigned int		is_self_initializing	:1;		unsigned int		is_fully_associative	:1;		unsigned int		reserved		:4;		unsigned int		num_threads_sharing	:12;		unsigned int		num_cores_on_die	:6;	} split;	u32 full;}; union _cpuid4_leaf_ebx {	struct {		unsigned int		coherency_line_size	:12;		unsigned int		physical_line_partition	:10;		unsigned int		ways_of_associativity	:10;	} split;	u32 full;}; union _cpuid4_leaf_ecx {	struct {		unsigned int		number_of_sets		:32;	} split;	u32 full;}; struct _cpuid4_info {	union _cpuid4_leaf_eax eax;	union _cpuid4_leaf_ebx ebx;	union _cpuid4_leaf_ecx ecx;	unsigned int id;	unsigned long size;}; /* Map CPUID(0x4) EAX.cache_type to <linux/cacheinfo.h> types */static const enum cache_type cache_type_map[] = {	[CTYPE_NULL]	= CACHE_TYPE_NOCACHE,	[CTYPE_DATA]	= CACHE_TYPE_DATA,	[CTYPE_INST]	= CACHE_TYPE_INST,	[CTYPE_UNIFIED] = CACHE_TYPE_UNIFIED,}; /* * Fallback AMD CPUID(0x4) emulation * AMD CPUs with TOPOEXT can just use CPUID(0x8000001d) * * @AMD_L2_L3_INVALID_ASSOC: cache info for the respective L2/L3 cache should * be determined from CPUID(0x8000001d) instead of CPUID(0x80000006). */ #define AMD_CPUID4_FULLY_ASSOCIATIVE	0xffff#define AMD_L2_L3_INVALID_ASSOC		0x9 union l1_cache {	struct {		unsigned line_size	:8;		unsigned lines_per_tag	:8;		unsigned assoc		:8;		unsigned size_in_kb	:8;	};	unsigned int val;}; union l2_cache {	struct {		unsigned line_size	:8;		unsigned lines_per_tag	:4;		unsigned assoc		:4;		unsigned size_in_kb	:16;	};	unsigned int val;}; union l3_cache {	struct {		unsigned line_size	:8;		unsigned lines_per_tag	:4;		unsigned assoc		:4;		unsigned res		:2;		unsigned size_encoded	:14;	};	unsigned int val;}; /* L2/L3 associativity mapping */static const unsigned short assocs[] = {	[1]		= 1,	[2]		= 2,	[3]		= 3,	[4]		= 4,	[5]		= 6,	[6]		= 8,	[8]		= 16,	[0xa]		= 32,	[0xb]		= 48,	[0xc]		= 64,	[0xd]		= 96,	[0xe]		= 128,	[0xf]		= AMD_CPUID4_FULLY_ASSOCIATIVE}; static const unsigned char levels[] = { 1, 1, 2, 3 };static const unsigned char types[]  = { 1, 2, 3, 3 }; static void legacy_amd_cpuid4(int index, union _cpuid4_leaf_eax *eax,			      union _cpuid4_leaf_ebx *ebx, union _cpuid4_leaf_ecx *ecx){	unsigned int dummy, line_size, lines_per_tag, assoc, size_in_kb;	union l1_cache l1i, l1d, *l1;	union l2_cache l2;	union l3_cache l3; 	eax->full = 0;	ebx->full = 0;	ecx->full = 0; 	cpuid(0x80000005, &dummy, &dummy, &l1d.val, &l1i.val);	cpuid(0x80000006, &dummy, &dummy, &l2.val, &l3.val); 	l1 = &l1d;	switch (index) {	case 1:		l1 = &l1i;		fallthrough;	case 0:		if (!l1->val)			return; 		assoc		= (l1->assoc == 0xff) ? AMD_CPUID4_FULLY_ASSOCIATIVE : l1->assoc;		line_size	= l1->line_size;		lines_per_tag	= l1->lines_per_tag;		size_in_kb	= l1->size_in_kb;		break;	case 2:		if (!l2.assoc || l2.assoc == AMD_L2_L3_INVALID_ASSOC)			return; 		/* Use x86_cache_size as it might have K7 errata fixes */		assoc		= assocs[l2.assoc];		line_size	= l2.line_size;		lines_per_tag	= l2.lines_per_tag;		size_in_kb	= __this_cpu_read(cpu_info.x86_cache_size);		break;	case 3:		if (!l3.assoc || l3.assoc == AMD_L2_L3_INVALID_ASSOC)			return; 		assoc		= assocs[l3.assoc];		line_size	= l3.line_size;		lines_per_tag	= l3.lines_per_tag;		size_in_kb	= l3.size_encoded * 512;		if (boot_cpu_has(X86_FEATURE_AMD_DCM)) {			size_in_kb	= size_in_kb >> 1;			assoc		= assoc >> 1;		}		break;	default:		return;	} 	eax->split.is_self_initializing		= 1;	eax->split.type				= types[index];	eax->split.level			= levels[index];	eax->split.num_threads_sharing		= 0;	eax->split.num_cores_on_die		= topology_num_cores_per_package(); 	if (assoc == AMD_CPUID4_FULLY_ASSOCIATIVE)		eax->split.is_fully_associative = 1; 	ebx->split.coherency_line_size		= line_size - 1;	ebx->split.ways_of_associativity	= assoc - 1;	ebx->split.physical_line_partition	= lines_per_tag - 1;	ecx->split.number_of_sets		= (size_in_kb * 1024) / line_size /		(ebx->split.ways_of_associativity + 1) - 1;} static int cpuid4_info_fill_done(struct _cpuid4_info *id4, union _cpuid4_leaf_eax eax,				 union _cpuid4_leaf_ebx ebx, union _cpuid4_leaf_ecx ecx){	if (eax.split.type == CTYPE_NULL)		return -EIO; 	id4->eax = eax;	id4->ebx = ebx;	id4->ecx = ecx;	id4->size = (ecx.split.number_of_sets          + 1) *		    (ebx.split.coherency_line_size     + 1) *		    (ebx.split.physical_line_partition + 1) *		    (ebx.split.ways_of_associativity   + 1); 	return 0;} static int amd_fill_cpuid4_info(int index, struct _cpuid4_info *id4){	union _cpuid4_leaf_eax eax;	union _cpuid4_leaf_ebx ebx;	union _cpuid4_leaf_ecx ecx;	u32 ignored; 	if (boot_cpu_has(X86_FEATURE_TOPOEXT) || boot_cpu_data.x86_vendor == X86_VENDOR_HYGON)		cpuid_count(0x8000001d, index, &eax.full, &ebx.full, &ecx.full, &ignored);	else		legacy_amd_cpuid4(index, &eax, &ebx, &ecx); 	return cpuid4_info_fill_done(id4, eax, ebx, ecx);} static int intel_fill_cpuid4_info(int index, struct _cpuid4_info *id4){	union _cpuid4_leaf_eax eax;	union _cpuid4_leaf_ebx ebx;	union _cpuid4_leaf_ecx ecx;	u32 ignored; 	cpuid_count(4, index, &eax.full, &ebx.full, &ecx.full, &ignored); 	return cpuid4_info_fill_done(id4, eax, ebx, ecx);} static int fill_cpuid4_info(int index, struct _cpuid4_info *id4){	u8 cpu_vendor = boot_cpu_data.x86_vendor; 	return (cpu_vendor == X86_VENDOR_AMD || cpu_vendor == X86_VENDOR_HYGON) ?		amd_fill_cpuid4_info(index, id4) :		intel_fill_cpuid4_info(index, id4);} static int find_num_cache_leaves(struct cpuinfo_x86 *c){	unsigned int eax, ebx, ecx, edx, op;	union _cpuid4_leaf_eax cache_eax;	int i = -1; 	/* Do a CPUID(op) loop to calculate num_cache_leaves */	op = (c->x86_vendor == X86_VENDOR_AMD || c->x86_vendor == X86_VENDOR_HYGON) ? 0x8000001d : 4;	do {		++i;		cpuid_count(op, i, &eax, &ebx, &ecx, &edx);		cache_eax.full = eax;	} while (cache_eax.split.type != CTYPE_NULL);	return i;} /* * The max shared threads number comes from CPUID(0x4) EAX[25-14] with input * ECX as cache index. Then right shift apicid by the number's order to get * cache id for this cache node. */static unsigned int get_cache_id(u32 apicid, const struct _cpuid4_info *id4){	unsigned long num_threads_sharing;	int index_msb; 	num_threads_sharing = 1 + id4->eax.split.num_threads_sharing;	index_msb = get_count_order(num_threads_sharing); 	return apicid >> index_msb;} /* * AMD/Hygon CPUs may have multiple LLCs if L3 caches exist. */ void cacheinfo_amd_init_llc_id(struct cpuinfo_x86 *c, u16 die_id){	if (!cpuid_amd_hygon_has_l3_cache())		return; 	if (c->x86 < 0x17) {		/* Pre-Zen: LLC is at the node level */		c->topo.llc_id = die_id;	} else if (c->x86 == 0x17 && c->x86_model <= 0x1F) {		/*		 * Family 17h up to 1F models: LLC is at the core		 * complex level.  Core complex ID is ApicId[3].		 */		c->topo.llc_id = c->topo.apicid >> 3;	} else {		/*		 * Newer families: LLC ID is calculated from the number		 * of threads sharing the L3 cache.		 */		u32 llc_index = find_num_cache_leaves(c) - 1;		struct _cpuid4_info id4 = {}; 		if (!amd_fill_cpuid4_info(llc_index, &id4))			c->topo.llc_id = get_cache_id(c->topo.apicid, &id4);	}} void cacheinfo_hygon_init_llc_id(struct cpuinfo_x86 *c){	if (!cpuid_amd_hygon_has_l3_cache())		return; 	/*	 * Hygons are similar to AMD Family 17h up to 1F models: LLC is	 * at the core complex level.  Core complex ID is ApicId[3].	 */	c->topo.llc_id = c->topo.apicid >> 3;} void init_amd_cacheinfo(struct cpuinfo_x86 *c){	struct cpu_cacheinfo *ci = get_cpu_cacheinfo(c->cpu_index); 	if (boot_cpu_has(X86_FEATURE_TOPOEXT))		ci->num_leaves = find_num_cache_leaves(c);	else if (c->extended_cpuid_level >= 0x80000006)		ci->num_leaves = (cpuid_edx(0x80000006) & 0xf000) ? 4 : 3;} void init_hygon_cacheinfo(struct cpuinfo_x86 *c){	struct cpu_cacheinfo *ci = get_cpu_cacheinfo(c->cpu_index); 	ci->num_leaves = find_num_cache_leaves(c);} static void intel_cacheinfo_done(struct cpuinfo_x86 *c, unsigned int l3,				 unsigned int l2, unsigned int l1i, unsigned int l1d){	/*	 * If llc_id is still unset, then cpuid_level < 4, which implies	 * that the only possibility left is SMT.  Since CPUID(0x2) doesn't	 * specify any shared caches and SMT shares all caches, we can	 * unconditionally set LLC ID to the package ID so that all	 * threads share it.	 */	if (c->topo.llc_id == BAD_APICID)		c->topo.llc_id = c->topo.pkg_id; 	c->x86_cache_size = l3 ? l3 : (l2 ? l2 : l1i + l1d); 	if (!l2)		cpu_detect_cache_sizes(c);} /* * Legacy Intel CPUID(0x2) path if CPUID(0x4) is not available. */static void intel_cacheinfo_0x2(struct cpuinfo_x86 *c){	unsigned int l1i = 0, l1d = 0, l2 = 0, l3 = 0;	const struct leaf_0x2_table *desc;	union leaf_0x2_regs regs;	u8 *ptr; 	if (c->cpuid_level < 2)		return; 	cpuid_leaf_0x2(&regs);	for_each_cpuid_0x2_desc(regs, ptr, desc) {		switch (desc->c_type) {		case CACHE_L1_INST:	l1i += desc->c_size; break;		case CACHE_L1_DATA:	l1d += desc->c_size; break;		case CACHE_L2:		l2  += desc->c_size; break;		case CACHE_L3:		l3  += desc->c_size; break;		}	} 	intel_cacheinfo_done(c, l3, l2, l1i, l1d);} static unsigned int calc_cache_topo_id(struct cpuinfo_x86 *c, const struct _cpuid4_info *id4){	unsigned int num_threads_sharing;	int index_msb; 	num_threads_sharing = 1 + id4->eax.split.num_threads_sharing;	index_msb = get_count_order(num_threads_sharing);	return c->topo.apicid & ~((1 << index_msb) - 1);} static bool intel_cacheinfo_0x4(struct cpuinfo_x86 *c){	struct cpu_cacheinfo *ci = get_cpu_cacheinfo(c->cpu_index);	unsigned int l2_id = BAD_APICID, l3_id = BAD_APICID;	unsigned int l1d = 0, l1i = 0, l2 = 0, l3 = 0; 	if (c->cpuid_level < 4)		return false; 	/*	 * There should be at least one leaf. A non-zero value means	 * that the number of leaves has been previously initialized.	 */	if (!ci->num_leaves)		ci->num_leaves = find_num_cache_leaves(c); 	if (!ci->num_leaves)		return false; 	for (int i = 0; i < ci->num_leaves; i++) {		struct _cpuid4_info id4 = {};		int ret; 		ret = intel_fill_cpuid4_info(i, &id4);		if (ret < 0)			continue; 		switch (id4.eax.split.level) {		case 1:			if (id4.eax.split.type == CTYPE_DATA)				l1d = id4.size / 1024;			else if (id4.eax.split.type == CTYPE_INST)				l1i = id4.size / 1024;			break;		case 2:			l2 = id4.size / 1024;			l2_id = calc_cache_topo_id(c, &id4);			break;		case 3:			l3 = id4.size / 1024;			l3_id = calc_cache_topo_id(c, &id4);			break;		default:			break;		}	} 	c->topo.l2c_id = l2_id;	c->topo.llc_id = (l3_id == BAD_APICID) ? l2_id : l3_id;	intel_cacheinfo_done(c, l3, l2, l1i, l1d);	return true;} void init_intel_cacheinfo(struct cpuinfo_x86 *c){	/* Don't use CPUID(0x2) if CPUID(0x4) is supported. */	if (intel_cacheinfo_0x4(c))		return; 	intel_cacheinfo_0x2(c);} /* * <linux/cacheinfo.h> shared_cpu_map setup, AMD/Hygon */static int __cache_amd_cpumap_setup(unsigned int cpu, int index,				    const struct _cpuid4_info *id4){	struct cpu_cacheinfo *this_cpu_ci;	struct cacheinfo *ci;	int i, sibling; 	/*	 * For L3, always use the pre-calculated cpu_llc_shared_mask	 * to derive shared_cpu_map.	 */	if (index == 3) {		for_each_cpu(i, cpu_llc_shared_mask(cpu)) {

Lines 1–500 of 820.