[PATCH v8 2/2] x86/cacheinfo: Delete global num_cache_leaves

Ricardo Neri posted 2 patches 1 year, 2 months ago
[PATCH v8 2/2] x86/cacheinfo: Delete global num_cache_leaves
Posted by Ricardo Neri 1 year, 2 months ago
Linux remembers cpu_cachinfo::num_leaves per CPU, but x86 initializes all
CPUs from the same global "num_cache_leaves".

This is erroneous on systems such as Meteor Lake, where each CPU has a
distinct num_leaves value. Delete the global "num_cache_leaves" and
initialize num_leaves on each CPU.

init_cache_level() no longer needs to set num_leaves. Also, it never had to
set num_levels as it is unnecessary in x86. Keep checking for zero cache
leaves. Such condition indicates a bug.

Reviewed-by: Andreas Herrmann <aherrmann@suse.de>
Reviewed-by: Len Brown <len.brown@intel.com>
Reviewed-by: Nikolay Borisov <nik.borisov@suse.com>
Tested-by: Andreas Herrmann <aherrmann@suse.de>
Signed-off-by: Ricardo Neri <ricardo.neri-calderon@linux.intel.com>
---
Cc: Andreas Herrmann <aherrmann@suse.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Chen Yu <yu.c.chen@intel.com>
Cc: Huang Ying <ying.huang@intel.com>
Cc: Len Brown <len.brown@intel.com>
Cc: Nikolay Borisov <nik.borisov@suse.com>
Cc: Radu Rendec <rrendec@redhat.com>
Cc: Pierre Gondois <Pierre.Gondois@arm.com>
Cc: Pu Wen <puwen@hygon.cn>
Cc: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
Cc: Sudeep Holla <sudeep.holla@arm.com>
Cc: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
Cc: Will Deacon <will@kernel.org>
Cc: Zhang Rui <rui.zhang@intel.com>
Cc: linux-arm-kernel@lists.infradead.org
Cc: stable@vger.kernel.org # 6.3+
---
After this change, all CPUs will traverse CPUID leaf 0x4 when booted for
the first time. On systems with symmetric cache topologies this is
useless work.

Creating a list of processor models that have asymmetric cache topologies
was considered. The burden of maintaining such list would outweigh the
performance benefit of skipping this extra step.
---
Changes since v7:
 * Removed an ugly linebreak. (Boris)
 * Folded patch 3/3 into 2/3 as both patches deal with init_cache_level().
   (Boris)
 * Removed the [set,get]_num_cache_leaves() wrappers. Instead, use the
   existing get_cpu_cacheinfo(). (Boris)
 * Future-proof init_cache_level() for cases in which cpu_cacheinfo::
   num_leaves is still zero afer cache info initialization.

Changes since v6:
 * None

Changes since v5:
 * Reordered the arguments of set_num_cache_leaves() for readability.
   (Nikolay)
 * Added Reviewed-by tag from Nikolay and Andreas. Thanks!
 * Added Tested-by tag from Andreas. Thanks!

Changes since v4:
 * None

Changes since v3:
 * Rebased on v6.7-rc5.

Changes since v2:
 * None

Changes since v1:
 * Do not make num_cache_leaves a per-CPU variable. Instead, reuse the
   existing per-CPU ci_cpu_cacheinfo variable. (Dave Hansen)
---
 arch/x86/kernel/cpu/cacheinfo.c | 41 +++++++++++++++------------------
 1 file changed, 18 insertions(+), 23 deletions(-)

diff --git a/arch/x86/kernel/cpu/cacheinfo.c b/arch/x86/kernel/cpu/cacheinfo.c
index 392d09c936d6..95e38ab98a72 100644
--- a/arch/x86/kernel/cpu/cacheinfo.c
+++ b/arch/x86/kernel/cpu/cacheinfo.c
@@ -178,8 +178,6 @@ struct _cpuid4_info_regs {
 	struct amd_northbridge *nb;
 };
 
