All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] sparc: populate sysfs cache topology information on sun4v
@ 2026-10-18 22:27 Jan Engelhardt
  2026-10-19  0:16 ` Tony Rodriguez
  2026-10-19  9:49 ` Stian Halseth
  0 siblings, 2 replies; 15+ messages in thread
From: Jan Engelhardt @ 2026-10-18 22:27 UTC (permalink / raw)
  To: davem; +Cc: sparclinux, linux-kernel, John Paul Adrian Glaubitz,
	Stian Halseth

The SPARC port does not publish any cache topology information. The
lscpu utility therefore falls back to assuming the published cache
sizes are for unshared caches, and as a result, reports way more
cache than there really is present in the system. On a T5240 machine:

```
$ lscpu
Architecture:          sparc64
  CPU op-mode(s):      32-bit, 64-bit
  Byte Order:          Big Endian
CPU(s):                128
  On-line CPU(s) list: 0-127
Model name:            UltraSparc T2 (Niagara2)
  Thread(s) per core:  4
  Core(s) per socket:  16
  Socket(s):           2
  Flags:               sun4v
Caches (sum of all):
  L1d:                 1 MiB (128 instances)
  L1i:                 2 MiB (128 instances)
  L2:                  512 MiB (128 instances)
```

This patch implements population of the sysfs cacheinfo structures
for sun4v (systems with mdesc). The proper amount is now reflected in
lscpu's output:

```
Caches (sum of all):
  L1d:                 128 KiB (16 instances)
  L1i:                 256 KiB (16 instances)
  L2:                  8 MiB (2 instances)
```

Signed-off-by: Jan Engelhardt <jengelh@inai.de>
---
 arch/sparc/include/asm/cpudata_64.h |  8 +++-
 arch/sparc/kernel/mdesc.c           | 61 +++++++++++++++++++++++++++++
 2 files changed, 68 insertions(+), 1 deletion(-)

diff --git a/arch/sparc/include/asm/cpudata_64.h b/arch/sparc/include/asm/cpudata_64.h
index 056b3c0e7ef9..e28b841ffe23 100644
--- a/arch/sparc/include/asm/cpudata_64.h
+++ b/arch/sparc/include/asm/cpudata_64.h
@@ -18,13 +18,19 @@ typedef struct {
 	unsigned int	irq0_irqs;
 	unsigned int	__pad2;
 
-	/* Dcache line 2, rarely used */
+	/* Dcache line 2+3, rarely used */
 	unsigned int	dcache_size;
 	unsigned int	dcache_line_size;
+	unsigned int	dcache_assoc;
+	unsigned int	dcache_id;
 	unsigned int	icache_size;
 	unsigned int	icache_line_size;
+	unsigned int	icache_assoc;
+	unsigned int	icache_id;
 	unsigned int	ecache_size;
 	unsigned int	ecache_line_size;
+	unsigned int	ecache_assoc;
+	unsigned int	ecache_id;
 	unsigned short	sock_id;	/* physical package */
 	unsigned short	core_id;
 	unsigned short  max_cache_id;	/* groupings of highest shared cache */
diff --git a/arch/sparc/kernel/mdesc.c b/arch/sparc/kernel/mdesc.c
index 23b51a32e96e..029d9d812074 100644
--- a/arch/sparc/kernel/mdesc.c
+++ b/arch/sparc/kernel/mdesc.c
@@ -818,6 +818,7 @@ static void fill_in_one_cache(cpuinfo_sparc *c, struct mdesc_handle *hp, u64 mp)
 	const u64 *level = mdesc_get_property(hp, mp, "level", NULL);
 	const u64 *size = mdesc_get_property(hp, mp, "size", NULL);
 	const u64 *line_size = mdesc_get_property(hp, mp, "line-size", NULL);
+	const u64 *assoc = mdesc_get_property(hp, mp, "associativity", NULL);
 	const char *type;
 	int type_len;
 
@@ -828,15 +829,21 @@ static void fill_in_one_cache(cpuinfo_sparc *c, struct mdesc_handle *hp, u64 mp)
 		if (of_find_in_proplist(type, "instn", type_len)) {
 			c->icache_size = *size;
 			c->icache_line_size = *line_size;
+			c->icache_assoc = assoc ? *assoc : 0;
+			c->icache_id = mp;
 		} else if (of_find_in_proplist(type, "data", type_len)) {
 			c->dcache_size = *size;
 			c->dcache_line_size = *line_size;
+			c->dcache_assoc = assoc ? *assoc : 0;
+			c->dcache_id = mp;
 		}
 		break;
 
 	case 2:
 		c->ecache_size = *size;
 		c->ecache_line_size = *line_size;
+		c->ecache_assoc = assoc ? *assoc : 0;
+		c->ecache_id = mp;
 		break;
 
 	default:
@@ -1251,6 +1258,60 @@ void mdesc_fill_in_cpu_data(cpumask_t *mask)
 	smp_fill_in_sib_core_maps();
 }
 
