// 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
*/
/* * 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).
*/
staticvoid legacy_amd_cpuid4(int index, union _cpuid4_leaf_eax *eax, union _cpuid4_leaf_ebx *ebx, union _cpuid4_leaf_ecx *ecx)
{ unsignedint dummy, line_size, lines_per_tag, assoc, size_in_kb; union l1_cache l1i, l1d, *l1; union l2_cache l2; union l3_cache l3;
staticint 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;
staticint find_num_cache_leaves(struct cpuinfo_x86 *c)
{ unsignedint 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;
}
/* * 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;
} elseif (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 eax, ebx, ecx, edx, num_sharing_cache = 0;
u32 llc_index = find_num_cache_leaves(c) - 1;
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;
}
staticvoid intel_cacheinfo_done(struct cpuinfo_x86 *c, unsignedint l3, unsignedint l2, unsignedint l1i, unsignedint 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;
/* * 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) returnfalse;
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;
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
*/ staticint __cache_amd_cpumap_setup(unsignedint cpu, int index, conststruct _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)) {
this_cpu_ci = get_cpu_cacheinfo(i); if (!this_cpu_ci->info_list) continue;
int init_cache_level(unsignedint cpu)
{ struct cpu_cacheinfo *ci = get_cpu_cacheinfo(cpu);
/* There should be at least one leaf. */ if (!ci->num_leaves) return -ENOENT;
return 0;
}
/* * 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.
*/ staticvoid get_cache_id(int cpu, struct _cpuid4_info *id4)
{ struct cpuinfo_x86 *c = &cpu_data(cpu); unsignedlong num_threads_sharing; int index_msb;
/* * Disable and enable caches. Needed for changing MTRRs and the PAT MSR. * * Since we are disabling the cache don't allow any interrupts, * they would run extremely slow and would only increase the pain. * * The caller must ensure that local interrupts are disabled and * are reenabled after cache_enable() has been called.
*/ staticunsignedlong saved_cr4; static DEFINE_RAW_SPINLOCK(cache_disable_lock);
/* * Cache flushing is the most time-consuming step when programming the * MTRRs. On many Intel CPUs without known erratas, it can be skipped * if the CPU declares cache self-snooping support.
*/ staticvoid maybe_flush_caches(void)
{ if (!static_cpu_has(X86_FEATURE_SELFSNOOP))
wbinvd();
}
/* * This is not ideal since the cache is only flushed/disabled * for this CPU while the MTRRs are changed, but changing this * requires more invasive changes to the way the kernel boots.
*/
raw_spin_lock(&cache_disable_lock);
/* Enter the no-fill (CD=1, NW=0) cache mode and flush caches. */
cr0 = read_cr0() | X86_CR0_CD;
write_cr0(cr0);
maybe_flush_caches();
/* Save value of CR4 and clear Page Global Enable (bit 7) */ if (cpu_feature_enabled(X86_FEATURE_PGE)) {
saved_cr4 = __read_cr4();
__write_cr4(saved_cr4 & ~X86_CR4_PGE);
}
/* Flush all TLBs via a mov %cr3, %reg; mov %reg, %cr3 */
count_vm_tlb_event(NR_TLB_LOCAL_FLUSH_ALL);
flush_tlb_local();
if (cpu_feature_enabled(X86_FEATURE_MTRR))
mtrr_disable();
maybe_flush_caches();
}
void cache_enable(void) __releases(cache_disable_lock)
{ /* Flush TLBs (no need to flush caches - they are disabled) */
count_vm_tlb_event(NR_TLB_LOCAL_FLUSH_ALL);
flush_tlb_local();
if (cpu_feature_enabled(X86_FEATURE_MTRR))
mtrr_enable();
if (!memory_caching_control || get_cache_aps_delayed_init()) return 0;
/* * Ideally we should hold mtrr_mutex here to avoid MTRR entries * changed, but this routine will be called in CPU boot time, * holding the lock breaks it. * * This routine is called in two cases: * * 1. very early time of software resume, when there absolutely * isn't MTRR entry changes; * * 2. CPU hotadd time. We let mtrr_add/del_page hold cpuhotplug * lock to prevent MTRR entry changes
*/
stop_machine_from_inactive_cpu(cache_rendezvous_handler, NULL,
cpu_cacheinfo_mask);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.