-static unsigned short num_cache_leaves;
-
 /* AMD doesn't have CPUID4. Emulate it here to report the same
    information to the user.  This makes some assumptions about the machine:
    L2 not shared, no SMT etc. that is currently true on AMD CPUs.
@@ -718,19 +716,21 @@ void cacheinfo_hygon_init_llc_id(struct cpuinfo_x86 *c)
 void init_amd_cacheinfo(struct cpuinfo_x86 *c)
 {
 
+	unsigned int cpu = c->cpu_index;
+
 	if (boot_cpu_has(X86_FEATURE_TOPOEXT)) {
-		num_cache_leaves = find_num_cache_leaves(c);
+		get_cpu_cacheinfo(cpu)->num_leaves = find_num_cache_leaves(c);
 	} else if (c->extended_cpuid_level >= 0x80000006) {
 		if (cpuid_edx(0x80000006) & 0xf000)
-			num_cache_leaves = 4;
+			get_cpu_cacheinfo(cpu)->num_leaves = 4;
 		else
-			num_cache_leaves = 3;
+			get_cpu_cacheinfo(cpu)->num_leaves = 3;
 	}
 }
 
 void init_hygon_cacheinfo(struct cpuinfo_x86 *c)
 {
-	num_cache_leaves = find_num_cache_leaves(c);
+	get_cpu_cacheinfo(c->cpu_index)->num_leaves = find_num_cache_leaves(c);
 }
 
 void init_intel_cacheinfo(struct cpuinfo_x86 *c)
@@ -742,19 +742,18 @@ void init_intel_cacheinfo(struct cpuinfo_x86 *c)
 	unsigned int l2_id = 0, l3_id = 0, num_threads_sharing, index_msb;
 
 	if (c->cpuid_level > 3) {
-		static int is_initialized;
-
-		if (is_initialized == 0) {
-			/* Init num_cache_leaves from boot CPU */
-			num_cache_leaves = find_num_cache_leaves(c);
-			is_initialized++;
-		}
+		/*
+		 * There should be at least one leaf. A non-zero value means
+		 * that the number of leaves has been initialized.
+		 */
+		if (!get_cpu_cacheinfo(c->cpu_index)->num_leaves)
+			get_cpu_cacheinfo(c->cpu_index)->num_leaves = find_num_cache_leaves(c);
 
 		/*
 		 * Whenever possible use cpuid(4), deterministic cache
 		 * parameters cpuid leaf to find the cache details
 		 */
