// 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(®s); 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. Load more
// 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(®s); 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)) {