Re: [PATCH] sparc: populate sysfs cache topology information on sun4v

From: Tony Rodriguez

Date: Mon Oct 26 2026 - 14:54:41 EST


Tested-by: Tony Rodriguez <unixpro1970@xxxxxxxxx>

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@xxxxxxx> 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@xxxxxxx>
> ---
> 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
>
>