+int init_cache_level(unsigned int cpu)
+{
+	struct cpu_cacheinfo *i = get_cpu_cacheinfo(cpu);
+	i->num_levels = 2;
+	i->num_leaves = 3;
+	return 0;
+}
+
+int populate_cache_leaves(unsigned int cpu)
+{
+	struct cacheinfo *leaf = get_cpu_cacheinfo(cpu)->info_list;
+	cpuinfo_sparc *cs = &cpu_data(cpu);
+
+	leaf->id                    = cs->icache_id;
+	leaf->attributes            = CACHE_ID;
+	leaf->level                 = 1;
+	leaf->type                  = CACHE_TYPE_INST;
+	leaf->coherency_line_size   = cs->icache_line_size;
+	leaf->ways_of_associativity = cs->icache_assoc;
+	leaf->size                  = cs->icache_size;
+	leaf->priv                  = cs;
+	if (cs->icache_line_size > 0 && cs->icache_assoc > 0)
+		leaf->number_of_sets = cs->icache_size / cs->icache_line_size /
+		                       cs->icache_assoc;
+	++leaf;
+
+	leaf->id                    = cs->dcache_id;
+	leaf->attributes            = CACHE_ID;
+	leaf->level                 = 1;
+	leaf->type                  = CACHE_TYPE_DATA;
+	leaf->coherency_line_size   = cs->dcache_line_size;
+	leaf->ways_of_associativity = cs->dcache_assoc;
+	leaf->size                  = cs->dcache_size;
+	leaf->priv                  = cs;
+	if (cs->dcache_line_size > 0 && cs->dcache_assoc > 0)
+		leaf->number_of_sets = cs->dcache_size / cs->dcache_line_size /
+		                       cs->dcache_assoc;
+	++leaf;
+
+	leaf->id                    = cs->ecache_id;
+	leaf->attributes            = CACHE_ID;
+	leaf->level                 = 2;
+	leaf->type                  = CACHE_TYPE_UNIFIED;
+	leaf->coherency_line_size   = cs->ecache_line_size;
+	leaf->ways_of_associativity = cs->ecache_assoc;
+	leaf->size                  = cs->ecache_size;
+	leaf->priv                  = cs;
+	if (cs->ecache_line_size > 0 && cs->ecache_assoc > 0)
+		leaf->number_of_sets = cs->ecache_size / cs->ecache_line_size /
+		                       cs->ecache_assoc;
+
+	return 0;
+}
+
 /* mdesc_open() - Grab a reference to mdesc_handle when /dev/mdesc is
  * opened. Hold this reference until /dev/mdesc is closed to ensure
  * mdesc data structure is not released underneath us. Store the
-- 
2.56.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Re: [PATCH] sparc: populate sysfs cache topology information on sun4v
  2026-10-18 22:27 Jan Engelhardt
@ 2026-10-19  0:16 ` Tony Rodriguez
  2026-10-19  0:37   ` Tony Rodriguez
  2026-10-19  9:49 ` Stian Halseth
  1 sibling, 1 reply; 15+ messages in thread
From: Tony Rodriguez @ 2026-10-19  0:16 UTC (permalink / raw)
  To: Jan Engelhardt, davem
  Cc: sparclinux, linux-kernel, John Paul Adrian Glaubitz,
	Stian Halseth

Hi Jan,

On 10/18/26 3:27 PM, Jan Engelhardt wrote:
> The SPARC port does not publish any cache topology information. The
> lscpu utility therefore falls back to assuming the published cache
> sizes are for unshared caches, and as a result, reports way more
> cache than there really is present in the system. On a T5240 machine:
>
> ```
> $ lscpu
> Architecture:          sparc64
>    CPU op-mode(s):      32-bit, 64-bit
>    Byte Order:          Big Endian
> CPU(s):                128
>    On-line CPU(s) list: 0-127
> Model name:            UltraSparc T2 (Niagara2)
>    Thread(s) per core:  4
>    Core(s) per socket:  16
>    Socket(s):           2
>    Flags:               sun4v
> Caches (sum of all):
>    L1d:                 1 MiB (128 instances)
>    L1i:                 2 MiB (128 instances)
>    L2:                  512 MiB (128 instances)
> ```
>
> This patch implements population of the sysfs cacheinfo structures
> for sun4v (systems with mdesc). The proper amount is now reflected in
> lscpu's output:
>
> ```
> Caches (sum of all):
>    L1d:                 128 KiB (16 instances)
>    L1i:                 256 KiB (16 instances)
>    L2:                  8 MiB (2 instances)
> ```
>
> Signed-off-by: Jan Engelhardt <jengelh@inai.de>
> ---
>   arch/sparc/include/asm/cpudata_64.h |  8 +++-
>   arch/sparc/kernel/mdesc.c           | 61 +++++++++++++++++++++++++++++
>   2 files changed, 68 insertions(+), 1 deletion(-)
>
> diff --git a/arch/sparc/include/asm/cpudata_64.h b/arch/sparc/include/asm/cpudata_64.h
> index 056b3c0e7ef9..e28b841ffe23 100644
> --- a/arch/sparc/include/asm/cpudata_64.h
> +++ b/arch/sparc/include/asm/cpudata_64.h
> @@ -18,13 +18,19 @@ typedef struct {
>   	unsigned int	irq0_irqs;
>   	unsigned int	__pad2;
>   
> -	/* Dcache line 2, rarely used */
> +	/* Dcache line 2+3, rarely used */
>   	unsigned int	dcache_size;
>   	unsigned int	dcache_line_size;
> +	unsigned int	dcache_assoc;
> +	unsigned int	dcache_id;
>   	unsigned int	icache_size;
>   	unsigned int	icache_line_size;
> +	unsigned int	icache_assoc;
> +	unsigned int	icache_id;
>   	unsigned int	ecache_size;
>   	unsigned int	ecache_line_size;
> +	unsigned int	ecache_assoc;
> +	unsigned int	ecache_id;
>   	unsigned short	sock_id;	/* physical package */
>   	unsigned short	core_id;
>   	unsigned short  max_cache_id;	/* groupings of highest shared cache */
> diff --git a/arch/sparc/kernel/mdesc.c b/arch/sparc/kernel/mdesc.c
> index 23b51a32e96e..029d9d812074 100644
> --- a/arch/sparc/kernel/mdesc.c
> +++ b/arch/sparc/kernel/mdesc.c
> @@ -818,6 +818,7 @@ static void fill_in_one_cache(cpuinfo_sparc *c, struct mdesc_handle *hp, u64 mp)
>   	const u64 *level = mdesc_get_property(hp, mp, "level", NULL);
>   	const u64 *size = mdesc_get_property(hp, mp, "size", NULL);
>   	const u64 *line_size = mdesc_get_property(hp, mp, "line-size", NULL);
> +	const u64 *assoc = mdesc_get_property(hp, mp, "associativity", NULL);
>   	const char *type;
>   	int type_len;
>   
> @@ -828,15 +829,21 @@ static void fill_in_one_cache(cpuinfo_sparc *c, struct mdesc_handle *hp, u64 mp)
>   		if (of_find_in_proplist(type, "instn", type_len)) {
>   			c->icache_size = *size;
>   			c->icache_line_size = *line_size;
> +			c->icache_assoc = assoc ? *assoc : 0;
> +			c->icache_id = mp;
>   		} else if (of_find_in_proplist(type, "data", type_len)) {
>   			c->dcache_size = *size;
>   			c->dcache_line_size = *line_size;
> +			c->dcache_assoc = assoc ? *assoc : 0;
> +			c->dcache_id = mp;
>   		}
>   		break;
>   
>   	case 2:
>   		c->ecache_size = *size;
>   		c->ecache_line_size = *line_size;
> +		c->ecache_assoc = assoc ? *assoc : 0;
> +		c->ecache_id = mp;
>   		break;
>   
>   	default:
> @@ -1251,6 +1258,60 @@ void mdesc_fill_in_cpu_data(cpumask_t *mask)
>   	smp_fill_in_sib_core_maps();
>   }
>   
> +int init_cache_level(unsigned int cpu)
> +{
> +	struct cpu_cacheinfo *i = get_cpu_cacheinfo(cpu);
> +	i->num_levels = 2;
> +	i->num_leaves = 3;
> +	return 0;
> +}
> +
> +int populate_cache_leaves(unsigned int cpu)
> +{
> +	struct cacheinfo *leaf = get_cpu_cacheinfo(cpu)->info_list;
> +	cpuinfo_sparc *cs = &cpu_data(cpu);
> +
> +	leaf->id                    = cs->icache_id;
> +	leaf->attributes            = CACHE_ID;
> +	leaf->level                 = 1;
> +	leaf->type                  = CACHE_TYPE_INST;
> +	leaf->coherency_line_size   = cs->icache_line_size;
> +	leaf->ways_of_associativity = cs->icache_assoc;
> +	leaf->size                  = cs->icache_size;
> +	leaf->priv                  = cs;
> +	if (cs->icache_line_size > 0 && cs->icache_assoc > 0)
> +		leaf->number_of_sets = cs->icache_size / cs->icache_line_size /
> +		                       cs->icache_assoc;
> +	++leaf;
> +
> +	leaf->id                    = cs->dcache_id;
> +	leaf->attributes            = CACHE_ID;
> +	leaf->level                 = 1;
> +	leaf->type                  = CACHE_TYPE_DATA;
> +	leaf->coherency_line_size   = cs->dcache_line_size;
> +	leaf->ways_of_associativity = cs->dcache_assoc;
> +	leaf->size                  = cs->dcache_size;
> +	leaf->priv                  = cs;
> +	if (cs->dcache_line_size > 0 && cs->dcache_assoc > 0)
> +		leaf->number_of_sets = cs->dcache_size / cs->dcache_line_size /
> +		                       cs->dcache_assoc;
> +	++leaf;
> +
> +	leaf->id                    = cs->ecache_id;
> +	leaf->attributes            = CACHE_ID;
> +	leaf->level                 = 2;
> +	leaf->type                  = CACHE_TYPE_UNIFIED;
> +	leaf->coherency_line_size   = cs->ecache_line_size;
> +	leaf->ways_of_associativity = cs->ecache_assoc;
> +	leaf->size                  = cs->ecache_size;
> +	leaf->priv                  = cs;
> +	if (cs->ecache_line_size > 0 && cs->ecache_assoc > 0)
> +		leaf->number_of_sets = cs->ecache_size / cs->ecache_line_size /
> +		                       cs->ecache_assoc;
> +
> +	return 0;
> +}
> +
>   /* mdesc_open() - Grab a reference to mdesc_handle when /dev/mdesc is
>    * opened. Hold this reference until /dev/mdesc is closed to ensure
>    * mdesc data structure is not released underneath us. Store the

Thanks for working on this — publishing proper SPARC cache topology is 
long overdue, and reading associativity from MDESC plus carrying cache 
IDs through cpuinfo_sparc is a sensible direction.


*Full disclosure: I ran this patch through "DeepSeek AI" out of 
curiosity to sanity-check it and I am interested in learning more.  Deep 
Seek AI flagged a few issues that seem worth mentioning:  You may want 
to double check the "Deep Seek AI" review of this patch.

Main blockers

1)    leaf->attributes = CACHE_ID is invalid. attributes is a bitmask of 
CACHE_WRITE_BACK / CACHE_WRITE_THROUGH / etc.; CACHE_ID isn’t a valid 
attribute. It should be 0 or real attribute bits from MDESC.

  2)   Shared-cache reporting is not actually wired up, so T2-class 
machines still show the wrong cache sizes. The patch sets leaf->id = mp 
and leaf->priv = cs, but never fills leaf->shared_cpu_map, and the 
generic detect_cache_attributes() -> cache_shared_cpu_map_setup() path 
doesn’t appear to be invoked on SPARC. As a result, the generic 
cacheinfo code still treats every leaf as private to one CPU, and lscpu 
still multiplies shared cache size by the number of threads — the exact 
bug being reported.

Concretely: with this patch applied, a T5240 would still report L2: 512 
MiB (128 instances) rather than the correct L2: 8 MiB (2 instances) (4 
MB per socket, shared across 64 threads, 2 sockets). A T5120 would still 
report the 4 MB shared L2 as 4 MB × 64 threads = 256 MiB in 64 instances 
, instead of 4 MiB (1 instance). The patch does not change the outcome 
on T2-class hardware because the leaves are still treated as private to 
each CPU.

3)    MDESC level handling is incomplete and mislabels caches on S7/T7. 
fill_in_one_cache() only handles case 1: (L1I/L1D) and case 2: (ecache); 
everything else falls through to default:. On T2/T5240 that happens to 
work because the shared 4 MB is reported at level 2. But on S7-2/T7-1, 
MDESC reports L2 at level 2 and the shared L3 at level 3 — so the L2 
gets stored as ecache_*, and the real L3 is silently ignored. 
cpuinfo_sparc also has no separate l2_* fields, so the data model can’t 
hold both levels.

4) Hardcoded num_levels = 2 / num_leaves = 3 and ecache labeled level 2 
are wrong across the family. T2/T5240: L1I + L1D + shared L2 (no L3). 
S7-2: L1 + shared L2 + shared L3. T7-1: L1 + private L2 + shared L3. The 
leaf count, levels, and types need to be derived from MDESC, not hardcoded.

5)    The new init_cache_level() / populate_cache_leaves() don’t appear 
to be wired into the generic cacheinfo path , so they may never actually 
be called.

Additional issues

6)    leaf->id is int, but the patch stores a truncated u64 MDESC 
handle. ecache_id/ icache_id/ dcache_id are unsigned int, assigned from 
u64 mp. Beyond truncation, assigning u32 → int id can produce negative 
IDs. An index-based or hashed ID would be cleaner.

7)   Growing cpuinfo_sparc needs an asm-offset audit. The struct has a 
“must be 1st” field and SPARC asm accesses cpu_data at fixed offsets in 
places like rtrap_64.S. The patch adds fields after that, so offset 0 is 
preserved, but it should be confirmed that no asm hardcodes offsets into 
the fields that now shift ( sock_id, core_id, max_cache_id, cache 
fields). It also grows the per-CPU area past the “Dcache line 2” region.

8)    The data model has no separate L2 storage for S7/T7. cpuinfo_sparc 
only has ecache_* for the shared cache. To model S7 (shared L2 per 
cluster + shared L3 per socket) and T7 (private L2 per core + shared 
L3), you need distinct l2_* and l3/ ecache_* fields.

9)    The new *_id scheme duplicates existing infrastructure. 
cpuinfo_sparc already has max_cache_id (“groupings of highest shared 
cache”) and smp_fill_in_sib_core_maps(). It’s worth checking whether 
that mechanism should be extended rather than adding six new fields.

10)    num_leaves may not exist in the target kernel. struct 
cpu_cacheinfo gained num_leaves relatively late (~4.16). If the tree is 
older, init_cache_level() won’t compile or won’t allocate the right 
number of leaves.

11)    Missing validation / NULL checks. fill_in_one_cache() 
dereferences *size and *line_size without NULL checks (pre-existing, but 
adjacent to the new code). assoc is checked, which is good, but there’s 
no power-of-two validation, and when assoc == 0 the leaf still publishes 
a size with number_of_sets = 0, which lscpu may render oddly.

12)    “ecache” naming is platform-dependent. Historically “ecache” 
meant L2 on older SPARC; on S7/T7 the external/shared cache is L3. Using 
ecache_* for both with a hardcoded level 2 is semantically confusing; 
the level should come from MDESC.

13)    Style / linkage. The new functions should probably be static; 
there’s mixed indentation versus the file’s tabs; and a checkpatch.pl 
--strict run would be worthwhile.

14)    Error paths always return 0. init_cache_level() and 
populate_cache_leaves() publish leaves even if all MDESC cache data is 
zero/absent, yielding zero-size cache leaves rather than “unknown.”

15)    leaf->priv = cs is probably harmless, but worth confirming 
nothing in the generic path or arch code expects a particular priv type.

A topology-driven fix — dynamic leaves from MDESC, correct levels, 
separate L2/L3 storage, and a shared_cpu_map keyed on sock_id/cluster — 
would make T2/T5240 report 8 MiB L2 in 2 instances and S7-2/T7-1 report 
their shared L3 correctly instead of 64× inflated.

One clarification on the T5240 lscpu output, since it can look partly 
correct: the L1d: 1 MiB (128 instances) and L1i: 2 MiB (128 instances) 
lines are actually right — L1 really is private per core on T2, so 128 
instances is correct. The L2 line is the giveaway: it reports 512 MiB 
(128 instances), i.e. 128 × 4 MB, when a dual-socket T5240 has only 2 
shared 4 MB L2 caches (one per socket), for 8 MiB total in 2 instances . 
The default “every leaf is private” assumption happens to be true for L1 
and false for L2, which is why only the L2 line is inflated. This patch, 
as written, would leave that L2 line unchanged.

In short: on T2-class hardware (T5120/T5240) this patch produces the 
same incorrect lscpu output as unpatched mainline — it does not resolve 
the reported over-reporting there either.

Regards,

Tony


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] sparc: populate sysfs cache topology information on sun4v
  2026-10-19  0:16 ` Tony Rodriguez
@ 2026-10-19  0:37   ` Tony Rodriguez
  2026-10-19  2:32     ` Tony Rodriguez
       [not found]     ` <c7d7a96c-feb9-4676-8cc2-73529e8324ef@gmail.com>
  0 siblings, 2 replies; 15+ messages in thread
From: Tony Rodriguez @ 2026-10-19  0:37 UTC (permalink / raw)
  To: Jan Engelhardt, davem
  Cc: sparclinux, linux-kernel, John Paul Adrian Glaubitz,
	Stian Halseth

Hi Jan,

On 10/18/26 5:16 PM, Tony Rodriguez wrote:
> Hi Jan,
>
> On 10/18/26 3:27 PM, Jan Engelhardt wrote:
>> The SPARC port does not publish any cache topology information. The
>> lscpu utility therefore falls back to assuming the published cache
>> sizes are for unshared caches, and as a result, reports way more
>> cache than there really is present in the system. On a T5240 machine:
>>
>> ```
>> $ lscpu
>> Architecture:          sparc64
>>    CPU op-mode(s):      32-bit, 64-bit
>>    Byte Order:          Big Endian
>> CPU(s):                128
>>    On-line CPU(s) list: 0-127
>> Model name:            UltraSparc T2 (Niagara2)
>>    Thread(s) per core:  4
>>    Core(s) per socket:  16
>>    Socket(s):           2
>>    Flags:               sun4v
>> Caches (sum of all):
>>    L1d:                 1 MiB (128 instances)
>>    L1i:                 2 MiB (128 instances)
>>    L2:                  512 MiB (128 instances)
>> ```
>>
>> This patch implements population of the sysfs cacheinfo structures
>> for sun4v (systems with mdesc). The proper amount is now reflected in
>> lscpu's output:
>>
>> ```
>> Caches (sum of all):
>>    L1d:                 128 KiB (16 instances)
>>    L1i:                 256 KiB (16 instances)
>>    L2:                  8 MiB (2 instances)
>> ```
>>
>> Signed-off-by: Jan Engelhardt <jengelh@inai.de>
>> ---
>>   arch/sparc/include/asm/cpudata_64.h |  8 +++-
>>   arch/sparc/kernel/mdesc.c           | 61 +++++++++++++++++++++++++++++
>>   2 files changed, 68 insertions(+), 1 deletion(-)
>>
>> diff --git a/arch/sparc/include/asm/cpudata_64.h 
>> b/arch/sparc/include/asm/cpudata_64.h
>> index 056b3c0e7ef9..e28b841ffe23 100644
>> --- a/arch/sparc/include/asm/cpudata_64.h
>> +++ b/arch/sparc/include/asm/cpudata_64.h
>> @@ -18,13 +18,19 @@ typedef struct {
>>       unsigned int    irq0_irqs;
>>       unsigned int    __pad2;
>>   -    /* Dcache line 2, rarely used */
>> +    /* Dcache line 2+3, rarely used */
>>       unsigned int    dcache_size;
>>       unsigned int    dcache_line_size;
>> +    unsigned int    dcache_assoc;
>> +    unsigned int    dcache_id;
>>       unsigned int    icache_size;
>>       unsigned int    icache_line_size;
>> +    unsigned int    icache_assoc;
>> +    unsigned int    icache_id;
>>       unsigned int    ecache_size;
>>       unsigned int    ecache_line_size;
>> +    unsigned int    ecache_assoc;
>> +    unsigned int    ecache_id;
>>       unsigned short    sock_id;    /* physical package */
>>       unsigned short    core_id;
>>       unsigned short  max_cache_id;    /* groupings of highest shared 
>> cache */
>> diff --git a/arch/sparc/kernel/mdesc.c b/arch/sparc/kernel/mdesc.c
>> index 23b51a32e96e..029d9d812074 100644
>> --- a/arch/sparc/kernel/mdesc.c
>> +++ b/arch/sparc/kernel/mdesc.c
>> @@ -818,6 +818,7 @@ static void fill_in_one_cache(cpuinfo_sparc *c, 
>> struct mdesc_handle *hp, u64 mp)
>>       const u64 *level = mdesc_get_property(hp, mp, "level", NULL);
>>       const u64 *size = mdesc_get_property(hp, mp, "size", NULL);
>>       const u64 *line_size = mdesc_get_property(hp, mp, "line-size", 
>> NULL);
>> +    const u64 *assoc = mdesc_get_property(hp, mp, "associativity", 
>> NULL);
>>       const char *type;
>>       int type_len;
>>   @@ -828,15 +829,21 @@ static void fill_in_one_cache(cpuinfo_sparc 
>> *c, struct mdesc_handle *hp, u64 mp)
>>           if (of_find_in_proplist(type, "instn", type_len)) {
>>               c->icache_size = *size;
>>               c->icache_line_size = *line_size;
>> +            c->icache_assoc = assoc ? *assoc : 0;
>> +            c->icache_id = mp;
>>           } else if (of_find_in_proplist(type, "data", type_len)) {
>>               c->dcache_size = *size;
>>               c->dcache_line_size = *line_size;
>> +            c->dcache_assoc = assoc ? *assoc : 0;
>> +            c->dcache_id = mp;
>>           }
>>           break;
>>         case 2:
>>           c->ecache_size = *size;
>>           c->ecache_line_size = *line_size;
>> +        c->ecache_assoc = assoc ? *assoc : 0;
>> +        c->ecache_id = mp;
>>           break;
>>         default:
>> @@ -1251,6 +1258,60 @@ void mdesc_fill_in_cpu_data(cpumask_t *mask)
>>       smp_fill_in_sib_core_maps();
>>   }
>>   +int init_cache_level(unsigned int cpu)
>> +{
>> +    struct cpu_cacheinfo *i = get_cpu_cacheinfo(cpu);
>> +    i->num_levels = 2;
>> +    i->num_leaves = 3;
>> +    return 0;
>> +}
>> +
>> +int populate_cache_leaves(unsigned int cpu)
>> +{
>> +    struct cacheinfo *leaf = get_cpu_cacheinfo(cpu)->info_list;
>> +    cpuinfo_sparc *cs = &cpu_data(cpu);
>> +
>> +    leaf->id                    = cs->icache_id;
>> +    leaf->attributes            = CACHE_ID;
>> +    leaf->level                 = 1;
>> +    leaf->type                  = CACHE_TYPE_INST;
>> +    leaf->coherency_line_size   = cs->icache_line_size;
>> +    leaf->ways_of_associativity = cs->icache_assoc;
>> +    leaf->size                  = cs->icache_size;
>> +    leaf->priv                  = cs;
>> +    if (cs->icache_line_size > 0 && cs->icache_assoc > 0)
>> +        leaf->number_of_sets = cs->icache_size / cs->icache_line_size /
>> +                               cs->icache_assoc;
>> +    ++leaf;
>> +
>> +    leaf->id                    = cs->dcache_id;
>> +    leaf->attributes            = CACHE_ID;
>> +    leaf->level                 = 1;
>> +    leaf->type                  = CACHE_TYPE_DATA;
>> +    leaf->coherency_line_size   = cs->dcache_line_size;
>> +    leaf->ways_of_associativity = cs->dcache_assoc;
>> +    leaf->size                  = cs->dcache_size;
>> +    leaf->priv                  = cs;
>> +    if (cs->dcache_line_size > 0 && cs->dcache_assoc > 0)
>> +        leaf->number_of_sets = cs->dcache_size / cs->dcache_line_size /
>> +                               cs->dcache_assoc;
>> +    ++leaf;
>> +
>> +    leaf->id                    = cs->ecache_id;
>> +    leaf->attributes            = CACHE_ID;
>> +    leaf->level                 = 2;
>> +    leaf->type                  = CACHE_TYPE_UNIFIED;
>> +    leaf->coherency_line_size   = cs->ecache_line_size;
>> +    leaf->ways_of_associativity = cs->ecache_assoc;
>> +    leaf->size                  = cs->ecache_size;
>> +    leaf->priv                  = cs;
>> +    if (cs->ecache_line_size > 0 && cs->ecache_assoc > 0)
>> +        leaf->number_of_sets = cs->ecache_size / cs->ecache_line_size /
>> +                               cs->ecache_assoc;
>> +
>> +    return 0;
>> +}
>> +
>>   /* mdesc_open() - Grab a reference to mdesc_handle when /dev/mdesc is
>>    * opened. Hold this reference until /dev/mdesc is closed to ensure
>>    * mdesc data structure is not released underneath us. Store the

I need to correct my previous reply — parts of it were wrong, and I'd 
rather retract them clearly than leave them in the thread.


The claim that the patch doesn't fix the L2 over-reporting was 
incorrect. I misread the "before" lscpu output in the message.

     L1d: 128 KiB (16 instances)
     L1i: 256 KiB (16 instances)
     L2:  8 MiB (2 instances)


What may still be worth a look:

These are the points from my earlier list that don't depend on the 
behavior claims I got wrong — but please treat them as questions and not 
assertions.

leaf->id is int while the MDESC handle is u64; the *_id fields are 
unsigned int, so there's truncation (harmless if only equality matters, 
but worth a cast or a comment).

Growing cpuinfo_sparc shifts sock_id / core_id / max_cache_id — worth 
confirming no SPARC asm hardcodes offsets into those.

fill_in_one_cache() handles case 1: and case 2: only; if S7/T7 report L2 
and L3 at different MDESC levels, it'd be worth confirming both are 
captured (the "after" output only covers T2, so this is untested 
territory rather than a known bug).

num_leaves in struct cpu_cacheinfo is version-dependent; worth 
confirming the target tree has it.


Apologies for the noise — I should have read the "before/after" pair 
more carefully before replying. The patch does what it says for T2, and 
my earlier message should be disregarded where it says otherwise.


Regards,

Tony


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] sparc: populate sysfs cache topology information on sun4v
  2026-10-19  0:37   ` Tony Rodriguez
@ 2026-10-19  2:32     ` Tony Rodriguez
       [not found]     ` <c7d7a96c-feb9-4676-8cc2-73529e8324ef@gmail.com>
  1 sibling, 0 replies; 15+ messages in thread
From: Tony Rodriguez @ 2026-10-19  2:32 UTC (permalink / raw)
  To: Jan Engelhardt, davem
  Cc: sparclinux, linux-kernel, John Paul Adrian Glaubitz,
	Stian Halseth

Hi Jan,

>>
>> On 10/18/26 3:27 PM, Jan Engelhardt wrote:
>>> The SPARC port does not publish any cache topology information. The
>>> lscpu utility therefore falls back to assuming the published cache
>>> sizes are for unshared caches, and as a result, reports way more
>>> cache than there really is present in the system. On a T5240 machine:
>>>
>>> ```
>>> $ lscpu
>>> Architecture:          sparc64
>>>    CPU op-mode(s):      32-bit, 64-bit
>>>    Byte Order:          Big Endian
>>> CPU(s):                128
>>>    On-line CPU(s) list: 0-127
>>> Model name:            UltraSparc T2 (Niagara2)
>>>    Thread(s) per core:  4
>>>    Core(s) per socket:  16
>>>    Socket(s):           2
>>>    Flags:               sun4v
>>> Caches (sum of all):
>>>    L1d:                 1 MiB (128 instances)
>>>    L1i:                 2 MiB (128 instances)
>>>    L2:                  512 MiB (128 instances)
>>> ```
>>>
>>> This patch implements population of the sysfs cacheinfo structures
>>> for sun4v (systems with mdesc). The proper amount is now reflected in
>>> lscpu's output:
>>>
>>> ```
>>> Caches (sum of all):
>>>    L1d:                 128 KiB (16 instances)
>>>    L1i:                 256 KiB (16 instances)
>>>    L2:                  8 MiB (2 instances)
>>> ```
>>>
>>> Signed-off-by: Jan Engelhardt <jengelh@inai.de>
>>> ---
>>>   arch/sparc/include/asm/cpudata_64.h |  8 +++-
>>>   arch/sparc/kernel/mdesc.c           | 61 
>>> +++++++++++++++++++++++++++++
>>>   2 files changed, 68 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/arch/sparc/include/asm/cpudata_64.h 
>>> b/arch/sparc/include/asm/cpudata_64.h
>>> index 056b3c0e7ef9..e28b841ffe23 100644
>>> --- a/arch/sparc/include/asm/cpudata_64.h
>>> +++ b/arch/sparc/include/asm/cpudata_64.h
>>> @@ -18,13 +18,19 @@ typedef struct {
>>>       unsigned int    irq0_irqs;
>>>       unsigned int    __pad2;
>>>   -    /* Dcache line 2, rarely used */
>>> +    /* Dcache line 2+3, rarely used */
>>>       unsigned int    dcache_size;
>>>       unsigned int    dcache_line_size;
>>> +    unsigned int    dcache_assoc;
>>> +    unsigned int    dcache_id;
>>>       unsigned int    icache_size;
>>>       unsigned int    icache_line_size;
>>> +    unsigned int    icache_assoc;
>>> +    unsigned int    icache_id;
>>>       unsigned int    ecache_size;
>>>       unsigned int    ecache_line_size;
>>> +    unsigned int    ecache_assoc;
>>> +    unsigned int    ecache_id;
>>>       unsigned short    sock_id;    /* physical package */
>>>       unsigned short    core_id;
>>>       unsigned short  max_cache_id;    /* groupings of highest 
>>> shared cache */
>>> diff --git a/arch/sparc/kernel/mdesc.c b/arch/sparc/kernel/mdesc.c
>>> index 23b51a32e96e..029d9d812074 100644
>>> --- a/arch/sparc/kernel/mdesc.c
>>> +++ b/arch/sparc/kernel/mdesc.c
>>> @@ -818,6 +818,7 @@ static void fill_in_one_cache(cpuinfo_sparc *c, 
>>> struct mdesc_handle *hp, u64 mp)
>>>       const u64 *level = mdesc_get_property(hp, mp, "level", NULL);
>>>       const u64 *size = mdesc_get_property(hp, mp, "size", NULL);
>>>       const u64 *line_size = mdesc_get_property(hp, mp, "line-size", 
>>> NULL);
>>> +    const u64 *assoc = mdesc_get_property(hp, mp, "associativity", 
>>> NULL);
>>>       const char *type;
>>>       int type_len;
>>>   @@ -828,15 +829,21 @@ static void fill_in_one_cache(cpuinfo_sparc 
>>> *c, struct mdesc_handle *hp, u64 mp)
>>>           if (of_find_in_proplist(type, "instn", type_len)) {
>>>               c->icache_size = *size;
>>>               c->icache_line_size = *line_size;
>>> +            c->icache_assoc = assoc ? *assoc : 0;
>>> +            c->icache_id = mp;
>>>           } else if (of_find_in_proplist(type, "data", type_len)) {
>>>               c->dcache_size = *size;
>>>               c->dcache_line_size = *line_size;
>>> +            c->dcache_assoc = assoc ? *assoc : 0;
>>> +            c->dcache_id = mp;
>>>           }
>>>           break;
>>>         case 2:
>>>           c->ecache_size = *size;
>>>           c->ecache_line_size = *line_size;
>>> +        c->ecache_assoc = assoc ? *assoc : 0;
>>> +        c->ecache_id = mp;
>>>           break;
>>>         default:
>>> @@ -1251,6 +1258,60 @@ void mdesc_fill_in_cpu_data(cpumask_t *mask)
>>>       smp_fill_in_sib_core_maps();
>>>   }
>>>   +int init_cache_level(unsigned int cpu)
>>> +{
>>> +    struct cpu_cacheinfo *i = get_cpu_cacheinfo(cpu);
>>> +    i->num_levels = 2;
>>> +    i->num_leaves = 3;
>>> +    return 0;
>>> +}
>>> +
>>> +int populate_cache_leaves(unsigned int cpu)
>>> +{
>>> +    struct cacheinfo *leaf = get_cpu_cacheinfo(cpu)->info_list;
>>> +    cpuinfo_sparc *cs = &cpu_data(cpu);
>>> +
>>> +    leaf->id                    = cs->icache_id;
>>> +    leaf->attributes            = CACHE_ID;
>>> +    leaf->level                 = 1;
>>> +    leaf->type                  = CACHE_TYPE_INST;
>>> +    leaf->coherency_line_size   = cs->icache_line_size;
>>> +    leaf->ways_of_associativity = cs->icache_assoc;
>>> +    leaf->size                  = cs->icache_size;
>>> +    leaf->priv                  = cs;
>>> +    if (cs->icache_line_size > 0 && cs->icache_assoc > 0)
>>> +        leaf->number_of_sets = cs->icache_size / 
>>> cs->icache_line_size /
>>> +                               cs->icache_assoc;
>>> +    ++leaf;
>>> +
>>> +    leaf->id                    = cs->dcache_id;
>>> +    leaf->attributes            = CACHE_ID;
>>> +    leaf->level                 = 1;
>>> +    leaf->type                  = CACHE_TYPE_DATA;
>>> +    leaf->coherency_line_size   = cs->dcache_line_size;
>>> +    leaf->ways_of_associativity = cs->dcache_assoc;
>>> +    leaf->size                  = cs->dcache_size;
>>> +    leaf->priv                  = cs;
>>> +    if (cs->dcache_line_size > 0 && cs->dcache_assoc > 0)
>>> +        leaf->number_of_sets = cs->dcache_size / 
>>> cs->dcache_line_size /
>>> +                               cs->dcache_assoc;
>>> +    ++leaf;
>>> +
>>> +    leaf->id                    = cs->ecache_id;
>>> +    leaf->attributes            = CACHE_ID;
>>> +    leaf->level                 = 2;
>>> +    leaf->type                  = CACHE_TYPE_UNIFIED;
>>> +    leaf->coherency_line_size   = cs->ecache_line_size;
>>> +    leaf->ways_of_associativity = cs->ecache_assoc;
>>> +    leaf->size                  = cs->ecache_size;
>>> +    leaf->priv                  = cs;
>>> +    if (cs->ecache_line_size > 0 && cs->ecache_assoc > 0)
>>> +        leaf->number_of_sets = cs->ecache_size / 
>>> cs->ecache_line_size /
>>> +                               cs->ecache_assoc;
>>> +
>>> +    return 0;
>>> +}
>>> +
>>>   /* mdesc_open() - Grab a reference to mdesc_handle when /dev/mdesc is
>>>    * opened. Hold this reference until /dev/mdesc is closed to ensure
>>>    * mdesc data structure is not released underneath us. Store the
>
I’ve applied the patch to a dual‑socket SPARC S7‑2 (16 cores total, 128 
threads) and wanted to share the resulting lscpu output, since it 
clarifies what the patch fixes and what still needs work for 3‑level parts.

Before the patch:

L1d: 2 MiB (128 instances)
L1i: 2 MiB (128 instances)
L2:  32 MiB (128 instances)

After the patch:

L1d: 256 KiB (16 instances)
L1i: 256 KiB (16 instances)
L2:  2 MiB (8 instances)

A) The L1 reporting is now correct. Each of the 16 cores has a private 
16 KB L1I and 16 KB L1D, so 256 KiB in 16 instances is exactly right. 
The per‑thread inflation is gone for L1, and the sharing/instance counts 
are sensible. The generic leaf->id grouping is clearly doing its job.

* B)  L2 is only half‑reported on S7:  This may be true for S7, T7, and 
T8 CPUs.

For the L2 split (On S7):

     L2D: 256 KB shared by every 2 cores -> 8 instances × 256 KB = 2 
MiB  (matches the output)

     L2I: 256 KB shared by every 4 cores ->4 instances × 256 KB = 1 MiB  
(missing)

So the total L2 on this box should  be 3 MiB across 12 instances , or 
shown as separate L2D/L2I lines. It looks like MDESC reports the L2I and 
L2D nodes separately, and the current case 2: overwrites one with the 
other in ecache_*. Supporting S7 properly would need either separate 
storage for L2I/L2D or a way to handle multiple level‑2 nodes.

* C) Need "level 3" cache details for (useful for T4, T5, S7, T7, T8).

As expected, the S7’s 16 MB per‑socket L3 (32 MiB total, 2 instances on 
this box) does not appear at all. init_cache_level() hardcodes 
num_levels = 2 / num_leaves = 3, and fill_in_one_cache() has no case 3:, 
so the MDESC level‑3 node is dropped in default.

Adding an L3 leaf may need?

     l3_* fields in cpuinfo_sparc,

     a case 3: in fill_in_one_cache(),

     num_levels = 3 / num_leaves = 4 in init_cache_level(),

     a 4th leaf in populate_cache_leaves().

For T7‑1/T8‑1 this would surface  their 64 MiB per‑socket L3; for S7‑2 
it would show 32 MiB in 2 instances (16 MiB per cpu). The T5240 result 
looks correct because that machine is 2‑level only, so the missing case 
3: doesn’t show up there.

Summary:

The patch is an improvement but needs more work.  It fixes the L1 (and 
L2D) inflation on S7‑2. To make it complete for the 3‑level SPARC parts, 
it needs improved L2I/L2D handling and an L3 leaf. It would be great to 
have L3 cache details shown via lscpu as well.


Tested-By: Tony Rodriguez <unixpro1970@gmail.com>


Regards,

Tony




^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] sparc: populate sysfs cache topology information on sun4v
       [not found]     ` <c7d7a96c-feb9-4676-8cc2-73529e8324ef@gmail.com>
@ 2026-10-19  2:42       ` Tony Rodriguez
  0 siblings, 0 replies; 15+ messages in thread
From: Tony Rodriguez @ 2026-10-19  2:42 UTC (permalink / raw)
  To: Jan Engelhardt, davem
  Cc: sparclinux, linux-kernel, John Paul Adrian Glaubitz,
	Stian Halseth

Hi Jan,

On 10/18/26 7:29 PM, Tony Rodriguez wrote:
>
> Hi Jan,
>
>>>
>>> On 10/18/26 3:27 PM, Jan Engelhardt wrote:
>>>> The SPARC port does not publish any cache topology information. The
>>>> lscpu utility therefore falls back to assuming the published cache
>>>> sizes are for unshared caches, and as a result, reports way more
>>>> cache than there really is present in the system. On a T5240 machine:
>>>>
>>>> ```
>>>> $ lscpu
>>>> Architecture:          sparc64
>>>>    CPU op-mode(s):      32-bit, 64-bit
>>>>    Byte Order:          Big Endian
>>>> CPU(s):                128
>>>>    On-line CPU(s) list: 0-127
>>>> Model name:            UltraSparc T2 (Niagara2)
>>>>    Thread(s) per core:  4
>>>>    Core(s) per socket:  16
>>>>    Socket(s):           2
>>>>    Flags:               sun4v
>>>> Caches (sum of all):
>>>>    L1d:                 1 MiB (128 instances)
>>>>    L1i:                 2 MiB (128 instances)
>>>>    L2:                  512 MiB (128 instances)
>>>> ```
>>>>
>>>> This patch implements population of the sysfs cacheinfo structures
>>>> for sun4v (systems with mdesc). The proper amount is now reflected in
>>>> lscpu's output:
>>>>
>>>> ```
>>>> Caches (sum of all):
>>>>    L1d:                 128 KiB (16 instances)
>>>>    L1i:                 256 KiB (16 instances)
>>>>    L2:                  8 MiB (2 instances)
>>>> ```
>>>>
>>>> Signed-off-by: Jan Engelhardt <jengelh@inai.de>
>>>> ---
>>>>   arch/sparc/include/asm/cpudata_64.h |  8 +++-
>>>>   arch/sparc/kernel/mdesc.c           | 61 
>>>> +++++++++++++++++++++++++++++
>>>>   2 files changed, 68 insertions(+), 1 deletion(-)
>>>>
>>>> diff --git a/arch/sparc/include/asm/cpudata_64.h 
>>>> b/arch/sparc/include/asm/cpudata_64.h
>>>> index 056b3c0e7ef9..e28b841ffe23 100644
>>>> --- a/arch/sparc/include/asm/cpudata_64.h
>>>> +++ b/arch/sparc/include/asm/cpudata_64.h
>>>> @@ -18,13 +18,19 @@ typedef struct {
>>>>       unsigned int    irq0_irqs;
>>>>       unsigned int    __pad2;
>>>>   -    /* Dcache line 2, rarely used */
>>>> +    /* Dcache line 2+3, rarely used */
>>>>       unsigned int    dcache_size;
>>>>       unsigned int    dcache_line_size;
>>>> +    unsigned int    dcache_assoc;
>>>> +    unsigned int    dcache_id;
>>>>       unsigned int    icache_size;
>>>>       unsigned int    icache_line_size;
>>>> +    unsigned int    icache_assoc;
>>>> +    unsigned int    icache_id;
>>>>       unsigned int    ecache_size;
>>>>       unsigned int    ecache_line_size;
>>>> +    unsigned int    ecache_assoc;
>>>> +    unsigned int    ecache_id;
>>>>       unsigned short    sock_id;    /* physical package */
>>>>       unsigned short    core_id;
>>>>       unsigned short  max_cache_id;    /* groupings of highest 
>>>> shared cache */
>>>> diff --git a/arch/sparc/kernel/mdesc.c b/arch/sparc/kernel/mdesc.c
>>>> index 23b51a32e96e..029d9d812074 100644
>>>> --- a/arch/sparc/kernel/mdesc.c
>>>> +++ b/arch/sparc/kernel/mdesc.c
>>>> @@ -818,6 +818,7 @@ static void fill_in_one_cache(cpuinfo_sparc *c, 
>>>> struct mdesc_handle *hp, u64 mp)
>>>>       const u64 *level = mdesc_get_property(hp, mp, "level", NULL);
>>>>       const u64 *size = mdesc_get_property(hp, mp, "size", NULL);
>>>>       const u64 *line_size = mdesc_get_property(hp, mp, 
>>>> "line-size", NULL);
>>>> +    const u64 *assoc = mdesc_get_property(hp, mp, "associativity", 
>>>> NULL);
>>>>       const char *type;
>>>>       int type_len;
>>>>   @@ -828,15 +829,21 @@ static void fill_in_one_cache(cpuinfo_sparc 
>>>> *c, struct mdesc_handle *hp, u64 mp)
>>>>           if (of_find_in_proplist(type, "instn", type_len)) {
>>>>               c->icache_size = *size;
>>>>               c->icache_line_size = *line_size;
>>>> +            c->icache_assoc = assoc ? *assoc : 0;
>>>> +            c->icache_id = mp;
>>>>           } else if (of_find_in_proplist(type, "data", type_len)) {
>>>>               c->dcache_size = *size;
>>>>               c->dcache_line_size = *line_size;
>>>> +            c->dcache_assoc = assoc ? *assoc : 0;
>>>> +            c->dcache_id = mp;
>>>>           }
>>>>           break;
>>>>         case 2:
>>>>           c->ecache_size = *size;
>>>>           c->ecache_line_size = *line_size;
>>>> +        c->ecache_assoc = assoc ? *assoc : 0;
>>>> +        c->ecache_id = mp;
>>>>           break;
>>>>         default:
>>>> @@ -1251,6 +1258,60 @@ void mdesc_fill_in_cpu_data(cpumask_t *mask)
>>>>       smp_fill_in_sib_core_maps();
>>>>   }
>>>>   +int init_cache_level(unsigned int cpu)
>>>> +{
>>>> +    struct cpu_cacheinfo *i = get_cpu_cacheinfo(cpu);
>>>> +    i->num_levels = 2;
>>>> +    i->num_leaves = 3;
>>>> +    return 0;
>>>> +}
>>>> +
>>>> +int populate_cache_leaves(unsigned int cpu)
>>>> +{
>>>> +    struct cacheinfo *leaf = get_cpu_cacheinfo(cpu)->info_list;
>>>> +    cpuinfo_sparc *cs = &cpu_data(cpu);
>>>> +
>>>> +    leaf->id                    = cs->icache_id;
>>>> +    leaf->attributes            = CACHE_ID;
>>>> +    leaf->level                 = 1;
>>>> +    leaf->type                  = CACHE_TYPE_INST;
>>>> +    leaf->coherency_line_size   = cs->icache_line_size;
>>>> +    leaf->ways_of_associativity = cs->icache_assoc;
>>>> +    leaf->size                  = cs->icache_size;
>>>> +    leaf->priv                  = cs;
>>>> +    if (cs->icache_line_size > 0 && cs->icache_assoc > 0)
>>>> +        leaf->number_of_sets = cs->icache_size / 
>>>> cs->icache_line_size /
>>>> +                               cs->icache_assoc;
>>>> +    ++leaf;
>>>> +
>>>> +    leaf->id                    = cs->dcache_id;
>>>> +    leaf->attributes            = CACHE_ID;
>>>> +    leaf->level                 = 1;
>>>> +    leaf->type                  = CACHE_TYPE_DATA;
>>>> +    leaf->coherency_line_size   = cs->dcache_line_size;
>>>> +    leaf->ways_of_associativity = cs->dcache_assoc;
>>>> +    leaf->size                  = cs->dcache_size;
>>>> +    leaf->priv                  = cs;
>>>> +    if (cs->dcache_line_size > 0 && cs->dcache_assoc > 0)
>>>> +        leaf->number_of_sets = cs->dcache_size / 
>>>> cs->dcache_line_size /
>>>> +                               cs->dcache_assoc;
>>>> +    ++leaf;
>>>> +
>>>> +    leaf->id                    = cs->ecache_id;
>>>> +    leaf->attributes            = CACHE_ID;
>>>> +    leaf->level                 = 2;
>>>> +    leaf->type                  = CACHE_TYPE_UNIFIED;
>>>> +    leaf->coherency_line_size   = cs->ecache_line_size;
>>>> +    leaf->ways_of_associativity = cs->ecache_assoc;
>>>> +    leaf->size                  = cs->ecache_size;
>>>> +    leaf->priv                  = cs;
>>>> +    if (cs->ecache_line_size > 0 && cs->ecache_assoc > 0)
>>>> +        leaf->number_of_sets = cs->ecache_size / 
>>>> cs->ecache_line_size /
>>>> +                               cs->ecache_assoc;
>>>> +
>>>> +    return 0;
>>>> +}
>>>> +
>>>>   /* mdesc_open() - Grab a reference to mdesc_handle when 
>>>> /dev/mdesc is
>>>>    * opened. Hold this reference until /dev/mdesc is closed to ensure
>>>>    * mdesc data structure is not released underneath us. Store the
>>
> I’ve applied the patch to a dual‑socket SPARC S7‑2 (16 cores total, 
> 128 threads) and wanted to share the resulting lscpu output, since it 
> clarifies what the patch fixes and what still needs work for 3‑level 
> parts.
>
> Before the patch:
>
> L1d: 2 MiB (128 instances)
> L1i: 2 MiB (128 instances)
> L2:  32 MiB (128 instances)
>
> After the patch:
>
> L1d: 256 KiB (16 instances)
> L1i: 256 KiB (16 instances)
> L2:  2 MiB (8 instances)
>
> A) The L1 reporting is now correct. Each of the 16 cores has a private 
> 16 KB L1I and 16 KB L1D, so 256 KiB in 16 instances is exactly right. 
> The per‑thread inflation is gone for L1, and the sharing/instance 
> counts are sensible. The generic leaf->id grouping is clearly doing 
> its job.
>
> * B)  L2 is only half‑reported on S7:  This may be true for S7, T7, 
> and T8 CPUs.
>
> For the L2 split (On S7):
>
>     L2D: 256 KB shared by every 2 cores -> 8 instances × 256 KB = 2 
> MiB  (matches the output)
>
>     L2I: 256 KB shared by every 4 cores ->4 instances × 256 KB = 1 
> MiB  (missing)
>
> So the total L2 on this box should  be 3 MiB across 12 instances , or 
> shown as separate L2D/L2I lines. It looks like MDESC reports the L2I 
> and L2D nodes separately, and the current case 2: overwrites one with 
> the other in ecache_*. Supporting S7 properly would need either 
> separate storage for L2I/L2D or a way to handle multiple level‑2 nodes.
>
> * C) Need "level 3" cache details for (useful for T4, T5, S7, T7, 
> T8).  Perhaps in revised patch?
>
> As expected, the S7’s 16 MB per‑socket L3 (32 MiB total, 2 instances 
> on this box) does not appear at all. init_cache_level() hardcodes 
> num_levels = 2 / num_leaves = 3, and fill_in_one_cache() has no case 
> 3:, so the MDESC level‑3 node is dropped in default.
>
> Adding an L3 leaf may need?
>
>     l3_* fields in cpuinfo_sparc,
>
>     a case 3: in fill_in_one_cache(),
>
>     num_levels = 3 / num_leaves = 4 in init_cache_level(),
>
>     a 4th leaf in populate_cache_leaves().
>
> For T7‑1/T8‑1 this would surface  their 64 MiB per‑socket L3; for S7‑2 
> it would show 32 MiB in 2 instances (16 MiB per cpu). The T5240 result 
> looks correct because that machine is 2‑level only, so the missing 
> case 3: doesn’t show up there.
>
> Summary:
>
> The patch is an improvement but needs more work. It fixes the L1 (and 
> L2D) inflation on S7‑2. To make it complete for the 3‑level SPARC 
> parts, it needs L2I handling and an L3 leaf.  It would be great to 
> have L3 cache details shown via lscpu as well.
>
>
> Tested-By: Tony Rodriguez <unixpro1970@gmail.com>
>
>
> Regards,
>
> Tony
>

Corrected typos in my summary and changed point C to be "Perhaps in a 
revised patch?"

Tony


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] sparc: populate sysfs cache topology information on sun4v
  2026-10-18 22:27 Jan Engelhardt
  2026-10-19  0:16 ` Tony Rodriguez
@ 2026-10-19  9:49 ` Stian Halseth
  2026-10-22 16:17   ` Jan Engelhardt
  1 sibling, 1 reply; 15+ messages in thread
From: Stian Halseth @ 2026-10-19  9:49 UTC (permalink / raw)
  To: Jan Engelhardt, davem; +Cc: sparclinux, linux-kernel, John Paul Adrian Glaubitz

[-- Attachment #1: Type: text/plain, Size: 2255 bytes --]

Hi Jan, thanks for looking into this!

On Mon, 2026-10-19 at 00:27 +0200, Jan Engelhardt wrote:
> +int init_cache_level(unsigned int cpu)
> +{
> +	struct cpu_cacheinfo *i = get_cpu_cacheinfo(cpu);
> +	i->num_levels = 2;
> +	i->num_leaves = 3;

Using the MD node as the cache id with CACHE_ID set looks to me like
the right way to do it. I tried it on my T4-1 and V240 and have a few
comments.

mdesc.c is also built for sun4u and there the new *_id fields are never
filled in and stay 0, so both CPUs end up sharing every cache. On my
V240 lscpu goes from 128K L1d in 2 instances to 64K in 1, and the same
for L1i and L2. A tlb_type == hypervisor check here, like the one in
setup_per_cpu_areas(), and -ENOENT otherwise puts it back to how it is
today. I tested that too.

The fixed 2 levels / 3 leaves also only fits T1-T3. On the T4-1 L1 and
L2 come out right now (16K and 128K per core, 8 instances), but the
4M L3 shared by all strands is missing. I dumped the cache nodes from
/dev/mdesc on my T4-1 and T7-1:

  T4:  L1i/L1d per core, L2 128K per core, L3 4M shared by all
  M7:  L1i/L1d per core, L2 data per 2 cores + L2 instn per 4 cores
       (both level 2), L3 8M per 4 cores

On M7 the L2 you get is whichever level 2 node fill_in_one_cache()
hits last (data here), labelled Unified. That's what Tony saw on his
S7 too. The missing L3 isn't just cosmetic either. The scheduler,
sched_ext and the page allocator use the last leaf as the LLC, so the
T4 would end up with a per-core 128K L2 as its LLC.

Maybe it's simpler to just walk the MD directly in init_cache_level()
and populate_cache_leaves(), following the same arcs as
fill_in_one_cpu(), and add one leaf per cache node? Then you get the
split L2 and the L3 for free, and cpuinfo_sparc doesn't have to grow.

Tony, about your first list: CACHE_ID is right, it's the flag that
tells the generic code leaf->id is valid. I built the T4 kernel without
it as well, and then nothing shows up at all, cacheinfo just says
"Failed to find cpu0 device node" and gives up. The functions override
the __weak ones in drivers/base/cacheinfo.c, so they have to stay
non-static. The L2 split and the L3 you found on the S7 are real
though.

/Stian



[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] sparc: populate sysfs cache topology information on sun4v
  2026-10-19  9:49 ` Stian Halseth
@ 2026-10-22 16:17   ` Jan Engelhardt
  2026-10-22 16:55     ` Stian Halseth
  0 siblings, 1 reply; 15+ messages in thread
From: Jan Engelhardt @ 2026-10-22 16:17 UTC (permalink / raw)
  To: Stian Halseth; +Cc: davem, sparclinux, linux-kernel, John Paul Adrian Glaubitz


On Monday 2026-10-19 11:49, Stian Halseth wrote:
>Hi Jan, thanks for looking into this!
>
>On Mon, 2026-10-19 at 00:27 +0200, Jan Engelhardt wrote:
>> +int init_cache_level(unsigned int cpu)
>> +{
>> +	struct cpu_cacheinfo *i = get_cpu_cacheinfo(cpu);
>> +	i->num_levels = 2;
>> +	i->num_leaves = 3;
>
>Using the MD node as the cache id with CACHE_ID set looks to me like
>the right way to do it. I tried it on my T4-1 and V240 and have a few
>comments.

I did not even expect this to do anything sensible on V240.
That is just an accident - and multicore-per-package (MC)
sun4u are expected to be still misreported. That needs again 
different logic.

>Maybe it's simpler to just walk the MD directly in init_cache_level()
>and populate_cache_leaves(), following the same arcs as
>fill_in_one_cpu(), and add one leaf per cache node?

After some pondering and in anticipation of MC sun4u, I think
re-walking is sensible. I expect there to be a few savings
(just 24 per strand) and a bunch of LOC removals.

More later.

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] sparc: populate sysfs cache topology information on sun4v
  2026-10-22 16:17   ` Jan Engelhardt
@ 2026-10-22 16:55     ` Stian Halseth
  0 siblings, 0 replies; 15+ messages in thread
From: Stian Halseth @ 2026-10-22 16:55 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: davem, sparclinux, linux-kernel, John Paul Adrian Glaubitz

[-- Attachment #1: Type: text/plain, Size: 889 bytes --]

Hi Jan,

On Thu, 2026-10-22 at 18:17 +0200, Jan Engelhardt wrote:
> I did not even expect this to do anything sensible on V240.
> That is just an accident - and multicore-per-package (MC)
> sun4u are expected to be still misreported. That needs again 
> different logic.

Right, so maybe just return -ENOENT on sun4u and leave it as it is? The
lscpu fallback already gets the single-core ones right.

> 
> > Maybe it's simpler to just walk the MD directly in
> > init_cache_level()
> > and populate_cache_leaves(), following the same arcs as
> > fill_in_one_cpu(), and add one leaf per cache node?
> 
> After some pondering and in anticipation of MC sun4u, I think
> re-walking is sensible. I expect there to be a few savings
> (just 24 per strand) and a bunch of LOC removals.

Sounds good. I'm happy to test v2 on my T4-1, T7-1 and V240 when it's
ready.

/Stian

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] sparc: populate sysfs cache topology information on sun4v
       [not found] <X-TH#1.20261018222723.43442-1-jengelh@inai.de>
@ 2026-10-26 18:52 ` Tony Rodriguez
  2026-10-26 19:18   ` Tony Rodriguez
  0 siblings, 1 reply; 15+ messages in thread
From: Tony Rodriguez @ 2026-10-26 18:52 UTC (permalink / raw)
  To: Jan Engelhardt
  Cc: davem, sparclinux, linux-kernel, John Paul Adrian Glaubitz,
	Stian Halseth

Tested-by: Tony Rodriguez <unixpro1970@gmail.com>

Tested on both SPARC T7-1 and S7-2 systems; the lscpu output is correct on both.

A) T7-1:
A1) lscpu
Architecture:          sparc64
  CPU op-mode(s):      32-bit, 64-bit
  Byte Order:          Big Endian
CPU(s):                256
  On-line CPU(s) list: 0-255
Model name:            SPARC-M7
  Thread(s) per core:  8
  Core(s) per socket:  32
  Socket(s):           1
  Flags:               sun4v
Caches (sum of all):
  L1d:                 512 KiB (32 instances)
  L1i:                 512 KiB (32 instances)
  L2d:                 4 MiB (16 instances)
  L2i:                 2 MiB (8 instances)
  L3:                  64 MiB (8 instances)
NUMA:
  NUMA node(s):        1
  NUMA node0 CPU(s):   0-255

A2) lscpu --cache
NAME ONE-SIZE ALL-SIZE WAYS TYPE        LEVEL  SETS PHY-LINE COHERENCY-SIZE
L1d       16K     512K    4 Data            1   128       32
L1i       16K     512K    4 Instruction     1    64       64
L2d      256K       4M    8 Data            2   512       64
L2i      256K       2M    8 Instruction     2   512       64
L3         8M      64M    8 Unified         3 16384       64

B) S7-2:
B1) lscpu
Architecture:          sparc64
  CPU op-mode(s):      32-bit, 64-bit
  Byte Order:          Big Endian
CPU(s):                128
  On-line CPU(s) list: 0-127
Model name:            SPARC-S7 (Sonoma)
  Thread(s) per core:  8
  Core(s) per socket:  8
  Socket(s):           2
  Flags:               sun4v
Caches (sum of all):
  L1d:                 256 KiB (16 instances)
  L1i:                 256 KiB (16 instances)
  L2d:                 2 MiB (8 instances)
  L2i:                 1 MiB (4 instances)
  L3:                  32 MiB (4 instances)
NUMA:
  NUMA node(s):        2
  NUMA node0 CPU(s):   0-63
  NUMA node1 CPU(s):   64-127

B2) lscpu --cache
NAME ONE-SIZE ALL-SIZE WAYS TYPE        LEVEL  SETS PHY-LINE COHERENCY-SIZE
L1d       16K     256K    4 Data            1   128       32
L1i       16K     256K    4 Instruction     1    64       64
L2d      256K       2M    8 Data            2   512       64
L2i      256K       1M    8 Instruction     2   512       64
L3         8M      32M    8 Unified         3 16384       64


On Mon, Oct 26, 2026 at 11:42 AM Jan Engelhardt <jengelh@inai.de> wrote:
>
> The SPARC port does not publish any cache topology information. The
> lscpu utility therefore falls back to assuming the published cache
> sizes are for unshared caches, and as a result, reports way more
> cache than there really is present in the system. On a T5240 machine:
>
> ```
> $ lscpu
> Architecture:          sparc64
>   CPU op-mode(s):      32-bit, 64-bit
>   Byte Order:          Big Endian
> CPU(s):                128
>   On-line CPU(s) list: 0-127
> Model name:            UltraSparc T2 (Niagara2)
>   Thread(s) per core:  4
>   Core(s) per socket:  16
>   Socket(s):           2
>   Flags:               sun4v
> Caches (sum of all):
>   L1d:                 1 MiB (128 instances)
>   L1i:                 2 MiB (128 instances)
>   L2:                  512 MiB (128 instances)
> ```
>
> This patch implements population of the sysfs cacheinfo structures
> for sun4v (systems with mdesc). The proper amount is now reflected in
> lscpu's output:
>
> ```
> Caches (sum of all):
>   L1d:                 128 KiB (16 instances)
>   L1i:                 256 KiB (16 instances)
>   L2:                  8 MiB (2 instances)
> ```
>
> Signed-off-by: Jan Engelhardt <jengelh@inai.de>
> ---
>  arch/sparc/include/asm/cpudata_64.h |  8 +++-
>  arch/sparc/kernel/mdesc.c           | 61 +++++++++++++++++++++++++++++
>  2 files changed, 68 insertions(+), 1 deletion(-)
>
> diff --git a/arch/sparc/include/asm/cpudata_64.h b/arch/sparc/include/asm/cpudata_64.h
> index 056b3c0e7ef9..e28b841ffe23 100644
> --- a/arch/sparc/include/asm/cpudata_64.h
> +++ b/arch/sparc/include/asm/cpudata_64.h
> @@ -18,13 +18,19 @@ typedef struct {
>         unsigned int    irq0_irqs;
>         unsigned int    __pad2;
>
> -       /* Dcache line 2, rarely used */
> +       /* Dcache line 2+3, rarely used */
>         unsigned int    dcache_size;
>         unsigned int    dcache_line_size;
> +       unsigned int    dcache_assoc;
> +       unsigned int    dcache_id;
>         unsigned int    icache_size;
>         unsigned int    icache_line_size;
> +       unsigned int    icache_assoc;
> +       unsigned int    icache_id;
>         unsigned int    ecache_size;
>         unsigned int    ecache_line_size;
> +       unsigned int    ecache_assoc;
> +       unsigned int    ecache_id;
>         unsigned short  sock_id;        /* physical package */
>         unsigned short  core_id;
>         unsigned short  max_cache_id;   /* groupings of highest shared cache */
> diff --git a/arch/sparc/kernel/mdesc.c b/arch/sparc/kernel/mdesc.c
> index 23b51a32e96e..029d9d812074 100644
> --- a/arch/sparc/kernel/mdesc.c
> +++ b/arch/sparc/kernel/mdesc.c
> @@ -818,6 +818,7 @@ static void fill_in_one_cache(cpuinfo_sparc *c, struct mdesc_handle *hp, u64 mp)
>         const u64 *level = mdesc_get_property(hp, mp, "level", NULL);
>         const u64 *size = mdesc_get_property(hp, mp, "size", NULL);
>         const u64 *line_size = mdesc_get_property(hp, mp, "line-size", NULL);
> +       const u64 *assoc = mdesc_get_property(hp, mp, "associativity", NULL);
>         const char *type;
>         int type_len;
>
> @@ -828,15 +829,21 @@ static void fill_in_one_cache(cpuinfo_sparc *c, struct mdesc_handle *hp, u64 mp)
>                 if (of_find_in_proplist(type, "instn", type_len)) {
>                         c->icache_size = *size;
>                         c->icache_line_size = *line_size;
> +                       c->icache_assoc = assoc ? *assoc : 0;
> +                       c->icache_id = mp;
>                 } else if (of_find_in_proplist(type, "data", type_len)) {
>                         c->dcache_size = *size;
>                         c->dcache_line_size = *line_size;
> +                       c->dcache_assoc = assoc ? *assoc : 0;
> +                       c->dcache_id = mp;
>                 }
>                 break;
>
>         case 2:
>                 c->ecache_size = *size;
>                 c->ecache_line_size = *line_size;
> +               c->ecache_assoc = assoc ? *assoc : 0;
> +               c->ecache_id = mp;
>                 break;
>
>         default:
> @@ -1251,6 +1258,60 @@ void mdesc_fill_in_cpu_data(cpumask_t *mask)
>         smp_fill_in_sib_core_maps();
>  }
>
> +int init_cache_level(unsigned int cpu)
> +{
> +       struct cpu_cacheinfo *i = get_cpu_cacheinfo(cpu);
> +       i->num_levels = 2;
> +       i->num_leaves = 3;
> +       return 0;
> +}
> +
> +int populate_cache_leaves(unsigned int cpu)
> +{
> +       struct cacheinfo *leaf = get_cpu_cacheinfo(cpu)->info_list;
> +       cpuinfo_sparc *cs = &cpu_data(cpu);
> +
> +       leaf->id                    = cs->icache_id;
> +       leaf->attributes            = CACHE_ID;
> +       leaf->level                 = 1;
> +       leaf->type                  = CACHE_TYPE_INST;
> +       leaf->coherency_line_size   = cs->icache_line_size;
> +       leaf->ways_of_associativity = cs->icache_assoc;
> +       leaf->size                  = cs->icache_size;
> +       leaf->priv                  = cs;
> +       if (cs->icache_line_size > 0 && cs->icache_assoc > 0)
> +               leaf->number_of_sets = cs->icache_size / cs->icache_line_size /
> +                                      cs->icache_assoc;
> +       ++leaf;
> +
> +       leaf->id                    = cs->dcache_id;
> +       leaf->attributes            = CACHE_ID;
> +       leaf->level                 = 1;
> +       leaf->type                  = CACHE_TYPE_DATA;
> +       leaf->coherency_line_size   = cs->dcache_line_size;
> +       leaf->ways_of_associativity = cs->dcache_assoc;
> +       leaf->size                  = cs->dcache_size;
> +       leaf->priv                  = cs;
> +       if (cs->dcache_line_size > 0 && cs->dcache_assoc > 0)
> +               leaf->number_of_sets = cs->dcache_size / cs->dcache_line_size /
> +                                      cs->dcache_assoc;
> +       ++leaf;
> +
> +       leaf->id                    = cs->ecache_id;
> +       leaf->attributes            = CACHE_ID;
> +       leaf->level                 = 2;
> +       leaf->type                  = CACHE_TYPE_UNIFIED;
> +       leaf->coherency_line_size   = cs->ecache_line_size;
> +       leaf->ways_of_associativity = cs->ecache_assoc;
> +       leaf->size                  = cs->ecache_size;
> +       leaf->priv                  = cs;
> +       if (cs->ecache_line_size > 0 && cs->ecache_assoc > 0)
> +               leaf->number_of_sets = cs->ecache_size / cs->ecache_line_size /
> +                                      cs->ecache_assoc;
> +
> +       return 0;
> +}
> +
>  /* mdesc_open() - Grab a reference to mdesc_handle when /dev/mdesc is
>   * opened. Hold this reference until /dev/mdesc is closed to ensure
>   * mdesc data structure is not released underneath us. Store the
> --
> 2.56.0
>
>

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] sparc: populate sysfs cache topology information on sun4v
  2026-10-26 18:52 ` Tony Rodriguez
@ 2026-10-26 19:18   ` Tony Rodriguez
  0 siblings, 0 replies; 15+ messages in thread
From: Tony Rodriguez @ 2026-10-26 19:18 UTC (permalink / raw)
  To: Jan Engelhardt
  Cc: davem, sparclinux, linux-kernel, John Paul Adrian Glaubitz,
	Stian Halseth


On 10/26/26 11:52 AM, Tony Rodriguez wrote:
> Tested-by: Tony Rodriguez <unixpro1970@gmail.com>
>
> Tested on both SPARC T7-1 and S7-2 systems; the lscpu output is correct on both.
>
> A) T7-1:
> A1) lscpu
> Architecture:          sparc64
>    CPU op-mode(s):      32-bit, 64-bit
>    Byte Order:          Big Endian
> CPU(s):                256
>    On-line CPU(s) list: 0-255
> Model name:            SPARC-M7
>    Thread(s) per core:  8
>    Core(s) per socket:  32
>    Socket(s):           1
>    Flags:               sun4v
> Caches (sum of all):
>    L1d:                 512 KiB (32 instances)
>    L1i:                 512 KiB (32 instances)
>    L2d:                 4 MiB (16 instances)
>    L2i:                 2 MiB (8 instances)
>    L3:                  64 MiB (8 instances)
> NUMA:
>    NUMA node(s):        1
>    NUMA node0 CPU(s):   0-255
>
> A2) lscpu --cache
> NAME ONE-SIZE ALL-SIZE WAYS TYPE        LEVEL  SETS PHY-LINE COHERENCY-SIZE
> L1d       16K     512K    4 Data            1   128                      32
> L1i       16K     512K    4 Instruction     1    64                      64
> L2d      256K       4M    8 Data            2   512                      64
> L2i      256K       2M    8 Instruction     2   512                      64
> L3         8M      64M    8 Unified         3 16384                      64
>
> B) S7-2:
> B1) lscpu
> Architecture:          sparc64
>    CPU op-mode(s):      32-bit, 64-bit
>    Byte Order:          Big Endian
> CPU(s):                128
>    On-line CPU(s) list: 0-127
> Model name:            SPARC-S7 (Sonoma)
>    Thread(s) per core:  8
>    Core(s) per socket:  8
>    Socket(s):           2
>    Flags:               sun4v
> Caches (sum of all):
>    L1d:                 256 KiB (16 instances)
>    L1i:                 256 KiB (16 instances)
>    L2d:                 2 MiB (8 instances)
>    L2i:                 1 MiB (4 instances)
>    L3:                  32 MiB (4 instances)
> NUMA:
>    NUMA node(s):        2
>    NUMA node0 CPU(s):   0-63
>    NUMA node1 CPU(s):   64-127
>
> B2) lscpu --cache
> NAME ONE-SIZE ALL-SIZE WAYS TYPE        LEVEL  SETS PHY-LINE COHERENCY-SIZE
> L1d       16K     256K    4 Data            1   128                      32
> L1i       16K     256K    4 Instruction     1    64                      64
> L2d      256K       2M    8 Data            2   512                      64
> L2i      256K       1M    8 Instruction     2   512                      64
> L3         8M      32M    8 Unified         3 16384                      64

Corrected the lscpu --cache output to fix the PHY-LINE and 
COHERENCY-SIZE fields, making the output easier to read in the email.


^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH] sparc: populate sysfs cache topology information on sun4v
@ 2026-10-26 19:34 Jan Engelhardt
  2026-10-26 19:55 ` sashiko-bot
  2026-10-26 20:16 ` Stian Halseth
  0 siblings, 2 replies; 15+ messages in thread
From: Jan Engelhardt @ 2026-10-26 19:34 UTC (permalink / raw)
  To: sparclinux

The SPARC port (arch/sparc/) originally invented its own way of
reporting cache sizes for Linux version 2.6.22, by providing e.g.
/sys/devices/system/cpu/cpu0/l1_icache_size. lscpu (util-linux 2.37)
gained code just to read these SPARC-specific sysfs files.

The Linux kernel gained a generic cacheinfo reporting mechanism
(drivers/base/cacheinfo.c) in v3.19, but arch/sparc's sysfs files
were not unified into it, unfortunately.

Because the SPARC sysfs files convey no information about the shared
nature of caches, lscpu (util-linux 2.42) overreports cache numbers.
For example, on a T5240 machine:

```
Model name:            UltraSparc T2 (Niagara2)
  Thread(s) per core:  4
  Core(s) per socket:  16
  Socket(s):           2
  Flags:               sun4v
Caches (sum of all):
  L1d:                 1 MiB (128 instances)
  L1i:                 2 MiB (128 instances)
  L2:                  512 MiB (128 instances)
```

The SPARC port also lacked reporting L3 caches, present in the
Oracle S3 and Fujitsu SPARC64 XII cores (machines: Oracle T4 and
Fujitsu M12, respectively.)

On a SPARC S7-2:

```
Model name:            SPARC-S7 (Sonoma)
  Thread(s) per core:  8
  Core(s) per socket:  8
  Socket(s):           2
  Flags:               sun4v
Caches (sum of all):
  L1d:                 256 KiB (16 instances)
  L1i:                 256 KiB (16 instances)
  L2:                  2 MiB (8 instances)
```

This patch implements population of the sysfs cacheinfo structures
for sun4v. The proper amount is now reflected in lscpu's output:

```
<<T5240>>
Caches (sum of all):
  L1d:                 128 KiB (16 instances)
  L1i:                 256 KiB (16 instances)
  L2:                  8 MiB (2 instances)

<<S7-2>>
  L1d:                 256 KiB (16 instances)
  L1i:                 256 KiB (16 instances)
  L2d:                 2 MiB (8 instances)
  L2i:                 1 MiB (4 instances)
  L3:                  32 MiB (4 instances)
```
---
 arch/sparc/include/asm/cpudata_64.h |   1 +
 arch/sparc/include/asm/mdesc.h      |   2 +
 arch/sparc/kernel/mdesc.c           | 136 ++++++++++++++++++++++++++++
 arch/sparc/kernel/prom_common.c     |  34 +++++++
 4 files changed, 173 insertions(+)

diff --git a/arch/sparc/include/asm/cpudata_64.h b/arch/sparc/include/asm/cpudata_64.h
index 056b3c0e7ef9..57b020ba93e0 100644
--- a/arch/sparc/include/asm/cpudata_64.h
+++ b/arch/sparc/include/asm/cpudata_64.h
@@ -29,6 +29,7 @@ typedef struct {
 	unsigned short	core_id;
 	unsigned short  max_cache_id;	/* groupings of highest shared cache */
 	signed short	proc_id;	/* strand (aka HW thread) id */
