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

From: Tony Rodriguez

Date: Sun Oct 18 2026 - 20:37:26 EST


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

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