From nobody Mon Apr 6 10:43:32 2026 Received: from mgamail.intel.com (mgamail.intel.com [198.175.65.21]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 172363CCFC7 for ; Thu, 19 Mar 2026 12:39:21 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.175.65.21 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773923966; cv=none; b=DlM1I43ENlzLL8wjvR2ouFxljxJadeqJah5f088Sonseu+jzvuSqTVZ24I5jqfVuX0lrgBEs5BG0DAOuwT+OlVRCqOb9LWj8KlA+fcH5RFp4biMpoA0AsDvu6Q/lutX1FxDSfddjOphtH7dNfme1+7+fLYDGXcOWfY5koewk8Fs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773923966; c=relaxed/simple; bh=zCxZGQf+i7YQXi8sSRk2vAy3Zu7kEpldvqdaiCEfzS4=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:To:Cc; b=XQfrKQNAxzjFwtLQUhulXBqPK5mcaG0zGOP7eCuZ9IUzTOCnS0sKG8+Q4sU5ufSMPApUuTVNyJetXDlo73yU4VNeS00+IMo5W6gItNKn2cG7L6rUFOgjjjtG9ApboTdK2kDrvgGInwO0EWinpiAr9NZnx2O9W2NxiPnlrfen6EY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.intel.com; spf=pass smtp.mailfrom=linux.intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=DH5S8GfQ; arc=none smtp.client-ip=198.175.65.21 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="DH5S8GfQ" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1773923963; x=1805459963; h=from:date:subject:mime-version:content-transfer-encoding: message-id:to:cc; bh=zCxZGQf+i7YQXi8sSRk2vAy3Zu7kEpldvqdaiCEfzS4=; b=DH5S8GfQ3bpIdfgBG/hQJKgtI8DTRaD3/c7PNgixjtYlVdarf0uUxKQE u3CKUDlYvkIO/5nHiA5fHJVRKmC7Y7iXxUnksTykwezJCAP475EpNhDXc u1mvJ6oIUUIsqmXD7ViKSRJPN3bytEAR/XsTxsN5uLZYLae61p9uhcqKg 7cDrtlk/sN7ykJN/gKul++MuUkdeKYgTC1QqFiDyVll77FuLkWgLWg+Qh VvOyP85dgP5OpsO0VL5ElWqhzLyX5+djB/EvSWW1J3XrJXuxfMoOH7ZZ3 bTfhzMkTp8CSjoyKUnusjtDLUyPckKrdggdF5xPGb+qUqa003AWh183zs w==; X-CSE-ConnectionGUID: tOEtHRc+TB6APxf5ske/Ig== X-CSE-MsgGUID: QhcwlhalTFGOK3aA4TW6XA== X-IronPort-AV: E=McAfee;i="6800,10657,11733"; a="74880636" X-IronPort-AV: E=Sophos;i="6.23,129,1770624000"; d="scan'208";a="74880636" Received: from fmviesa005.fm.intel.com ([10.60.135.145]) by orvoesa113.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 19 Mar 2026 05:39:22 -0700 X-CSE-ConnectionGUID: /SLWoq4eSauJmbSvAbYlSg== X-CSE-MsgGUID: a+mAIXUTQcydX5H7TpGM6Q== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.23,129,1770624000"; d="scan'208";a="227637725" Received: from unknown (HELO [172.25.112.21]) ([172.25.112.21]) by fmviesa005.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 19 Mar 2026 05:39:21 -0700 From: Ricardo Neri Date: Thu, 19 Mar 2026 05:38:00 -0700 Subject: [PATCH RFC] x86/cacheinfo: Remove get_cache_id() due to ID collisions Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260319-rneri-x86-cacheinfoid-bug-v1-1-5875849dd0e5@linux.intel.com> X-B4-Tracking: v=1; b=H4sIACfuu2kC/6tWKk4tykwtVrJSqFYqSi3LLM7MzwNyDHUUlJIzE vPSU3UzU4B8JSMDIzMDIyMD3aI8oAbdCgsz3eTE5IzUzLy0/MwU3aTSdF1zw1TjNPNUI4tUSxM loP6CotS0zAqw2dFKQW7OSrG1tQBA6z2ucAAAAA== To: K Prateek Nayak , "Ahmed S. Darwish" Cc: Len Brown , Ricardo Neri , linux-kernel@vger.kernel.org, Ricardo Neri X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1773923904; l=6375; i=ricardo.neri-calderon@linux.intel.com; s=20250602; h=from:subject:message-id; bh=zCxZGQf+i7YQXi8sSRk2vAy3Zu7kEpldvqdaiCEfzS4=; b=Tj9Lvcy2CEWGyXxmDpZ/ovjoPuLEmlc386EVzo0j3rOyKdnZCa4sr5s3WHMILhZgycY+5e6Bv 18EBeuYFiKqDpQnyXzV8KYBq4geI8HlCa4yO7zsL3MAQaAH2a0yqGI8 X-Developer-Key: i=ricardo.neri-calderon@linux.intel.com; a=ed25519; pk=NfZw5SyQ2lxVfmNMaMR6KUj3+0OhcwDPyRzFDH9gY2w= calc_cache_topo_id() and get_cache_id() both compute cache IDs used to determine which CPUs share a cache. Intel processors use calc_cache_topo_id(), while Hygon and recent AMD processors use get_cache_id(). Intel processors use get_cache_id() as well, but only to populate /sys/devices/system/cpu/cpuN/cache/index*/id. Besides duplicating logic, get_cache_id() returns incorrect and colliding IDs. calc_cache_topo_id() masks off the least-significant N bits of an APIC ID. N is the smallest integer such that 2^N is greater than or equal to the number of logical CPUs sharing a cache as reported in CPUID.4.. EAX[25:14]. This produces unique IDs for all CPUs that share a cache. get_cache_id() instead right-shifts the APIC ID by N. This only works when N is consistent across CPUs. On systems with heterogeneous cores (e.g., Intel hybrid processors), different cores report different numbers of logical CPUs sharing a cache. This results in inconsistent shifts and, consequently, cache ID collisions between CPUs that do not actually share the cache. Consider the APIC IDs and CPUID leaf 4 data for the L1 cache of selected CPUs from a Meteor Lake-based Lenovo ThinkPad T16 Gen 3: CPU APICID num_threads_sharing calc_cache_topo_id() get_cache_id() 2 0x18 2 0x18 0xc 3 0x19 2 0x18 0xc 10 0xc 1 0xc 0xc CPUs 2 and 3 are the two logical CPUs of one core. They share the L1 cache. CPU10 does not, yet get_cache_id() assigns it the same ID, illustrating the collision. While masking and shifting are equivalent when N is identical across CPUs, using a shift makes the computation dependent on per-CPU topology and therefore unsafe. Switch all users to calc_cache_topo_id() and remove get_cache_id(). The values exposed in /sys/devices/system/cpu/cpuN/cache/index*/id will change, but they will remain unique and continue to reflect shared caches correctly. Reported-by: Len Brown Tested-by: Len Brown Signed-off-by: Ricardo Neri --- Hi Prateek, Ahmed, I would like to get your feedback on this patch before involving the x86 maintainers. I want to ensure that I don't break Hygon or AMD systems or miss any subtle detail. I tested this patch on Genoa and Turin systems, both of which used get_cache_id() to compute L3 cache IDs. The IDs changed but the rest of the files under /sys/devices/system/cpu/cpuN/cache/index* remain the the same. --- arch/x86/kernel/cpu/cacheinfo.c | 34 ++++++++-------------------------- 1 file changed, 8 insertions(+), 26 deletions(-) diff --git a/arch/x86/kernel/cpu/cacheinfo.c b/arch/x86/kernel/cpu/cacheinf= o.c index 51a95b07831f..56194ef55d8e 100644 --- a/arch/x86/kernel/cpu/cacheinfo.c +++ b/arch/x86/kernel/cpu/cacheinfo.c @@ -289,20 +289,14 @@ static int find_num_cache_leaves(struct cpuinfo_x86 *= c) return i; } =20 -/* - * The max shared threads number comes from CPUID(0x4) EAX[25-14] with inp= ut - * 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 *id= 4) +static unsigned int calc_cache_topo_id(struct cpuinfo_x86 *c, const struct= _cpuid4_info *id4) { - unsigned long num_threads_sharing; + unsigned int num_threads_sharing; int index_msb; =20 num_threads_sharing =3D 1 + id4->eax.split.num_threads_sharing; index_msb =3D get_count_order(num_threads_sharing); - - return apicid >> index_msb; + return c->topo.apicid & ~((1 << index_msb) - 1); } =20 /* @@ -332,7 +326,7 @@ void cacheinfo_amd_init_llc_id(struct cpuinfo_x86 *c, u= 16 die_id) struct _cpuid4_info id4 =3D {}; =20 if (!amd_fill_cpuid4_info(llc_index, &id4)) - c->topo.llc_id =3D get_cache_id(c->topo.apicid, &id4); + c->topo.llc_id =3D calc_cache_topo_id(c, &id4); } } =20 @@ -410,16 +404,6 @@ static void intel_cacheinfo_0x2(struct cpuinfo_x86 *c) intel_cacheinfo_done(c, l3, l2, l1i, l1d); } =20 -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 =3D 1 + id4->eax.split.num_threads_sharing; - index_msb =3D 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 =3D get_cpu_cacheinfo(c->cpu_index); @@ -551,7 +535,7 @@ static void __cache_cpumap_setup(unsigned int cpu, int = index, struct cpuinfo_x86 *c =3D &cpu_data(cpu); struct cacheinfo *ci, *sibling_ci; unsigned long num_threads_sharing; - int index_msb, i; + int i; =20 if (c->x86_vendor =3D=3D X86_VENDOR_AMD || c->x86_vendor =3D=3D X86_VENDO= R_HYGON) { if (__cache_amd_cpumap_setup(cpu, index, id4)) @@ -565,10 +549,9 @@ static void __cache_cpumap_setup(unsigned int cpu, int= index, if (num_threads_sharing =3D=3D 1) return; =20 - index_msb =3D get_count_order(num_threads_sharing); - for_each_online_cpu(i) - if (cpu_data(i).topo.apicid >> index_msb =3D=3D c->topo.apicid >> index_= msb) { + /* Caller calculated the cache ID of @cpu */ + if (id4->id =3D=3D calc_cache_topo_id(&cpu_data(i), id4)) { struct cpu_cacheinfo *sib_cpu_ci =3D get_cpu_cacheinfo(i); =20 /* Skip if itself or no cacheinfo */ @@ -612,7 +595,6 @@ int populate_cache_leaves(unsigned int cpu) struct cpu_cacheinfo *this_cpu_ci =3D get_cpu_cacheinfo(cpu); struct cacheinfo *ci =3D this_cpu_ci->info_list; u8 cpu_vendor =3D boot_cpu_data.x86_vendor; - u32 apicid =3D cpu_data(cpu).topo.apicid; struct amd_northbridge *nb =3D NULL; struct _cpuid4_info id4 =3D {}; int idx, ret; @@ -622,7 +604,7 @@ int populate_cache_leaves(unsigned int cpu) if (ret) return ret; =20 - id4.id =3D get_cache_id(apicid, &id4); + id4.id =3D calc_cache_topo_id(&cpu_data(cpu), &id4); =20 if (cpu_vendor =3D=3D X86_VENDOR_AMD || cpu_vendor =3D=3D X86_VENDOR_HYG= ON) nb =3D amd_init_l3_cache(idx); --- base-commit: 9fefc4bad1a3db12ecf6ca8e9886b29721249e77 change-id: 20260220-rneri-x86-cacheinfoid-bug-71e3f7e28e94 Best regards, --=20 Ricardo Neri