+	u64             mdesc_node;
 } cpuinfo_sparc;
 
 DECLARE_PER_CPU(cpuinfo_sparc, __cpu_data);
diff --git a/arch/sparc/include/asm/mdesc.h b/arch/sparc/include/asm/mdesc.h
index ec31a06d04cc..71e77641f158 100644
--- a/arch/sparc/include/asm/mdesc.h
+++ b/arch/sparc/include/asm/mdesc.h
@@ -90,6 +90,8 @@ u64 mdesc_get_node(struct mdesc_handle *hp, const char *node_name,
 int mdesc_get_node_info(struct mdesc_handle *hp, u64 node,
 			const char *node_name, union md_node_info *node_info);
 
+int mdesc_cache_walk(unsigned int cpuid, bool fill_it);
+
 void mdesc_fill_in_cpu_data(cpumask_t *mask);
 void mdesc_populate_present_mask(cpumask_t *mask);
 void mdesc_get_page_sizes(cpumask_t *mask, unsigned long *pgsz_mask);
diff --git a/arch/sparc/kernel/mdesc.c b/arch/sparc/kernel/mdesc.c
index ecd6c8ae49c7..8b982c6fee11 100644
--- a/arch/sparc/kernel/mdesc.c
+++ b/arch/sparc/kernel/mdesc.c
@@ -1195,6 +1195,12 @@ static void *fill_in_one_cpu(struct mdesc_handle *hp, u64 mp, int cpuid,
 
 	c = &cpu_data(cpuid);
 	c->clock_tick = *cfreq;
+	/*
+	 * This will help more quickly finding the mdesc node again
+	 * (otherwise we would have to iterate over all cpu nodes
+	 * to locate any one with a given id).
+	 */
+	c->mdesc_node = mp;
 
 	tb = &trap_block[cpuid];
 	get_mondo_data(hp, mp, tb);
@@ -1242,6 +1248,136 @@ void mdesc_fill_in_cpu_data(cpumask_t *mask)
 	smp_fill_in_sib_core_maps();
 }
 
+/**
+ * At MDESC node @node, analyze forward links to find the @want_target,
+ * and, if @want_type is not %NULL, that it is of the requested type.
+ */
+static u64 mdesc_find_subobject(struct mdesc_handle *mdh, u64 iu_node,
+				const char *want_target, const char *want_type)
+{
+	u64 iter;
+
+	mdesc_for_each_arc(iter, mdh, iu_node, MDESC_ARC_TYPE_FWD) {
+		u64 sub_node = mdesc_arc_target(mdh, iter);
+		const char *sub_name = mdesc_node_name(mdh, sub_node);
+		const char *type_list;
+		int len;
+
+		if (strcmp(sub_name, want_target) != 0)
+			continue;
+		if (want_type == NULL)
+			return sub_node;
+		type_list = mdesc_get_property(mdh, sub_node, "type", &len);
+		if (of_find_in_proplist(type_list, want_type, len))
+			return sub_node;
+	}
+	return -1;
+}
+
+static void mdesc_cache_fill(struct mdesc_handle *mdh, u64 cache_node,
+			     struct cacheinfo *leaf)
+{
+	int type_len;
+	const u64 *level      = mdesc_get_property(mdh, cache_node, "level", NULL);
+	const u64 *size       = mdesc_get_property(mdh, cache_node, "size", NULL);
+	const u64 *line_size  = mdesc_get_property(mdh, cache_node, "line-size", NULL);
+	const u64 *assoc      = mdesc_get_property(mdh, cache_node, "associativity", NULL);
+	const char *type_list = mdesc_get_property(mdh, cache_node, "type", &type_len);
+
+	leaf->fw_token = (void *)(uintptr_t)cache_node;
+	leaf->level = *level;
+	if (type_list != NULL) {
+		bool i = of_find_in_proplist(type_list, "instn", type_len);
+		bool d = of_find_in_proplist(type_list, "data", type_len);
+		if (i && d)
+			leaf->type = CACHE_TYPE_UNIFIED;
+		else if (i)
+			leaf->type = CACHE_TYPE_INST;
+		else if (d)
+			leaf->type = CACHE_TYPE_DATA;
+	}
+	if (size != NULL)
+		leaf->size = *size;
+	if (line_size != NULL)
+		leaf->coherency_line_size = *line_size;
+	if (assoc != NULL)
+		leaf->ways_of_associativity = *assoc;
+	if (leaf->coherency_line_size != 0 && leaf->ways_of_associativity != 0)
+		leaf->number_of_sets = leaf->size / leaf->coherency_line_size /
+				       leaf->ways_of_associativity;
+}
+
+static int mdesc_cache_record(struct mdesc_handle *mdh, u64 cache_node,
+			      unsigned int cpuid, unsigned int *leaf_count,
+			      bool fill_it)
+{
+	struct cpu_cacheinfo *ci;
+	const u64 *level = mdesc_get_property(mdh, cache_node, "level", NULL);
+	u64 iter;
+
+	if (level == NULL)
+		return -ENOENT;
+	ci = get_cpu_cacheinfo(cpuid);
+	if (fill_it) {
+		if (*leaf_count >= ci->num_leaves)
+			return -ELOOP;
+		mdesc_cache_fill(mdh, cache_node,
+				 &ci->info_list[(*leaf_count)++]);
+	} else {
+		++ci->num_leaves;
+		if (ci->num_levels < *level)
+			ci->num_levels = *level;
+	}
+
+	mdesc_for_each_arc(iter, mdh, cache_node, MDESC_ARC_TYPE_FWD) {
+		u64 sub_node = mdesc_arc_target(mdh, iter);
+		const char *sub_name = mdesc_node_name(mdh, sub_node);
+		int ret;
+
+		if (strcmp(sub_name, "cache") != 0)
+			continue;
+		ret = mdesc_cache_record(mdh, sub_node, cpuid,
+					 leaf_count, fill_it);
+		if (ret != 0)
+			return ret;
+	}
+	return 0;
+}
+
+int mdesc_cache_walk(unsigned int cpuid, bool fill_it)
+{
+	/*
+	 * ifetch is where instruction caches are reported under.
+	 * integer is where data cahes are reported under.
+	 *
+	 * The MDESCs of e.g. the SUNW,T5240 and ORCL,SPARC-S7-2 machines
+	 * use "integer". Which machines are using "int"?
+	 */
+	static const char *const unit_kinds[] = {"ifetch", "integer", "int"};
+	unsigned int leaf_count = 0;
+	int ret = 0;
+	struct mdesc_handle *mdh = mdesc_grab();
+
+	if (mdh == NULL)
+		return -ENOENT;
+
+	for (unsigned int i = 0; i < ARRAY_SIZE(unit_kinds); ++i) {
+		u64 node = mdesc_find_subobject(mdh, cpu_data(cpuid).mdesc_node,
+						"exec-unit", unit_kinds[i]);
+		if (node == -1)
+			continue;
+		node = mdesc_find_subobject(mdh, node, "cache", NULL);
+		if (node == -1)
+			continue;
+		ret = mdesc_cache_record(mdh, node, cpuid,
+					 &leaf_count, fill_it);
+		if (ret != 0)
+			break;
+	}
+	mdesc_release(mdh);
+	return ret;
+}
+
 /* mdesc_open() - Grab a reference to mdesc_handle when /dev/mdesc is
  * opened. Hold this reference until /dev/mdesc is closed to ensure
  * mdesc data structure is not released underneath us. Store the
diff --git a/arch/sparc/kernel/prom_common.c b/arch/sparc/kernel/prom_common.c
index d258fd10db01..34f3b2650547 100644
--- a/arch/sparc/kernel/prom_common.c
+++ b/arch/sparc/kernel/prom_common.c
@@ -11,12 +11,14 @@
  */
 
 #include <linux/kernel.h>
+#include <linux/cacheinfo.h>
 #include <linux/export.h>
 #include <linux/errno.h>
 #include <linux/mutex.h>
 #include <linux/slab.h>
 #include <linux/of.h>
 #include <linux/of_pdt.h>
+#include <asm/mdesc.h>
 #include <asm/prom.h>
 #include <asm/oplib.h>
 
@@ -112,6 +114,38 @@ int of_find_in_proplist(const char *list, const char *match, int len)
 }
 EXPORT_SYMBOL(of_find_in_proplist);
 
