All of lore.kernel.org
 help / color / mirror / Atom feed
* [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
[parent not found: <X-TH#1.20261018222723.43442-1-jengelh@inai.de>]
* [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

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.