-		for (i = 0; i < num_cache_leaves; i++) {
+		for (i = 0; i < get_cpu_cacheinfo(c->cpu_index)->num_leaves; i++) {
 			struct _cpuid4_info_regs this_leaf = {};
 			int retval;
 
@@ -790,14 +789,14 @@ void init_intel_cacheinfo(struct cpuinfo_x86 *c)
 	 * Don't use cpuid2 if cpuid4 is supported. For P4, we use cpuid2 for
 	 * trace cache
 	 */
-	if ((num_cache_leaves == 0 || c->x86 == 15) && c->cpuid_level > 1) {
+	if ((!get_cpu_cacheinfo(c->cpu_index)->num_leaves || c->x86 == 15) && c->cpuid_level > 1) {
 		/* supports eax=2  call */
 		int j, n;
 		unsigned int regs[4];
 		unsigned char *dp = (unsigned char *)regs;
 		int only_trace = 0;
 
-		if (num_cache_leaves != 0 && c->x86 == 15)
+		if (get_cpu_cacheinfo(c->cpu_index)->num_leaves && c->x86 == 15)
 			only_trace = 1;
 
 		/* Number of times to iterate */
@@ -991,14 +990,10 @@ static void ci_leaf_init(struct cacheinfo *this_leaf,
 
 int init_cache_level(unsigned int cpu)
 {
-	struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
-
-	if (!num_cache_leaves)
+	/* There should be at least one leaf. */
+	if (!get_cpu_cacheinfo(cpu)->num_leaves)
 		return -ENOENT;
-	if (!this_cpu_ci)
-		return -EINVAL;
-	this_cpu_ci->num_levels = 3;
-	this_cpu_ci->num_leaves = num_cache_leaves;
+
 	return 0;
 }
 
-- 
2.34.1
Re: [PATCH v8 2/2] x86/cacheinfo: Delete global num_cache_leaves
Posted by Borislav Petkov 1 year, 2 months ago
On Wed, Nov 27, 2024 at 04:22:47PM -0800, Ricardo Neri wrote:
>  arch/x86/kernel/cpu/cacheinfo.c | 41 +++++++++++++++------------------
>  1 file changed, 18 insertions(+), 23 deletions(-)

Does that work too?

---
diff --git a/arch/x86/kernel/cpu/cacheinfo.c b/arch/x86/kernel/cpu/cacheinfo.c
index 95e38ab98a72..e6fa03ed9172 100644
--- a/arch/x86/kernel/cpu/cacheinfo.c
+++ b/arch/x86/kernel/cpu/cacheinfo.c
@@ -715,22 +715,23 @@ void cacheinfo_hygon_init_llc_id(struct cpuinfo_x86 *c)
 
 void init_amd_cacheinfo(struct cpuinfo_x86 *c)
 {
-
-	unsigned int cpu = c->cpu_index;
+	struct cpu_cacheinfo *ci = get_cpu_cacheinfo(c->cpu_index);
 
 	if (boot_cpu_has(X86_FEATURE_TOPOEXT)) {
-		get_cpu_cacheinfo(cpu)->num_leaves = find_num_cache_leaves(c);
+		ci->num_leaves = find_num_cache_leaves(c);
 	} else if (c->extended_cpuid_level >= 0x80000006) {
 		if (cpuid_edx(0x80000006) & 0xf000)
-			get_cpu_cacheinfo(cpu)->num_leaves = 4;
+			ci->num_leaves = 4;
 		else
-			get_cpu_cacheinfo(cpu)->num_leaves = 3;
+			ci->num_leaves = 3;
 	}
 }
 
 void init_hygon_cacheinfo(struct cpuinfo_x86 *c)
 {
-	get_cpu_cacheinfo(c->cpu_index)->num_leaves = find_num_cache_leaves(c);
+	struct cpu_cacheinfo *ci = get_cpu_cacheinfo(c->cpu_index);
+
+	ci->num_leaves = find_num_cache_leaves(c);
 }
 
 void init_intel_cacheinfo(struct cpuinfo_x86 *c)
@@ -740,20 +741,21 @@ void init_intel_cacheinfo(struct cpuinfo_x86 *c)
 	unsigned int new_l1d = 0, new_l1i = 0; /* Cache sizes from cpuid(4) */
 	unsigned int new_l2 = 0, new_l3 = 0, i; /* Cache sizes from cpuid(4) */
 	unsigned int l2_id = 0, l3_id = 0, num_threads_sharing, index_msb;
+	struct cpu_cacheinfo *ci = get_cpu_cacheinfo(c->cpu_index);
 
 	if (c->cpuid_level > 3) {
 		/*
 		 * There should be at least one leaf. A non-zero value means
 		 * that the number of leaves has been initialized.
 		 */
-		if (!get_cpu_cacheinfo(c->cpu_index)->num_leaves)
-			get_cpu_cacheinfo(c->cpu_index)->num_leaves = find_num_cache_leaves(c);
+		if (!ci->num_leaves)
+			ci->num_leaves = find_num_cache_leaves(c);
 
 		/*
 		 * Whenever possible use cpuid(4), deterministic cache
 		 * parameters cpuid leaf to find the cache details
 		 */
-		for (i = 0; i < get_cpu_cacheinfo(c->cpu_index)->num_leaves; i++) {
+		for (i = 0; i < ci->num_leaves; i++) {
 			struct _cpuid4_info_regs this_leaf = {};
 			int retval;
 
@@ -789,14 +791,14 @@ void init_intel_cacheinfo(struct cpuinfo_x86 *c)
 	 * Don't use cpuid2 if cpuid4 is supported. For P4, we use cpuid2 for
 	 * trace cache
 	 */
-	if ((!get_cpu_cacheinfo(c->cpu_index)->num_leaves || c->x86 == 15) && c->cpuid_level > 1) {
+	if ((!ci->num_leaves || c->x86 == 15) && c->cpuid_level > 1) {
 		/* supports eax=2  call */
 		int j, n;
 		unsigned int regs[4];
 		unsigned char *dp = (unsigned char *)regs;
 		int only_trace = 0;
 
-		if (get_cpu_cacheinfo(c->cpu_index)->num_leaves && c->x86 == 15)
+		if (ci->num_leaves && c->x86 == 15)
 			only_trace = 1;
 
 		/* Number of times to iterate */
@@ -990,8 +992,10 @@ static void ci_leaf_init(struct cacheinfo *this_leaf,
 
 int init_cache_level(unsigned int cpu)
 {
+	struct cpu_cacheinfo *ci = get_cpu_cacheinfo(cpu);
+
 	/* There should be at least one leaf. */
-	if (!get_cpu_cacheinfo(cpu)->num_leaves)
+	if (!ci->num_leaves)
 		return -ENOENT;
 
 	return 0;

-- 
Regards/Gruss,
    Boris.

https://people.kernel.org/tglx/notes-about-netiquette
Re: [PATCH v8 2/2] x86/cacheinfo: Delete global num_cache_leaves
Posted by Ricardo Neri 1 year, 2 months ago
On Wed, Dec 04, 2024 at 03:32:06PM +0100, Borislav Petkov wrote:
> On Wed, Nov 27, 2024 at 04:22:47PM -0800, Ricardo Neri wrote:
> >  arch/x86/kernel/cpu/cacheinfo.c | 41 +++++++++++++++------------------
> >  1 file changed, 18 insertions(+), 23 deletions(-)
> 
> Does that work too?
> 
> ---
> diff --git a/arch/x86/kernel/cpu/cacheinfo.c b/arch/x86/kernel/cpu/cacheinfo.c
> index 95e38ab98a72..e6fa03ed9172 100644
> --- a/arch/x86/kernel/cpu/cacheinfo.c
> +++ b/arch/x86/kernel/cpu/cacheinfo.c
> @@ -715,22 +715,23 @@ void cacheinfo_hygon_init_llc_id(struct cpuinfo_x86 *c)
>  
>  void init_amd_cacheinfo(struct cpuinfo_x86 *c)
>  {
> -
> -	unsigned int cpu = c->cpu_index;
> +	struct cpu_cacheinfo *ci = get_cpu_cacheinfo(c->cpu_index);
>  
>  	if (boot_cpu_has(X86_FEATURE_TOPOEXT)) {
> -		get_cpu_cacheinfo(cpu)->num_leaves = find_num_cache_leaves(c);
> +		ci->num_leaves = find_num_cache_leaves(c);
>  	} else if (c->extended_cpuid_level >= 0x80000006) {
>  		if (cpuid_edx(0x80000006) & 0xf000)
> -			get_cpu_cacheinfo(cpu)->num_leaves = 4;
> +			ci->num_leaves = 4;
>  		else
> -			get_cpu_cacheinfo(cpu)->num_leaves = 3;
> +			ci->num_leaves = 3;
>  	}
>  }
>  
>  void init_hygon_cacheinfo(struct cpuinfo_x86 *c)
>  {
> -	get_cpu_cacheinfo(c->cpu_index)->num_leaves = find_num_cache_leaves(c);
> +	struct cpu_cacheinfo *ci = get_cpu_cacheinfo(c->cpu_index);
> +
> +	ci->num_leaves = find_num_cache_leaves(c);
>  }
>  
>  void init_intel_cacheinfo(struct cpuinfo_x86 *c)
> @@ -740,20 +741,21 @@ void init_intel_cacheinfo(struct cpuinfo_x86 *c)
>  	unsigned int new_l1d = 0, new_l1i = 0; /* Cache sizes from cpuid(4) */
>  	unsigned int new_l2 = 0, new_l3 = 0, i; /* Cache sizes from cpuid(4) */
>  	unsigned int l2_id = 0, l3_id = 0, num_threads_sharing, index_msb;
> +	struct cpu_cacheinfo *ci = get_cpu_cacheinfo(c->cpu_index);
>  
>  	if (c->cpuid_level > 3) {
>  		/*
>  		 * There should be at least one leaf. A non-zero value means
>  		 * that the number of leaves has been initialized.
>  		 */
> -		if (!get_cpu_cacheinfo(c->cpu_index)->num_leaves)
> -			get_cpu_cacheinfo(c->cpu_index)->num_leaves = find_num_cache_leaves(c);
> +		if (!ci->num_leaves)
> +			ci->num_leaves = find_num_cache_leaves(c);
>  
>  		/*
>  		 * Whenever possible use cpuid(4), deterministic cache
>  		 * parameters cpuid leaf to find the cache details
>  		 */
> -		for (i = 0; i < get_cpu_cacheinfo(c->cpu_index)->num_leaves; i++) {
> +		for (i = 0; i < ci->num_leaves; i++) {
>  			struct _cpuid4_info_regs this_leaf = {};
>  			int retval;
>  
> @@ -789,14 +791,14 @@ void init_intel_cacheinfo(struct cpuinfo_x86 *c)
>  	 * Don't use cpuid2 if cpuid4 is supported. For P4, we use cpuid2 for
>  	 * trace cache
>  	 */
> -	if ((!get_cpu_cacheinfo(c->cpu_index)->num_leaves || c->x86 == 15) && c->cpuid_level > 1) {
> +	if ((!ci->num_leaves || c->x86 == 15) && c->cpuid_level > 1) {
>  		/* supports eax=2  call */
>  		int j, n;
>  		unsigned int regs[4];
>  		unsigned char *dp = (unsigned char *)regs;
>  		int only_trace = 0;
>  
> -		if (get_cpu_cacheinfo(c->cpu_index)->num_leaves && c->x86 == 15)
> +		if (ci->num_leaves && c->x86 == 15)
>  			only_trace = 1;
>  
>  		/* Number of times to iterate */
> @@ -990,8 +992,10 @@ static void ci_leaf_init(struct cacheinfo *this_leaf,
>  
>  int init_cache_level(unsigned int cpu)
>  {
> +	struct cpu_cacheinfo *ci = get_cpu_cacheinfo(cpu);
> +
>  	/* There should be at least one leaf. */
> -	if (!get_cpu_cacheinfo(cpu)->num_leaves)
> +	if (!ci->num_leaves)
>  		return -ENOENT;
>  
>  	return 0;

Yes, this looks OK to me and is an improvement.

I can post a v9 with these changes.

I think I can keep the Reviewed-by tags as they were given to Patch 1.

Thanks and BR,
Ricardo
Re: [PATCH v8 2/2] x86/cacheinfo: Delete global num_cache_leaves
Posted by Borislav Petkov 1 year, 2 months ago
On Wed, Dec 04, 2024 at 08:39:11AM -0800, Ricardo Neri wrote:
> Yes, this looks OK to me and is an improvement.
> 
> I can post a v9 with these changes.

No need - I have everything here. I can give you a branch to test one last
time before I queue.

> I think I can keep the Reviewed-by tags as they were given to Patch 1.

I'll take care of that too.

Just sit tight and wait for a note from me. :)

Thx.

-- 
Regards/Gruss,
    Boris.

https://people.kernel.org/tglx/notes-about-netiquette
Re: [PATCH v8 2/2] x86/cacheinfo: Delete global num_cache_leaves
Posted by Ricardo Neri 1 year, 2 months ago
On Wed, Dec 04, 2024 at 08:32:05PM +0100, Borislav Petkov wrote:
> On Wed, Dec 04, 2024 at 08:39:11AM -0800, Ricardo Neri wrote:
> > Yes, this looks OK to me and is an improvement.
> > 
> > I can post a v9 with these changes.
> 
> No need - I have everything here. I can give you a branch to test one last
> time before I queue.

Sure! Thanks! I can test it on Meteor Lake and non-Meteor Lake.
Re: [PATCH v8 2/2] x86/cacheinfo: Delete global num_cache_leaves
Posted by Borislav Petkov 1 year, 2 months ago
On Wed, Dec 04, 2024 at 02:22:38PM -0800, Ricardo Neri wrote:
> Sure! Thanks! I can test it on Meteor Lake and non-Meteor Lake.

Ok, pls run this:

https://git.kernel.org/pub/scm/linux/kernel/git/bp/bp.git/log/?h=tip-x86-urgent-wip

Thx.

-- 
Regards/Gruss,
    Boris.

https://people.kernel.org/tglx/notes-about-netiquette
Re: [PATCH v8 2/2] x86/cacheinfo: Delete global num_cache_leaves
Posted by Ricardo Neri 1 year, 2 months ago
On Thu, Dec 05, 2024 at 04:14:45PM +0100, Borislav Petkov wrote:
> On Wed, Dec 04, 2024 at 02:22:38PM -0800, Ricardo Neri wrote:
> > Sure! Thanks! I can test it on Meteor Lake and non-Meteor Lake.
> 
> Ok, pls run this:
> 
> https://git.kernel.org/pub/scm/linux/kernel/git/bp/bp.git/log/?h=tip-x86-urgent-wip

I tested this branch on Alder Lake, Meteor Lake, Sapphire Rapids,
Broadwell server, Granite Rapids, Icelake server, Sierra Forest, and Rome.
/sys/devices/system/cpu/cpu*/cache is populated correctly.

I ran the tests documented in

https://lore.kernel.org/lkml/20230912032350.GA17008@ranerica-svr.sc.intel.com/

Thanks and BR,
Ricardo
Re: [PATCH v8 2/2] x86/cacheinfo: Delete global num_cache_leaves
Posted by Borislav Petkov 1 year, 2 months ago
On Thu, Dec 05, 2024 at 05:47:29PM -0800, Ricardo Neri wrote:
> I tested this branch on Alder Lake, Meteor Lake, Sapphire Rapids,
> Broadwell server, Granite Rapids, Icelake server, Sierra Forest, and Rome.
> /sys/devices/system/cpu/cpu*/cache is populated correctly.

Thanks!

-- 
Regards/Gruss,
    Boris.

https://people.kernel.org/tglx/notes-about-netiquette
[tip: x86/urgent] x86/cacheinfo: Delete global num_cache_leaves
Posted by tip-bot2 for Ricardo Neri 1 year, 2 months ago
The following commit has been merged into the x86/urgent branch of tip:

Commit-ID:     9677be09e5e4fbe48aeccb06ae3063c5eba331c3
Gitweb:        https://git.kernel.org/tip/9677be09e5e4fbe48aeccb06ae3063c5eba331c3
Author:        Ricardo Neri <ricardo.neri-calderon@linux.intel.com>
AuthorDate:    Wed, 27 Nov 2024 16:22:47 -08:00
Committer:     Borislav Petkov (AMD) <bp@alien8.de>
CommitterDate: Fri, 06 Dec 2024 13:13:36 +01:00

x86/cacheinfo: Delete global num_cache_leaves

Linux remembers cpu_cachinfo::num_leaves per CPU, but x86 initializes all
CPUs from the same global "num_cache_leaves".

This is erroneous on systems such as Meteor Lake, where each CPU has a
distinct num_leaves value. Delete the global "num_cache_leaves" and
initialize num_leaves on each CPU.

init_cache_level() no longer needs to set num_leaves. Also, it never had to
set num_levels as it is unnecessary in x86. Keep checking for zero cache
leaves. Such condition indicates a bug.

  [ bp: Cleanup. ]

Signed-off-by: Ricardo Neri <ricardo.neri-calderon@linux.intel.com>
Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
Cc: stable@vger.kernel.org # 6.3+
Link: https://lore.kernel.org/r/20241128002247.26726-3-ricardo.neri-calderon@linux.intel.com
---
 arch/x86/kernel/cpu/cacheinfo.c | 43 +++++++++++++++-----------------
 1 file changed, 21 insertions(+), 22 deletions(-)

diff --git a/arch/x86/kernel/cpu/cacheinfo.c b/arch/x86/kernel/cpu/cacheinfo.c
index 392d09c..e6fa03e 100644
--- a/arch/x86/kernel/cpu/cacheinfo.c
+++ b/arch/x86/kernel/cpu/cacheinfo.c
@@ -178,8 +178,6 @@ struct _cpuid4_info_regs {
 	struct amd_northbridge *nb;
 };
 
-static unsigned short num_cache_leaves;
-
 /* AMD doesn't have CPUID4. Emulate it here to report the same
    information to the user.  This makes some assumptions about the machine:
    L2 not shared, no SMT etc. that is currently true on AMD CPUs.
@@ -717,20 +715,23 @@ void cacheinfo_hygon_init_llc_id(struct cpuinfo_x86 *c)
 
 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)) {
-		num_cache_leaves = find_num_cache_leaves(c);
+		ci->num_leaves = find_num_cache_leaves(c);
 	} else if (c->extended_cpuid_level >= 0x80000006) {
 		if (cpuid_edx(0x80000006) & 0xf000)
-			num_cache_leaves = 4;
+			ci->num_leaves = 4;
 		else
-			num_cache_leaves = 3;
+			ci->num_leaves = 3;
 	}
 }
 
 void init_hygon_cacheinfo(struct cpuinfo_x86 *c)
 {
-	num_cache_leaves = find_num_cache_leaves(c);
+	struct cpu_cacheinfo *ci = get_cpu_cacheinfo(c->cpu_index);
+
+	ci->num_leaves = find_num_cache_leaves(c);
 }
 
 void init_intel_cacheinfo(struct cpuinfo_x86 *c)
@@ -740,21 +741,21 @@ void init_intel_cacheinfo(struct cpuinfo_x86 *c)
 	unsigned int new_l1d = 0, new_l1i = 0; /* Cache sizes from cpuid(4) */
 	unsigned int new_l2 = 0, new_l3 = 0, i; /* Cache sizes from cpuid(4) */
 	unsigned int l2_id = 0, l3_id = 0, num_threads_sharing, index_msb;
+	struct cpu_cacheinfo *ci = get_cpu_cacheinfo(c->cpu_index);
 
 	if (c->cpuid_level > 3) {
-		static int is_initialized;
-
-		if (is_initialized == 0) {
-			/* Init num_cache_leaves from boot CPU */
-			num_cache_leaves = find_num_cache_leaves(c);
-			is_initialized++;
-		}
+		/*
+		 * There should be at least one leaf. A non-zero value means
+		 * that the number of leaves has been initialized.
+		 */
+		if (!ci->num_leaves)
+			ci->num_leaves = find_num_cache_leaves(c);
 
 		/*
 		 * Whenever possible use cpuid(4), deterministic cache
 		 * parameters cpuid leaf to find the cache details
 		 */
-		for (i = 0; i < num_cache_leaves; i++) {
+		for (i = 0; i < ci->num_leaves; i++) {
 			struct _cpuid4_info_regs this_leaf = {};
 			int retval;
 
@@ -790,14 +791,14 @@ void init_intel_cacheinfo(struct cpuinfo_x86 *c)
 	 * Don't use cpuid2 if cpuid4 is supported. For P4, we use cpuid2 for
 	 * trace cache
 	 */
-	if ((num_cache_leaves == 0 || c->x86 == 15) && c->cpuid_level > 1) {
+	if ((!ci->num_leaves || c->x86 == 15) && c->cpuid_level > 1) {
 		/* supports eax=2  call */
 		int j, n;
 		unsigned int regs[4];
 		unsigned char *dp = (unsigned char *)regs;
 		int only_trace = 0;
 
-		if (num_cache_leaves != 0 && c->x86 == 15)
+		if (ci->num_leaves && c->x86 == 15)
 			only_trace = 1;
 
 		/* Number of times to iterate */
@@ -991,14 +992,12 @@ static void ci_leaf_init(struct cacheinfo *this_leaf,
 
 int init_cache_level(unsigned int cpu)
 {
-	struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+	struct cpu_cacheinfo *ci = get_cpu_cacheinfo(cpu);
 
-	if (!num_cache_leaves)
+	/* There should be at least one leaf. */
+	if (!ci->num_leaves)
 		return -ENOENT;
-	if (!this_cpu_ci)
-		return -EINVAL;
-	this_cpu_ci->num_levels = 3;
-	this_cpu_ci->num_leaves = num_cache_leaves;
+
 	return 0;
 }