+int init_cache_level(unsigned int cpuid)
+{
+#ifdef CONFIG_SPARC64
+	if (tlb_type == hypervisor)
+		return mdesc_cache_walk(cpuid, false);
+#endif
+	return -ENOENT;
+}
+
+int populate_cache_leaves(unsigned int cpuid)
+{
+	/*
+	 * Note that drivers/base/cacheinfo expects leaves to be sorted by
+	 * level, then by index in [inst, data_or_unified]. Code should fill
+	 * in leaves in that order (else, a call to qsort will be needed).
+	 */
+	int ret = -ENOENT;
+	switch (tlb_type) {
+#ifdef CONFIG_SPARC64
+	case hypervisor:
+		ret = mdesc_cache_walk(cpuid, true);
+		break;
+#endif
+	default:
+		break;
+	}
+	if (ret == -ELOOP)
+		pr_err("%s: tried to populate too much. "
+		       "(Programming error, go investigate.)\n", __func__);
+	return ret;
+}
+
 /*
  * SPARC32 and SPARC64's prom_nextprop() do things differently
  * here, despite sharing the same interface.  SPARC32 doesn't fill in 'buf',
-- 
2.56.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Re: [PATCH] sparc: populate sysfs cache topology information on sun4v
  2026-10-26 19:34 [PATCH] sparc: populate sysfs cache topology information on sun4v Jan Engelhardt
@ 2026-10-26 19:55 ` sashiko-bot
  2026-10-26 20:16 ` Stian Halseth
  1 sibling, 0 replies; 15+ messages in thread
From: sashiko-bot @ 2026-10-26 19:55 UTC (permalink / raw)
  To: Jan Engelhardt, sparclinux

Thank you for your contribution! Sashiko AI review found 8 potential issue(s) to consider:
- [Critical] sparc: unreset num_leaves in mdesc_cache_walk() causes OOB on CPU hotplug
- [High] sparc: non-pointer fw_token in mdesc_cache_fill() passed to of_node_put()
- [High] sparc: NULL dereference and uninitialized len in mdesc_find_subobject()
- [High] sparc: depth-first walk in mdesc_cache_record() duplicates and misorders leaves
- [High] sparc: mdesc: stale raw mdesc node indices cached in cpu_data and fw_token
- [Medium] sparc: mdesc_cache_walk() misses direct cache arcs and exec_unit nodes
- [Low] sparc: missing linux/cacheinfo.h include in arch/sparc/kernel/mdesc.c
- [Low] sparc: unguarded tlb_type in populate_cache_leaves() breaks build
--

commit 6af74801a44f46a9a2d5477d269ec4c8488504e6
Author: Jan Engelhardt <jengelh@inai.de>

sparc: populate sysfs cache topology information on sun4v

This commit populates generic sysfs cacheinfo structures on sun4v systems by
walking the hypervisor Machine Description (MDESC) cache hierarchy from
init_cache_level() and populate_cache_leaves().

> diff --git a/arch/sparc/kernel/mdesc.c b/arch/sparc/kernel/mdesc.c
> index 8e645ddac58e2..bfc3f422bffaa 100644
> --- a/arch/sparc/kernel/mdesc.c
> +++ b/arch/sparc/kernel/mdesc.c
> @@ -1196,6 +1196,12 @@ static void *fill_in_one_cpu(struct mdesc_handle *hp, u64 mp, int cpuid,
>  
>  	c = &cpu_data(cpuid);
>  	c->clock_tick = *cfreq;
> +	/*
> +	 * This will help more quickly finding the mdesc node again
> +	 * (otherwise we would have to iterate over all cpu nodes
> +	 * to locate any one with a given id).
> +	 */
> +	c->mdesc_node = mp;

[Severity: High]
Can caching raw MDESC node indices in cpu_data(cpuid).mdesc_node and
leaf->fw_token become stale across runtime Machine Description updates?

In arch/sparc/kernel/mdesc.c, node identifiers are raw array indices into
node_block(&hp->mdesc). When the hypervisor updates the Machine Description at
runtime via md_update_data() -> mdesc_update(), cur_mdesc is replaced and raw
node indices can shift, as noted in arch/sparc/kernel/vio.c:vio_vdev_node():

    /* Node numbers arevesion-specific and cannot be cached across
     * mdesc_update() calls...
     */

However, mdesc_update() does not refresh cpu_data(cpuid).mdesc_node for
existing CPUs, and dr_cpu_configure() only calls mdesc_fill_in_cpu_data() for
newly configured CPUs.

If an existing CPU is offlined and re-onlined after mdesc_update(),
cacheinfo_cpu_online() -> detect_cache_attributes() -> mdesc_cache_walk()
grabs the new cur_mdesc and passes the stale cpu_data(cpuid).mdesc_node into
mdesc_find_subobject() -> mdesc_next_arc():

    if (from == MDESC_NODE_NULL || from >= last_node)
        return MDESC_NODE_NULL;

    ep = base + from;

    ep++;
    for (; ep->tag != MD_NODE_END; ep++) {

Because mdesc_next_arc() does not check that base[from].tag == MD_NODE before
advancing ep and scanning until MD_NODE_END, a stale index can walk the wrong
node or scan past node_block if from lands on the final MD_NODE_END or
MD_LIST_END element.

Similarly, mdesc_cache_fill() stores the raw cache_node index in
leaf->fw_token. If CPUs are hot-added via dr_cpu_configure() -> add_cpu()
after mdesc_update(), newly added CPUs get leaf->fw_token values from the new
MDESC while already-online CPUs retain sib_leaf->fw_token values from the old
MDESC. When cache_shared_cpu_map_setup() calls cache_leaves_are_shared(),
could comparing those raw indices across different MDESC generations corrupt
shared_cpu_map?

>  
>  	tb = &trap_block[cpuid];
>  	get_mondo_data(hp, mp, tb);
> @@ -1243,6 +1249,136 @@ void mdesc_fill_in_cpu_data(cpumask_t *mask)
>  	smp_fill_in_sib_core_maps();
>  }
>  
> +/**
> + * At MDESC node @node, analyze forward links to find the @want_target,
> + * and, if @want_type is not %NULL, that it is of the requested type.
> + */
> +static u64 mdesc_find_subobject(struct mdesc_handle *mdh, u64 iu_node,
> +				const char *want_target, const char *want_type)
> +{
> +	u64 iter;
> +
> +	mdesc_for_each_arc(iter, mdh, iu_node, MDESC_ARC_TYPE_FWD) {
> +		u64 sub_node = mdesc_arc_target(mdh, iter);
> +		const char *sub_name = mdesc_node_name(mdh, sub_node);
> +		const char *type_list;
> +		int len;
> +
> +		if (strcmp(sub_name, want_target) != 0)
> +			continue;
> +		if (want_type == NULL)
> +			return sub_node;
> +		type_list = mdesc_get_property(mdh, sub_node, "type", &len);
> +		if (of_find_in_proplist(type_list, want_type, len))
> +			return sub_node;

[Severity: High]
What happens in mdesc_find_subobject() if a matching sub_node does not have a
"type" property?

When mdesc_get_property(mdh, sub_node, "type", &len) returns NULL, len is left
uninitialized (or retains its value from the previous loop iteration). Passing
type_list == NULL and an uninitialized len to of_find_in_proplist() will
dereference NULL in strcmp(list, match) whenever len > 0:

arch/sparc/kernel/prom_common.c:of_find_in_proplist() {
    while (len > 0) {
        int l;

        if (!strcmp(list, match))
            return 1;
        ...
    }
}

Should mdesc_find_subobject() check that type_list != NULL before calling
of_find_in_proplist(), similar to mdesc_cache_fill()?

> +	}
> +	return -1;
> +}
> +
> +static void mdesc_cache_fill(struct mdesc_handle *mdh, u64 cache_node,
> +			     struct cacheinfo *leaf)
> +{

[Severity: Low]
Does arch/sparc/kernel/mdesc.c build without including linux/cacheinfo.h?

mdesc_cache_fill() and mdesc_cache_record() dereference struct cacheinfo and
struct cpu_cacheinfo, use CACHE_TYPE_UNIFIED, CACHE_TYPE_INST, and
CACHE_TYPE_DATA, and call get_cpu_cacheinfo(), which are all defined in
linux/cacheinfo.h. The patch adds linux/cacheinfo.h to
arch/sparc/kernel/prom_common.c, but not to arch/sparc/kernel/mdesc.c or
arch/sparc/include/asm/mdesc.h, causing compilation of mdesc.c to fail on
CONFIG_SPARC64.

> +	int type_len;
> +	const u64 *level      = mdesc_get_property(mdh, cache_node, "level", NULL);
> +	const u64 *size       = mdesc_get_property(mdh, cache_node, "size", NULL);
> +	const u64 *line_size  = mdesc_get_property(mdh, cache_node, "line-size", NULL);
> +	const u64 *assoc      = mdesc_get_property(mdh, cache_node, "associativity", NULL);
> +	const char *type_list = mdesc_get_property(mdh, cache_node, "type", &type_len);
> +
> +	leaf->fw_token = (void *)(uintptr_t)cache_node;

[Severity: High]
Is it safe to store a non-pointer MDESC node index in leaf->fw_token when
of_have_populated_dt() is true?

On SPARC (including sun4v), CONFIG_OF is enabled and prom_build_devicetree()
populates of_root at boot, so of_have_populated_dt() returns true.

In drivers/base/cacheinfo.c, cache_shared_cpu_map_remove() assumes that
whenever of_have_populated_dt() is true, this_leaf->fw_token is a struct
device_node pointer and passes it to of_node_put():

drivers/base/cacheinfo.c:cache_shared_cpu_map_remove() {
    ...
        if (of_have_populated_dt())
            of_node_put(this_leaf->fw_token);
    ...
}

When a CPU is offlined (cacheinfo_cpu_pre_down() -> free_cache_attributes() ->
cache_shared_cpu_map_remove()) or cacheinfo setup fails and cleans up,
of_node_put() is called with the small integer cache_node cast to a pointer.
If CONFIG_OF_DYNAMIC is enabled, of_node_put() calls kobject_put(&node->kobj)
and dereferences the small integer as a pointer.

[ ... ]

> +static int mdesc_cache_record(struct mdesc_handle *mdh, u64 cache_node,
> +			      unsigned int cpuid, unsigned int *leaf_count,
> +			      bool fill_it)
> +{
> +	struct cpu_cacheinfo *ci;
> +	const u64 *level = mdesc_get_property(mdh, cache_node, "level", NULL);
> +	u64 iter;
> +
> +	if (level == NULL)
> +		return -ENOENT;
> +	ci = get_cpu_cacheinfo(cpuid);
> +	if (fill_it) {
> +		if (*leaf_count >= ci->num_leaves)
> +			return -ELOOP;
> +		mdesc_cache_fill(mdh, cache_node,
> +				 &ci->info_list[(*leaf_count)++]);
> +	} else {
> +		++ci->num_leaves;
> +		if (ci->num_levels < *level)
> +			ci->num_levels = *level;
> +	}

[Severity: Critical]
Will incrementing ci->num_leaves in place without resetting it to zero cause
out-of-bounds slab accesses across CPU hotplug?

When a CPU is offlined, free_cache_attributes() in drivers/base/cacheinfo.c
frees per_cpu_cacheinfo(cpu) (ci->info_list), but does not clear
ci->num_leaves or ci->num_levels:

drivers/base/cacheinfo.c:free_cache_attributes() {
    if (!per_cpu_cacheinfo(cpu))
        return;

    cache_shared_cpu_map_remove(cpu);

    kfree(per_cpu_cacheinfo(cpu));
    per_cpu_cacheinfo(cpu) = NULL;
}

When that CPU is brought back online, detect_cache_attributes() calls
init_cache_level(cpuid) -> mdesc_cache_walk(cpuid, false) ->
mdesc_cache_record(), which adds to the existing ci->num_leaves value instead
of starting from 0.

For a CPU with N cache leaves, re-onlining it inflates ci->num_leaves to 2*N
(and by another N on each subsequent offline/online cycle).
detect_cache_attributes() then allocates ci->info_list with 2*N entries, while
populate_cache_leaves() only fills the first N entries, leaving entries N..2*N-1
zeroed.

Next, cache_shared_cpu_map_setup() loops index from 0 to cache_leaves(cpu) - 1
(which is 2*N - 1) and indexes directly into every other online sibling CPU's
info_list (which only has N entries):

drivers/base/cacheinfo.c:cache_shared_cpu_map_setup() {
    for (index = 0; index < cache_leaves(cpu); index++) {
        ...
        for_each_online_cpu(i) {
            struct cpu_cacheinfo *sib_cpu_ci = get_cpu_cacheinfo(i);

            if (i == cpu || !sib_cpu_ci->info_list)
                continue;/* skip if itself or no cacheinfo */
            sib_leaf = sib_cpu_ci->info_list + index;
            if (cache_leaves_are_shared(this_leaf, sib_leaf)) {
                cpumask_set_cpu(cpu, &sib_leaf->shared_cpu_map);
                cpumask_set_cpu(i, &this_leaf->shared_cpu_map);
            }
        }
    }
}

For index >= N, sib_leaf points past the end of sib_cpu_ci->info_list, reading
out-of-bounds heap memory in cache_leaves_are_shared() and corrupting adjacent
slab memory via cpumask_set_cpu(cpu, &sib_leaf->shared_cpu_map) whenever the
out-of-bounds fw_token word happens to be NULL (matching the unpopulated
this_leaf->fw_token == NULL).

Should ci->num_leaves and ci->num_levels be reset to 0 before walking the
MDESC in init_cache_level() / mdesc_cache_walk(cpuid, false)?

> +
> +	mdesc_for_each_arc(iter, mdh, cache_node, MDESC_ARC_TYPE_FWD) {
> +		u64 sub_node = mdesc_arc_target(mdh, iter);
> +		const char *sub_name = mdesc_node_name(mdh, sub_node);
> +		int ret;
> +
> +		if (strcmp(sub_name, "cache") != 0)
> +			continue;
> +		ret = mdesc_cache_record(mdh, sub_node, cpuid,
> +					 leaf_count, fill_it);
> +		if (ret != 0)
> +			return ret;
> +	}
> +	return 0;
> +}

[Severity: High]
Does this recursive depth-first walk in mdesc_cache_record() duplicate shared
unified caches and populate leaves out of level order?

In the sun4v MDESC DAG, both the instruction cache chain (under "ifetch") and
the data cache chain (under "integer"/"int") have forward "cache" arcs that
converge on unified higher-level caches (such as unified L2 on T5240 or
unified L3 on S7-2).

Because mdesc_cache_walk() walks the entire forward "cache" chain from each
unit_kinds[] entry in depth-first order without tracking already-visited cache
nodes for cpuid:

1. Any unified L2 or L3 cache reachable from both the instruction and data
   cache paths (or from an exec-unit matching both "integer" and "int") is
   counted and populated multiple times in ci->info_list[] for the same CPU.

2. All higher-level caches reachable from "ifetch" (e.g. L2i and L3, or
   unified L2) are recorded before L1d under "integer", leaving ci->info_list
   unsorted by cache level despite drivers/base/cacheinfo expecting leaves to
   be ordered by level.

> +
> +int mdesc_cache_walk(unsigned int cpuid, bool fill_it)
> +{
> +	/*
> +	 * ifetch is where instruction caches are reported under.
> +	 * integer is where data cahes are reported under.
> +	 *
> +	 * The MDESCs of e.g. the SUNW,T5240 and ORCL,SPARC-S7-2 machines
> +	 * use "integer". Which machines are using "int"?
> +	 */
> +	static const char *const unit_kinds[] = {"ifetch", "integer", "int"};
> +	unsigned int leaf_count = 0;
> +	int ret = 0;
> +	struct mdesc_handle *mdh = mdesc_grab();
> +
> +	if (mdh == NULL)
> +		return -ENOENT;
> +
> +	for (unsigned int i = 0; i < ARRAY_SIZE(unit_kinds); ++i) {
> +		u64 node = mdesc_find_subobject(mdh, cpu_data(cpuid).mdesc_node,
> +						"exec-unit", unit_kinds[i]);
> +		if (node == -1)
> +			continue;
> +		node = mdesc_find_subobject(mdh, node, "cache", NULL);

[Severity: Medium]
Could mdesc_cache_walk() miss caches on sun4v machines that use "exec_unit"
nodes or link "cpu" nodes directly to "cache" nodes?

Earlier in arch/sparc/kernel/mdesc.c, set_proc_ids() checks both "exec_unit"
and "exec-unit":

    static void set_proc_ids(struct mdesc_handle *hp)
    {
        __set_proc_ids(hp, "exec_unit");
        __set_proc_ids(hp, "exec-unit");
    }

And fill_in_one_cpu() checks both direct cpu -> cache forward arcs and 2-hop
cpu -> child -> cache forward arcs:

    mdesc_for_each_arc(a, hp, mp, MDESC_ARC_TYPE_FWD) {
        u64 j, t = mdesc_arc_target(hp, a);
        const char *t_name;

        t_name = mdesc_node_name(hp, t);
        if (!strcmp(t_name, "cache")) {
            fill_in_one_cache(c, hp, t);
            continue;
        }
        ...
    }

Because mdesc_cache_walk() only looks for "exec-unit" subobjects, sun4v
systems whose MDESC uses "exec_unit" or direct cpu -> cache arcs (such as
UltraSPARC T1) will not have any cache leaves populated.

[ ... ]

> diff --git a/arch/sparc/kernel/prom_common.c b/arch/sparc/kernel/prom_common.c
> index c9ec70888a394..f3f655606cc6b 100644
> --- a/arch/sparc/kernel/prom_common.c
> +++ b/arch/sparc/kernel/prom_common.c

[ ... ]

> @@ -112,6 +114,38 @@ int of_find_in_proplist(const char *list, const char *match, int len)
>  }
>  EXPORT_SYMBOL(of_find_in_proplist);
>  
> +int init_cache_level(unsigned int cpuid)
> +{
> +#ifdef CONFIG_SPARC64
> +	if (tlb_type == hypervisor)
> +		return mdesc_cache_walk(cpuid, false);
> +#endif
> +	return -ENOENT;
> +}
> +
> +int populate_cache_leaves(unsigned int cpuid)
> +{
> +	/*
> +	 * Note that drivers/base/cacheinfo expects leaves to be sorted by
> +	 * level, then by index in [inst, data_or_unified]. Code should fill
> +	 * in leaves in that order (else, a call to qsort will be needed).
> +	 */
> +	int ret = -ENOENT;
> +	switch (tlb_type) {
> +#ifdef CONFIG_SPARC64
> +	case hypervisor:
> +		ret = mdesc_cache_walk(cpuid, true);
> +		break;
> +#endif
> +	default:
> +		break;
> +	}

[Severity: Low]
Does switch (tlb_type) in populate_cache_leaves() break the build on
CONFIG_SPARC32 or CONFIG_SPARC64 && !CONFIG_SMP?

arch/sparc/kernel/prom_common.c is built for both 32-bit and 64-bit SPARC, but
tlb_type and the hypervisor enumerator are only declared in
arch/sparc/include/asm/spitfire.h under #ifdef CONFIG_SPARC64.

In init_cache_level(), the tlb_type check is inside #ifdef CONFIG_SPARC64,
whereas in populate_cache_leaves(), switch (tlb_type) is outside the #ifdef,
causing an undeclared identifier error on CONFIG_SPARC32.

Additionally, arch/sparc/kernel/prom_common.c does not include asm/spitfire.h
directly, so on CONFIG_SPARC64 && !CONFIG_SMP where linux/smp.h does not pull
in asm/smp.h -> asm/spitfire.h, tlb_type and hypervisor are undeclared in both
functions.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261026193421.20404-1-jengelh@inai.de?part=1

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] sparc: populate sysfs cache topology information on sun4v
  2026-10-26 19:34 [PATCH] sparc: populate sysfs cache topology information on sun4v Jan Engelhardt
  2026-10-26 19:55 ` sashiko-bot
@ 2026-10-26 20:16 ` Stian Halseth
  2026-10-26 21:27   ` Jan Engelhardt
  1 sibling, 1 reply; 15+ messages in thread
From: Stian Halseth @ 2026-10-26 20:16 UTC (permalink / raw)
  To: Jan Engelhardt, sparclinux

[-- Attachment #1: Type: text/plain, Size: 12005 bytes --]

Hi Jan,

Thanks for the follow up to v1.

I ran the walk against the MDs on my T4-1 and T7-1, and the shared
caches are reached from both ifetch and integer, so every CPU gets 6
leaves: L1i, L2, L3, L1d, L2, L3. 

lscpu hides it, but the index* directories have duplicates and aren't
sorted by level. Collecting the distinct cache nodes first and sorting
them would fix that.

Some smaller things:

- Two exec-units have "integer" in their type, and only the first has
  the L1d arc. Following the cache arcs of any child, like
  fill_in_one_cpu() does, avoids that and the "int" question.
- MD node numbers change on an MD update (see vio_vdev_node()), so
  I'd look up the cpu node by id instead of storing mdesc_node.
- mdesc_find_subobject() passes NULL and an uninitialized len on if
  there is no "type" property.
- switch (tlb_type) breaks sparc32, since prom_common.c is built there
  too. Both functions could just live in mdesc.c.
- Signed-off-by is missing, and a v2 tag with a short changelog would
  help.

The Sashiko num_leaves and of_node_put() findings are based on old
cacheinfo code (5944ce092b97c, 2613cc29c5723), so those can be ignored.

/Stian

On Mon, 2026-10-26 at 20:34 +0100, Jan Engelhardt wrote:
> The SPARC port (arch/sparc/) originally invented its own way of
> reporting cache sizes for Linux version 2.6.22, by providing e.g.
> /sys/devices/system/cpu/cpu0/l1_icache_size. lscpu (util-linux 2.37)
> gained code just to read these SPARC-specific sysfs files.
> 
> The Linux kernel gained a generic cacheinfo reporting mechanism
> (drivers/base/cacheinfo.c) in v3.19, but arch/sparc's sysfs files
> were not unified into it, unfortunately.
> 
> Because the SPARC sysfs files convey no information about the shared
> nature of caches, lscpu (util-linux 2.42) overreports cache numbers.
> For example, on a T5240 machine:
> 
> ```
> Model name:            UltraSparc T2 (Niagara2)
>   Thread(s) per core:  4
>   Core(s) per socket:  16
>   Socket(s):           2
>   Flags:               sun4v
> Caches (sum of all):
>   L1d:                 1 MiB (128 instances)
>   L1i:                 2 MiB (128 instances)
>   L2:                  512 MiB (128 instances)
> ```
> 
> The SPARC port also lacked reporting L3 caches, present in the
> Oracle S3 and Fujitsu SPARC64 XII cores (machines: Oracle T4 and
> Fujitsu M12, respectively.)
> 
> On a SPARC S7-2:
> 
> ```
> Model name:            SPARC-S7 (Sonoma)
>   Thread(s) per core:  8
>   Core(s) per socket:  8
>   Socket(s):           2
>   Flags:               sun4v
> Caches (sum of all):
>   L1d:                 256 KiB (16 instances)
>   L1i:                 256 KiB (16 instances)
>   L2:                  2 MiB (8 instances)
> ```
> 
> This patch implements population of the sysfs cacheinfo structures
> for sun4v. The proper amount is now reflected in lscpu's output:
> 
> ```
> <<T5240>>
> Caches (sum of all):
>   L1d:                 128 KiB (16 instances)
>   L1i:                 256 KiB (16 instances)
>   L2:                  8 MiB (2 instances)
> 
> <<S7-2>>
>   L1d:                 256 KiB (16 instances)
>   L1i:                 256 KiB (16 instances)
>   L2d:                 2 MiB (8 instances)
>   L2i:                 1 MiB (4 instances)
>   L3:                  32 MiB (4 instances)
> ```
> ---
>  arch/sparc/include/asm/cpudata_64.h |   1 +
>  arch/sparc/include/asm/mdesc.h      |   2 +
>  arch/sparc/kernel/mdesc.c           | 136
> ++++++++++++++++++++++++++++
>  arch/sparc/kernel/prom_common.c     |  34 +++++++
>  4 files changed, 173 insertions(+)
> 
> diff --git a/arch/sparc/include/asm/cpudata_64.h
> b/arch/sparc/include/asm/cpudata_64.h
> index 056b3c0e7ef9..57b020ba93e0 100644
> --- a/arch/sparc/include/asm/cpudata_64.h
> +++ b/arch/sparc/include/asm/cpudata_64.h
> @@ -29,6 +29,7 @@ typedef struct {
>  	unsigned short	core_id;
>  	unsigned short  max_cache_id;	/* groupings of highest
> shared cache */
>  	signed short	proc_id;	/* strand (aka HW thread) id
> */
> +	u64             mdesc_node;
>  } cpuinfo_sparc;
>  
>  DECLARE_PER_CPU(cpuinfo_sparc, __cpu_data);
> diff --git a/arch/sparc/include/asm/mdesc.h
> b/arch/sparc/include/asm/mdesc.h
> index ec31a06d04cc..71e77641f158 100644
> --- a/arch/sparc/include/asm/mdesc.h
> +++ b/arch/sparc/include/asm/mdesc.h
> @@ -90,6 +90,8 @@ u64 mdesc_get_node(struct mdesc_handle *hp, const
> char *node_name,
>  int mdesc_get_node_info(struct mdesc_handle *hp, u64 node,
>  			const char *node_name, union md_node_info
> *node_info);
>  
> +int mdesc_cache_walk(unsigned int cpuid, bool fill_it);
> +
>  void mdesc_fill_in_cpu_data(cpumask_t *mask);
>  void mdesc_populate_present_mask(cpumask_t *mask);
>  void mdesc_get_page_sizes(cpumask_t *mask, unsigned long
> *pgsz_mask);
> diff --git a/arch/sparc/kernel/mdesc.c b/arch/sparc/kernel/mdesc.c
> index ecd6c8ae49c7..8b982c6fee11 100644
> --- a/arch/sparc/kernel/mdesc.c
> +++ b/arch/sparc/kernel/mdesc.c
> @@ -1195,6 +1195,12 @@ static void *fill_in_one_cpu(struct
> mdesc_handle *hp, u64 mp, int cpuid,
>  
>  	c = &cpu_data(cpuid);
>  	c->clock_tick = *cfreq;
> +	/*
> +	 * This will help more quickly finding the mdesc node again
> +	 * (otherwise we would have to iterate over all cpu nodes
> +	 * to locate any one with a given id).
> +	 */
> +	c->mdesc_node = mp;
>  
>  	tb = &trap_block[cpuid];
>  	get_mondo_data(hp, mp, tb);
> @@ -1242,6 +1248,136 @@ void mdesc_fill_in_cpu_data(cpumask_t *mask)
>  	smp_fill_in_sib_core_maps();
>  }
>  
> +/**
> + * At MDESC node @node, analyze forward links to find the
> @want_target,
> + * and, if @want_type is not %NULL, that it is of the requested
> type.
> + */
> +static u64 mdesc_find_subobject(struct mdesc_handle *mdh, u64
> iu_node,
> +				const char *want_target, const char
> *want_type)
> +{
> +	u64 iter;
> +
> +	mdesc_for_each_arc(iter, mdh, iu_node, MDESC_ARC_TYPE_FWD) {
> +		u64 sub_node = mdesc_arc_target(mdh, iter);
> +		const char *sub_name = mdesc_node_name(mdh,
> sub_node);
> +		const char *type_list;
> +		int len;
> +
> +		if (strcmp(sub_name, want_target) != 0)
> +			continue;
> +		if (want_type == NULL)
> +			return sub_node;
> +		type_list = mdesc_get_property(mdh, sub_node,
> "type", &len);
> +		if (of_find_in_proplist(type_list, want_type, len))
> +			return sub_node;
> +	}
> +	return -1;
> +}
> +
> +static void mdesc_cache_fill(struct mdesc_handle *mdh, u64
> cache_node,
> +			     struct cacheinfo *leaf)
> +{
> +	int type_len;
> +	const u64 *level      = mdesc_get_property(mdh, cache_node,
> "level", NULL);
> +	const u64 *size       = mdesc_get_property(mdh, cache_node,
> "size", NULL);
> +	const u64 *line_size  = mdesc_get_property(mdh, cache_node,
> "line-size", NULL);
> +	const u64 *assoc      = mdesc_get_property(mdh, cache_node,
> "associativity", NULL);
> +	const char *type_list = mdesc_get_property(mdh, cache_node,
> "type", &type_len);
> +
> +	leaf->fw_token = (void *)(uintptr_t)cache_node;
> +	leaf->level = *level;
> +	if (type_list != NULL) {
> +		bool i = of_find_in_proplist(type_list, "instn",
> type_len);
> +		bool d = of_find_in_proplist(type_list, "data",
> type_len);
> +		if (i && d)
> +			leaf->type = CACHE_TYPE_UNIFIED;
> +		else if (i)
> +			leaf->type = CACHE_TYPE_INST;
> +		else if (d)
> +			leaf->type = CACHE_TYPE_DATA;
> +	}
> +	if (size != NULL)
> +		leaf->size = *size;
> +	if (line_size != NULL)
> +		leaf->coherency_line_size = *line_size;
> +	if (assoc != NULL)
> +		leaf->ways_of_associativity = *assoc;
> +	if (leaf->coherency_line_size != 0 && leaf-
> >ways_of_associativity != 0)
> +		leaf->number_of_sets = leaf->size / leaf-
> >coherency_line_size /
> +				       leaf->ways_of_associativity;
> +}
> +
> +static int mdesc_cache_record(struct mdesc_handle *mdh, u64
> cache_node,
> +			      unsigned int cpuid, unsigned int
> *leaf_count,
> +			      bool fill_it)
> +{
> +	struct cpu_cacheinfo *ci;
> +	const u64 *level = mdesc_get_property(mdh, cache_node,
> "level", NULL);
> +	u64 iter;
> +
> +	if (level == NULL)
> +		return -ENOENT;
> +	ci = get_cpu_cacheinfo(cpuid);
> +	if (fill_it) {
> +		if (*leaf_count >= ci->num_leaves)
> +			return -ELOOP;
> +		mdesc_cache_fill(mdh, cache_node,
> +				 &ci->info_list[(*leaf_count)++]);
> +	} else {
> +		++ci->num_leaves;
> +		if (ci->num_levels < *level)
> +			ci->num_levels = *level;
> +	}
> +
> +	mdesc_for_each_arc(iter, mdh, cache_node,
> MDESC_ARC_TYPE_FWD) {
> +		u64 sub_node = mdesc_arc_target(mdh, iter);
> +		const char *sub_name = mdesc_node_name(mdh,
> sub_node);
> +		int ret;
> +
> +		if (strcmp(sub_name, "cache") != 0)
> +			continue;
> +		ret = mdesc_cache_record(mdh, sub_node, cpuid,
> +					 leaf_count, fill_it);
> +		if (ret != 0)
> +			return ret;
> +	}
> +	return 0;
> +}
> +
> +int mdesc_cache_walk(unsigned int cpuid, bool fill_it)
> +{
> +	/*
> +	 * ifetch is where instruction caches are reported under.
> +	 * integer is where data cahes are reported under.
> +	 *
> +	 * The MDESCs of e.g. the SUNW,T5240 and ORCL,SPARC-S7-2
> machines
> +	 * use "integer". Which machines are using "int"?
> +	 */
> +	static const char *const unit_kinds[] = {"ifetch",
> "integer", "int"};
> +	unsigned int leaf_count = 0;
> +	int ret = 0;
> +	struct mdesc_handle *mdh = mdesc_grab();
> +
> +	if (mdh == NULL)
> +		return -ENOENT;
> +
> +	for (unsigned int i = 0; i < ARRAY_SIZE(unit_kinds); ++i) {
> +		u64 node = mdesc_find_subobject(mdh,
> cpu_data(cpuid).mdesc_node,
> +						"exec-unit",
> unit_kinds[i]);
> +		if (node == -1)
> +			continue;
> +		node = mdesc_find_subobject(mdh, node, "cache",
> NULL);
> +		if (node == -1)
> +			continue;
> +		ret = mdesc_cache_record(mdh, node, cpuid,
> +					 &leaf_count, fill_it);
> +		if (ret != 0)
> +			break;
> +	}
> +	mdesc_release(mdh);
> +	return ret;
> +}
> +
>  /* mdesc_open() - Grab a reference to mdesc_handle when /dev/mdesc
> is
>   * opened. Hold this reference until /dev/mdesc is closed to ensure
>   * mdesc data structure is not released underneath us. Store the
> diff --git a/arch/sparc/kernel/prom_common.c
> b/arch/sparc/kernel/prom_common.c
> index d258fd10db01..34f3b2650547 100644
> --- a/arch/sparc/kernel/prom_common.c
> +++ b/arch/sparc/kernel/prom_common.c
> @@ -11,12 +11,14 @@
>   */
>  
>  #include <linux/kernel.h>
> +#include <linux/cacheinfo.h>
>  #include <linux/export.h>
>  #include <linux/errno.h>
>  #include <linux/mutex.h>
>  #include <linux/slab.h>
>  #include <linux/of.h>
>  #include <linux/of_pdt.h>
> +#include <asm/mdesc.h>
>  #include <asm/prom.h>
>  #include <asm/oplib.h>
>  
> @@ -112,6 +114,38 @@ int of_find_in_proplist(const char *list, const
> char *match, int len)
>  }
>  EXPORT_SYMBOL(of_find_in_proplist);
>  
> +int init_cache_level(unsigned int cpuid)
> +{
> +#ifdef CONFIG_SPARC64
> +	if (tlb_type == hypervisor)
> +		return mdesc_cache_walk(cpuid, false);
> +#endif
> +	return -ENOENT;
> +}
> +
> +int populate_cache_leaves(unsigned int cpuid)
> +{
> +	/*
> +	 * Note that drivers/base/cacheinfo expects leaves to be
> sorted by
> +	 * level, then by index in [inst, data_or_unified]. Code
> should fill
> +	 * in leaves in that order (else, a call to qsort will be
> needed).
> +	 */
> +	int ret = -ENOENT;
> +	switch (tlb_type) {
> +#ifdef CONFIG_SPARC64
> +	case hypervisor:
> +		ret = mdesc_cache_walk(cpuid, true);
> +		break;
> +#endif
> +	default:
> +		break;
> +	}
> +	if (ret == -ELOOP)
> +		pr_err("%s: tried to populate too much. "
> +		       "(Programming error, go investigate.)\n",
> __func__);
> +	return ret;
> +}
> +
>  /*
>   * SPARC32 and SPARC64's prom_nextprop() do things differently
>   * here, despite sharing the same interface.  SPARC32 doesn't fill
> in 'buf',

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] sparc: populate sysfs cache topology information on sun4v
  2026-10-26 20:16 ` Stian Halseth
@ 2026-10-26 21:27   ` Jan Engelhardt
  2026-10-26 22:57     ` Stian Halseth
  0 siblings, 1 reply; 15+ messages in thread
From: Jan Engelhardt @ 2026-10-26 21:27 UTC (permalink / raw)
  To: Stian Halseth; +Cc: sparclinux


On Monday 2026-10-26 21:16, Stian Halseth wrote:
>
>I ran the walk against the MDs on my T4-1 and T7-1 [..]
>
>Some smaller things:
>
>- Two exec-units have "integer" in their type, and only the first has
>  the L1d arc.

Which two? I have 16 of them.
(If you want, you can send me a directed mail with the contents from
/dev/mdesc and maybe it will enlighten me.)


>  Following the cache arcs of any child, like
>  fill_in_one_cpu() does, avoids that

But we do follow any child. (Well, of all the EUs that were whitelisted
to be interesting types. Going down a "cwq" crypto executor node seemed
unnecessary.)

+       for (unsigned int i = 0; i < ARRAY_SIZE(unit_kinds); ++i) {
+               u64 node = mdesc_find_subobject(mdh, cpu_data(cpuid).mdesc_node,
+                                               "exec-unit", unit_kinds[i]);
+               if (node == -1)
+                       continue;
+               node = mdesc_find_subobject(mdh, node, "cache", NULL);
+               if (node == -1)


>  and the "int" question.

"int" was there in the old code (since commit
f78eae2e6f5d1eb05f76a45486286445b916bd92), I just took it.


>- mdesc_find_subobject() passes NULL and an uninitialized len on if
>  there is no "type" property.

This too was rooted in old code which assumed that "type" always
exists.

->8-
        type = mdesc_get_property(hp, mp, "type", &type_len);

        switch (*level) {
        case 1:
                if (of_find_in_proplist(type, "instn", type_len)) {
-8<-


>- switch (tlb_type) breaks sparc32, since prom_common.c is built there
>  too. Both functions could just live in mdesc.c.
>- Signed-off-by is missing, and a v2 tag with a short changelog would
>  help.

I added one in the first try; that's the one that did not make it to
the list (or my disk). git-send-email is just too stateless for my
taste.

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] sparc: populate sysfs cache topology information on sun4v
  2026-10-26 21:27   ` Jan Engelhardt
@ 2026-10-26 22:57     ` Stian Halseth
  0 siblings, 0 replies; 15+ messages in thread
From: Stian Halseth @ 2026-10-26 22:57 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: sparclinux

[-- Attachment #1: Type: text/plain, Size: 1218 bytes --]

Hi Jan,

On Mon, 2026-10-26 at 22:27 +0100, Jan Engelhardt wrote:

> Which two? I have 16 of them.

Two per cpu node. On my T4-1 and T7-1 every cpu node has three
exec-unit children:

  ifetch                                               -> L1i
  integer,integer-load,integer-store,fp-load,fp-store  -> L1d
  fp,integer,vis,crypto                                -> no cache

Both of the last two match "integer", and mdesc_find_subobject()
returns the first one, which is the one with the L1d on both boxes. I
don't know if any firmware actually orders them differently, so it
might not be worth worrying too much about. 


> (If you want, you can send me a directed mail with the contents from
> /dev/mdesc and maybe it will enlighten me.)

Let me know if you still need it, or if you have all you need in the
table above.

> 
> "int" was there in the old code (since commit
> f78eae2e6f5d1eb05f76a45486286445b916bd92), I just took it.
> 
> 
> > - mdesc_find_subobject() passes NULL and an uninitialized len on if
> >  there is no "type" property.
> 
> This too was rooted in old code which assumed that "type" always
> exists.
> 

Ok, those are probably fine then.

/Stian


[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2026-10-26 22:57 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-26 19:34 [PATCH] sparc: populate sysfs cache topology information on sun4v Jan Engelhardt
2026-10-26 19:55 ` sashiko-bot
2026-10-26 20:16 ` Stian Halseth
2026-10-26 21:27   ` Jan Engelhardt
2026-10-26 22:57     ` Stian Halseth
     [not found] <X-TH#1.20261018222723.43442-1-jengelh@inai.de>
2026-10-26 18:52 ` Tony Rodriguez
2026-10-26 19:18   ` Tony Rodriguez
  -- strict thread matches above, loose matches on Subject: below --
2026-10-18 22:27 Jan Engelhardt
2026-10-19  0:16 ` Tony Rodriguez
2026-10-19  0:37   ` Tony Rodriguez
2026-10-19  2:32     ` Tony Rodriguez
     [not found]     ` <c7d7a96c-feb9-4676-8cc2-73529e8324ef@gmail.com>
2026-10-19  2:42       ` Tony Rodriguez
2026-10-19  9:49 ` Stian Halseth
2026-10-22 16:17   ` Jan Engelhardt
2026-10-22 16:55     ` Stian Halseth

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.