* [PATCH v3] sparc: populate sysfs cache topology information on sun4v
@ 2026-10-28 1:00 Jan Engelhardt
2026-10-28 1:21 ` sashiko-bot
` (2 more replies)
0 siblings, 3 replies; 6+ messages in thread
From: Jan Engelhardt @ 2026-10-28 1:00 UTC (permalink / raw)
To: andreas; +Cc: sparclinux, stian, unixpro1970, glaubitz
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
onwards 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)
```
---
v3:
//current
* Resolved the issue that only the first cache of any integer unit
was looked at.
* Resolved the issue that only the first integer unit of any strand
was looked at.
* Resolved the issue that only integer units were considered for caches.
* No more duplicated cache entries under
/sys/devices/system/cpu/cpu0/cache/indexN.
* No longer keep unstable MDESC node ids around past their lifetime.
v2:
//Message-ID: <20261026193421.20404-1-jengelh@inai.de>
* Rewalk MDESC rather than sourcing cache info from struct cpuinfo_sparc.
* Support L3 caches found on SPARC T4 and later machine models.
v1:
//Message-ID: <20261018222723.43442-1-jengelh@inai.de>
arch/sparc/include/asm/cpudata_64.h | 1 +
arch/sparc/include/asm/mdesc.h | 2 +
arch/sparc/kernel/mdesc.c | 215 ++++++++++++++++++++++++++++
arch/sparc/kernel/prom_common.c | 53 +++++++
4 files changed, 271 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..0e400b451b4b 100644
--- a/arch/sparc/kernel/mdesc.c
+++ b/arch/sparc/kernel/mdesc.c
@@ -11,6 +11,7 @@
#include <linux/mm.h>
#include <linux/miscdevice.h>
#include <linux/memblock.h>
+#include <linux/cacheinfo.h>
#include <linux/export.h>
#include <linux/refcount.h>
@@ -1242,6 +1243,220 @@ void mdesc_fill_in_cpu_data(cpumask_t *mask)
smp_fill_in_sib_core_maps();
}
+/**
+ * Return the MDESC node for a "cpu" object whose "id" property
+ * matches @cpuid.
+ */
+static u64 mdesc_find_cpu(struct mdesc_handle *mdh, int cpuid)
+{
+ u64 iter;
+ mdesc_for_each_node_by_name(mdh, iter, "cpu") {
+ const u64 *id = mdesc_get_property(mdh, iter, "id", NULL);
+ if (id != NULL && *id == cpuid)
+ return iter;
+ }
+ return MDESC_NODE_NULL;
+}
+
+/**
+ * Obtain the properties of a given MDESC cache object node and fill in the
+ * Linux representation (struct cacheinfo) for it.
+ */
+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);
+
+ /*
+ * fw_token is only temporary. It will be cleared again before the
+ * MDESC is unpinned.
+ */
+ 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;
+}
+
+/**
+ * Populate the sharedness bitmap of a given struct cacheinfo by walking up the
+ * cache chain again and finding all CPUs.
+ */
+static void mdesc_cache_set_shared(struct mdesc_handle *mdh, u64 cache_node,
+ struct cacheinfo *leaf)
+{
+ unsigned int maxcpus = num_possible_cpus();
+ u64 sup_iter, cpu_iter;
+
+ mdesc_for_each_arc(sup_iter, mdh, cache_node, MDESC_ARC_TYPE_BACK) {
+ u64 sup_node = mdesc_arc_target(mdh, sup_iter);
+ const char *tg_name = mdesc_node_name(mdh, sup_node);
+
+ if (strcmp(tg_name, "cache") == 0)
+ mdesc_cache_set_shared(mdh, sup_node, leaf);
+ if (strcmp(tg_name, "exec-unit") != 0)
+ continue;
+
+ mdesc_for_each_arc(cpu_iter, mdh, sup_node, MDESC_ARC_TYPE_BACK) {
+ u64 cpu_node = mdesc_arc_target(mdh, cpu_iter);
+ tg_name = mdesc_node_name(mdh, cpu_node);
+
+ if (strcmp(tg_name, "cpu") != 0)
+ continue;
+ const u64 *id = mdesc_get_property(mdh, cpu_node, "id", NULL);
+ if (id == NULL)
+ continue;
+ if (id != NULL && *id < maxcpus)
+ cpumask_set_cpu(*id, &leaf->shared_cpu_map);
+ }
+ }
+}
+
+static bool seen_cache(const struct cacheinfo *leaf, size_t n, u64 node)
+{
+ while (n-- > 0)
+ if ((uintptr_t)leaf[n].fw_token == node)
+ return true;
+ return false;
+}
+
+/**
+ * For a given MDESC "cache" object node, copy properties, and recursively
+ * search for lower-level caches.
+ */
+static int mdesc_cache_record(struct mdesc_handle *mdh, u64 cache_node,
+ unsigned int cpuid, unsigned int *leaf_count,
+ bool fill_it)
+{
+ const u64 *level = mdesc_get_property(mdh, cache_node, "level", NULL);
+ struct cpu_cacheinfo *cc;
+ u64 iter;
+
+ if (level == NULL)
+ return -ENOENT;
+ cc = get_cpu_cacheinfo(cpuid);
+ if (fill_it) {
+ if (*leaf_count >= cc->num_leaves) {
+ /*
+ * 1. Sudden MDESC change between init_cache_level
+ * and populate_cache_leaves, or 2. programming
+ * errors.
+ */
+ pr_err("%s: tried to populate more cacheinfos than "
+ "allocated.\n", __func__);
+ return -ELOOP;
+ }
+ if (!seen_cache(cc->info_list, *leaf_count, cache_node)) {
+ struct cacheinfo *ci = &cc->info_list[(*leaf_count)++];
+ mdesc_cache_fill(mdh, cache_node, ci);
+ mdesc_cache_set_shared(mdh, cache_node, ci);
+ cc->cpu_map_populated = true;
+ }
+ } else {
+ ++cc->num_leaves;
+ if (cc->num_levels < *level)
+ cc->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;
+}
+
+/**
+ * For a given Linux CPU @cpuid (hardware strand), find the corresponding "cpu"
+ * object node in the MDESC, and from there follow pointers to "exec-unit"
+ * object nodes and, subsequently, "cache" object nodes.
+ */
+int mdesc_cache_walk(unsigned int cpuid, bool fill_it)
+{
+ unsigned int leaf_count = 0;
+ struct mdesc_handle *mdh = mdesc_grab();
+ u64 cpu_node, eu_arc;
+ int ret = 0;
+
+ if (mdh == NULL)
+ return -ENOENT;
+
+#ifndef CONFIG_SMP
+ /*
+ * On UP, cpu_data() has just one entry with index 0, but we want
+ * the values from the real physical cpu the kernel booted on.
+ */
+ cpuid = real_hard_smp_processor_id();
+#endif
+
+ cpu_node = mdesc_find_cpu(mdh, cpuid);
+ if (cpu_node == MDESC_NODE_NULL) {
+ mdesc_release(mdh);
+ return -ENOENT;
+ }
+
+ mdesc_for_each_arc(eu_arc, mdh, cpu_node, MDESC_ARC_TYPE_FWD) {
+ u64 cache_arc;
+ u64 eu_node = mdesc_arc_target(mdh, eu_arc);
+ const char *node_name = mdesc_node_name(mdh, eu_node);
+
+ if (strcmp(node_name, "exec-unit") != 0)
+ continue;
+
+ mdesc_for_each_arc(cache_arc, mdh, eu_node, MDESC_ARC_TYPE_FWD) {
+ u64 cache_node = mdesc_arc_target(mdh, cache_arc);
+ node_name = mdesc_node_name(mdh, cache_node);
+ if (strcmp(node_name, "cache") != 0)
+ continue;
+ ret = mdesc_cache_record(mdh, cache_node, cpuid,
+ &leaf_count, fill_it);
+ if (ret != 0)
+ goto out;
+ }
+ }
+
+ if (fill_it) {
+ /* Reduce count to actually populated slots. */
+ struct cpu_cacheinfo *cc = get_cpu_cacheinfo(cpuid);
+ cc->num_leaves = leaf_count;
+
+ /* We have the sharedness bitmaps and don't need fw_token. */
+ for (size_t i = 0; i < cc->num_leaves; ++i)
+ cc->info_list[i].fw_token = NULL;
+ }
+ out:
+ 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..808dc044514f 100644
--- a/arch/sparc/kernel/prom_common.c
+++ b/arch/sparc/kernel/prom_common.c
@@ -11,12 +11,15 @@
*/
#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 <linux/sort.h>
+#include <asm/mdesc.h>
#include <asm/prom.h>
#include <asm/oplib.h>
@@ -112,6 +115,56 @@ int of_find_in_proplist(const char *list, const char *match, int len)
}
EXPORT_SYMBOL(of_find_in_proplist);
+/**
+ * Called from from drivers/base/cacheinfo.c meant to set
+ * cpu_cacheinfo::num_leaves for allocation purposes.
+ */
+int init_cache_level(unsigned int cpuid)
+{
+#ifdef CONFIG_SPARC64
+ if (tlb_type == hypervisor)
+ return mdesc_cache_walk(cpuid, false);
+#endif
+ return -ENOENT;
+}
+
+static int cache_leaf_compare(const void *va, const void *vb)
+{
+ const struct cacheinfo *a = va, *b = vb;
+ int r = cmp_int(a->level, b->level);
+ return r != 0 ? r : cmp_int(a->type, b->type);
+}
+
+static void cache_leaf_swap(void *va, void *vb, int size)
+{
+ struct cacheinfo c;
+ memcpy(&c, va, size);
+ memcpy(va, vb, size);
+ memcpy(vb, &c, size);
+}
+
+int populate_cache_leaves(unsigned int cpuid)
+{
+ int ret;
+#ifdef CONFIG_SPARC64
+ if (tlb_type == hypervisor) {
+ ret = mdesc_cache_walk(cpuid, true);
+ }
+ else
+#endif
+ {
+ /* Room to put the call for an OF-based cache walk here */
+ ret = -ENOENT;
+ }
+ if (ret == 0) {
+ /* cacheinfo.c wants the last item to be the farthest cache */
+ struct cpu_cacheinfo *ci = get_cpu_cacheinfo(cpuid);
+ sort(ci->info_list, ci->num_leaves, sizeof(struct cacheinfo),
+ cache_leaf_compare, cache_leaf_swap);
+ }
+ 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] 6+ messages in thread
* Re: [PATCH v3] sparc: populate sysfs cache topology information on sun4v
2026-10-28 1:00 [PATCH v3] sparc: populate sysfs cache topology information on sun4v Jan Engelhardt
@ 2026-10-28 1:21 ` sashiko-bot
2026-10-28 2:00 ` Tony Rodriguez
2026-10-28 10:29 ` Stian Halseth
2 siblings, 0 replies; 6+ messages in thread
From: sashiko-bot @ 2026-10-28 1:21 UTC (permalink / raw)
To: Jan Engelhardt; +Cc: sparclinux
Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider:
- [High] sparc: mdesc_cache_walk() leaves invalid fw_token on error path
- [High] sparc: undeclared cmp_int() and C99 declarations break build
- [Medium] sparc: mdesc_cache_walk() corrupts cacheinfo state across CPU hotplug
- [Medium] sparc: mdesc_cache_walk() misses direct cache arcs and exec_unit nodes
- [Medium] sparc: mdesc_cache_walk() breaks shared_cpu_map on !CONFIG_SMP
- [Low] sparc: unused mdesc_node field added to struct cpuinfo_sparc
--
commit 3b2a900d7b27574052192de660cf52382a5087a9
Author: Jan Engelhardt <jengelh@inai.de>
sparc: populate sysfs cache topology information on sun4v
This patch implements init_cache_level() and populate_cache_leaves() on
sun4v by walking the Machine Description (MDESC) graph to populate generic
sysfs cacheinfo attributes and shared CPU maps.
> diff --git a/arch/sparc/include/asm/cpudata_64.h b/arch/sparc/include/asm/cpudata_64.h
> index 9c3fc03abe9ae..3b79fb274f716 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;
[Severity: Low]
Is this mdesc_node field in cpuinfo_sparc still needed? Nothing in the
patch reads or writes it, and adding 8 bytes here pushes cpuinfo_sparc
from 64 bytes (two 32-byte L1 D-cache lines) to 72 bytes per CPU.
[ ... ]
> diff --git a/arch/sparc/kernel/mdesc.c b/arch/sparc/kernel/mdesc.c
> index 8e645ddac58e2..420ed85856023 100644
> --- a/arch/sparc/kernel/mdesc.c
> +++ b/arch/sparc/kernel/mdesc.c
[ ... ]
> +static void mdesc_cache_set_shared(struct mdesc_handle *mdh, u64 cache_node,
> + struct cacheinfo *leaf)
> +{
> + unsigned int maxcpus = num_possible_cpus();
> + u64 sup_iter, cpu_iter;
> +
> + mdesc_for_each_arc(sup_iter, mdh, cache_node, MDESC_ARC_TYPE_BACK) {
> + u64 sup_node = mdesc_arc_target(mdh, sup_iter);
> + const char *tg_name = mdesc_node_name(mdh, sup_node);
> +
> + if (strcmp(tg_name, "cache") == 0)
> + mdesc_cache_set_shared(mdh, sup_node, leaf);
> + if (strcmp(tg_name, "exec-unit") != 0)
> + continue;
> +
> + mdesc_for_each_arc(cpu_iter, mdh, sup_node, MDESC_ARC_TYPE_BACK) {
> + u64 cpu_node = mdesc_arc_target(mdh, cpu_iter);
> + tg_name = mdesc_node_name(mdh, cpu_node);
> +
> + if (strcmp(tg_name, "cpu") != 0)
> + continue;
> + const u64 *id = mdesc_get_property(mdh, cpu_node, "id", NULL);
> + if (id == NULL)
> + continue;
> + if (id != NULL && *id < maxcpus)
> + cpumask_set_cpu(*id, &leaf->shared_cpu_map);
> + }
> + }
> +}
[Severity: Medium]
Can mdesc_cache_set_shared() leave leaf->shared_cpu_map empty on
!CONFIG_SMP kernels when booted on a non-zero physical CPU?
In mdesc_cache_walk(), under #ifndef CONFIG_SMP, cpuid is set to
real_hard_smp_processor_id() so mdesc_find_cpu() locates the physical
boot CPU. However, in mdesc_cache_set_shared(), maxcpus is
num_possible_cpus(), which is 1U on !CONFIG_SMP, and the physical MDESC
CPU id is checked against maxcpus:
if (id != NULL && *id < maxcpus)
cpumask_set_cpu(*id, &leaf->shared_cpu_map);
When the physical boot CPU has a hardware strand ID >= 1, *id < 1 is
false, so bit 0 is never set in leaf->shared_cpu_map. Because
mdesc_cache_record() sets cc->cpu_map_populated = true,
cache_shared_cpu_map_setup() in drivers/base/cacheinfo.c returns early
without setting bit 0 either, hiding shared_cpu_map and shared_cpu_list
in sysfs for any cache not shared with physical strand 0.
[ ... ]
> +static int mdesc_cache_record(struct mdesc_handle *mdh, u64 cache_node,
> + unsigned int cpuid, unsigned int *leaf_count,
> + bool fill_it)
> +{
> + const u64 *level = mdesc_get_property(mdh, cache_node, "level", NULL);
> + struct cpu_cacheinfo *cc;
> + u64 iter;
> +
> + if (level == NULL)
> + return -ENOENT;
> + cc = get_cpu_cacheinfo(cpuid);
> + if (fill_it) {
> + if (*leaf_count >= cc->num_leaves) {
> + /*
> + * 1. Sudden MDESC change between init_cache_level
> + * and populate_cache_leaves, or 2. programming
> + * errors.
> + */
> + pr_err("%s: tried to populate more cacheinfos than "
> + "allocated.\n", __func__);
> + return -ELOOP;
> + }
> + if (!seen_cache(cc->info_list, *leaf_count, cache_node)) {
> + struct cacheinfo *ci = &cc->info_list[(*leaf_count)++];
> + mdesc_cache_fill(mdh, cache_node, ci);
> + mdesc_cache_set_shared(mdh, cache_node, ci);
> + cc->cpu_map_populated = true;
> + }
> + } else {
> + ++cc->num_leaves;
> + if (cc->num_levels < *level)
> + cc->num_levels = *level;
> + }
[Severity: Medium]
Does this corrupt cc->num_leaves and sibling shared_cpu_map bitmasks
across CPU offline/online cycles or LDOM dynamic reconfiguration?
When a CPU goes offline:
cacheinfo_cpu_pre_down()
-> free_cache_attributes()
-> cache_shared_cpu_map_remove()
-> kfree(per_cpu_cacheinfo(cpu))
free_cache_attributes() frees info_list, but does not reset
cc->num_leaves or cc->num_levels to 0. When the CPU is brought back
online and init_cache_level() calls mdesc_cache_walk(cpuid, false),
++cc->num_leaves increments on top of the previous value instead of
starting from 0.
In addition, cache_shared_cpu_map_remove() clears the offlined CPU from
each online sibling's sib_leaf->shared_cpu_map:
drivers/base/cacheinfo.c:cache_shared_cpu_map_remove() {
...
sib_leaf = sib_cpu_ci->info_list + index;
cpumask_clear_cpu(cpu, &sib_leaf->shared_cpu_map);
cpumask_clear_cpu(sibling, &this_leaf->shared_cpu_map);
...
}
When the CPU comes back online, mdesc_cache_set_shared() only sets bits
in the current CPU's ci->shared_cpu_map (including offline CPUs present
in the MDESC) without restoring the CPU's bit in online siblings'
sib_leaf->shared_cpu_map, and setting cc->cpu_map_populated = true causes
cache_shared_cpu_map_setup() in drivers/base/cacheinfo.c to skip updating
the sibling maps as well.
Also, in the fill_it branch above, *leaf_count >= cc->num_leaves is
checked before seen_cache(cc->info_list, *leaf_count, cache_node), and
when seen_cache() returns true the function still recurses through all
forward arcs of that cache node.
[ ... ]
> + mdesc_for_each_arc(eu_arc, mdh, cpu_node, MDESC_ARC_TYPE_FWD) {
> + u64 cache_arc;
> + u64 eu_node = mdesc_arc_target(mdh, eu_arc);
> + const char *node_name = mdesc_node_name(mdh, eu_node);
> +
> + if (strcmp(node_name, "exec-unit") != 0)
> + continue;
> +
> + mdesc_for_each_arc(cache_arc, mdh, eu_node, MDESC_ARC_TYPE_FWD) {
> + u64 cache_node = mdesc_arc_target(mdh, cache_arc);
> + node_name = mdesc_node_name(mdh, cache_node);
> + if (strcmp(node_name, "cache") != 0)
> + continue;
> + ret = mdesc_cache_record(mdh, cache_node, cpuid,
> + &leaf_count, fill_it);
> + if (ret != 0)
> + goto out;
> + }
> + }
[Severity: Medium]
Will mdesc_cache_walk() and mdesc_cache_set_shared() miss caches on
sun4v machines whose MDESC uses direct cpu-to-cache arcs or names
execution unit nodes "exec_unit"?
Earlier in arch/sparc/kernel/mdesc.c, set_proc_ids() checks for both
spelling variants:
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() also handles direct forward arcs from "cpu" to
"cache" nodes (such as on UltraSPARC T1) in addition to going through
execution unit nodes:
t_name = mdesc_node_name(hp, t);
if (!strcmp(t_name, "cache")) {
fill_in_one_cache(c, hp, t);
continue;
}
Because mdesc_cache_walk() and mdesc_cache_set_shared() only follow
nodes named "exec-unit", platforms using direct "cache" arcs or
"exec_unit" nodes will find 0 cache leaves (failing with -ENOENT in
detect_cache_attributes()) or fail to populate leaf->shared_cpu_map.
> +
> + if (fill_it) {
> + /* Reduce count to actually populated slots. */
> + struct cpu_cacheinfo *cc = get_cpu_cacheinfo(cpuid);
> + cc->num_leaves = leaf_count;
> +
> + /* We have the sharedness bitmaps and don't need fw_token. */
> + for (size_t i = 0; i < cc->num_leaves; ++i)
> + cc->info_list[i].fw_token = NULL;
> + }
> + out:
> + mdesc_release(mdh);
> + return ret;
> +}
[Severity: High]
If mdesc_cache_record() fails partway through when fill_it is true and
jumps to out, can the uncleared fw_token values cause an invalid pointer
dereference in of_node_put()?
During the fill_it pass, mdesc_cache_fill() stores the raw integer MDESC
node index into leaf->fw_token:
leaf->fw_token = (void *)(uintptr_t)cache_node;
If a subsequent call to mdesc_cache_record() returns an error (such as
-ENOENT if a cache node is missing a "level" property, or -ELOOP if the
MDESC changed between init_cache_level() and populate_cache_leaves()),
goto out skips clearing cc->info_list[i].fw_token and skips updating
cc->num_leaves.
When populate_cache_leaves() returns that error to
detect_cache_attributes() in drivers/base/cacheinfo.c, it runs:
detect_cache_attributes()
-> free_cache_attributes()
-> cache_shared_cpu_map_remove()
In cache_shared_cpu_map_remove():
sib_leaf = sib_cpu_ci->info_list + index;
cpumask_clear_cpu(cpu, &sib_leaf->shared_cpu_map);
cpumask_clear_cpu(sibling, &this_leaf->shared_cpu_map);
}
if (of_have_populated_dt())
of_node_put(this_leaf->fw_token);
Because SPARC populates of_root during boot in prom_build_devicetree(),
of_have_populated_dt() is true, so of_node_put() will be called with the
non-NULL integer MDESC node index cast to a pointer. Furthermore, because
cc->num_leaves was not reduced to leaf_count and cc->info_list was not
sorted, cache_shared_cpu_map_remove() can index past the end of
sib_cpu_ci->info_list or clear bits in mismatched cache leaves on online
sibling CPUs.
> diff --git a/arch/sparc/kernel/prom_common.c b/arch/sparc/kernel/prom_common.c
> index c9ec70888a394..137d84b89e483 100644
> --- a/arch/sparc/kernel/prom_common.c
> +++ b/arch/sparc/kernel/prom_common.c
[ ... ]
> +static int cache_leaf_compare(const void *va, const void *vb)
> +{
> + const struct cacheinfo *a = va, *b = vb;
> + int r = cmp_int(a->level, b->level);
> + return r != 0 ? r : cmp_int(a->type, b->type);
> +}
[Severity: High]
Does this build cleanly?
cmp_int() is not declared or defined in the kernel headers or in this
file, which fails compilation of arch/sparc/kernel/prom_common.c with
-Werror=implicit-function-declaration.
In addition, arch/sparc/kernel/mdesc.c fails to build under the kernel's
-std=gnu89, -Wdeclaration-after-statement, and arch/sparc/kernel/Makefile
ccflags-y := -Werror flags due to the mixed declaration in
mdesc_cache_set_shared():
if (strcmp(tg_name, "cpu") != 0)
continue;
const u64 *id = mdesc_get_property(mdh, cpu_node, "id", NULL);
and the C99 loop variable declaration in mdesc_cache_walk():
for (size_t i = 0; i < cc->num_leaves; ++i)
cc->info_list[i].fw_token = NULL;
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261028010100.24792-1-jengelh@inai.de?part=1
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v3] sparc: populate sysfs cache topology information on sun4v
2026-10-28 1:00 [PATCH v3] sparc: populate sysfs cache topology information on sun4v Jan Engelhardt
2026-10-28 1:21 ` sashiko-bot
@ 2026-10-28 2:00 ` Tony Rodriguez
2026-10-28 2:20 ` Tony Rodriguez
2026-10-28 8:17 ` Jan Engelhardt
2026-10-28 10:29 ` Stian Halseth
2 siblings, 2 replies; 6+ messages in thread
From: Tony Rodriguez @ 2026-10-28 2:00 UTC (permalink / raw)
To: Jan Engelhardt, andreas; +Cc: sparclinux, stian, glaubitz
Hi Jan,
On 10/27/26 6:00 PM, Jan Engelhardt wrote:
> The SPARC port also lacked reporting L3 caches, present in the
> Oracle S3 and Fujitsu SPARC64 XII cores (machines: Oracle T4
> onwards and Fujitsu M12, respectively.)
This seems like a typo. Instead of "Oracle S3" shouldn't this be
"Oracle S7"?
Regards,
Tony
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v3] sparc: populate sysfs cache topology information on sun4v
2026-10-28 2:00 ` Tony Rodriguez
@ 2026-10-28 2:20 ` Tony Rodriguez
2026-10-28 8:17 ` Jan Engelhardt
1 sibling, 0 replies; 6+ messages in thread
From: Tony Rodriguez @ 2026-10-28 2:20 UTC (permalink / raw)
To: Jan Engelhardt, andreas; +Cc: sparclinux, stian, glaubitz
Hi Jan,
On 10/27/26 7:00 PM, Tony Rodriguez wrote:
>
> On 10/27/26 6:00 PM, Jan Engelhardt wrote:
>> The SPARC port also lacked reporting L3 caches, present in
>> Oracle S3 and Fujitsu SPARC64 XII cores (machines: Oracle T4
>> onwards and Fujitsu M12, respectively.)
>
> This seems like a typo. Instead of "Oracle S3" shouldn't this be
> "Oracle S7"?
Never mind about the "Oracle S3" S3 core —my mistake. From my
understanding SPARC S7, M7, and T7 use S4 cores and T8 and M8 use S5 cores.
Tony
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v3] sparc: populate sysfs cache topology information on sun4v
2026-10-28 2:00 ` Tony Rodriguez
2026-10-28 2:20 ` Tony Rodriguez
@ 2026-10-28 8:17 ` Jan Engelhardt
1 sibling, 0 replies; 6+ messages in thread
From: Jan Engelhardt @ 2026-10-28 8:17 UTC (permalink / raw)
To: Tony Rodriguez; +Cc: sparclinux
On Wednesday 2026-10-28 03:00, Tony Rodriguez wrote:
> Hi Jan,
>
> On 10/27/26 6:00 PM, Jan Engelhardt wrote:
>> The SPARC port also lacked reporting L3 caches, present in the
>> Oracle S3 and Fujitsu SPARC64 XII cores (machines: Oracle T4
>> onwards and Fujitsu M12, respectively.)
>
> This seems like a typo. Instead of "Oracle S3" shouldn't this be "Oracle S7"?
It seems to be a law of nature that the chance for projects to
fumble-and-stumble with their numbering scheme, without changing the
namespace, increases with project time.[1] From what I could gather:
T1000 machine, T1 chip (microprocessor), S1 core(s)
T5120 machine, T2 chip, S2 [2]
T5240 machine, T2+ chip, S2
T3-1/2/4 machine, T3 chip, S2 [3], "UltraSPARC KT"
T4-1 machine, T4 chip, S3 [3], aka "UltraSPARC VT" (name in MDESC) [5]
T7-1 machine, M7 chip, S4 (?), aka "SPARC 3e40"
S7-2 machine, S7 chip, S4 [4], aka "SPARC 3e42"
[1] https://www.youtube.com/watch?v=MipuN4RU4m8
[2] https://en.wikipedia.org/wiki/UltraSPARC_T2
[3] https://www.touchpoint.com.au/resources/insights/oracle-sparc-cpu-overview-comparison-table/
[4] https://serverental.com/sparc-sonoma-sparc-s7-server-review/
[5] https://www.oracle.com/technetwork/server-storage/sun-sparc-enterprise/documentation/120214-t4-d04-p-ext-2305974.pdf
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v3] sparc: populate sysfs cache topology information on sun4v
2026-10-28 1:00 [PATCH v3] sparc: populate sysfs cache topology information on sun4v Jan Engelhardt
2026-10-28 1:21 ` sashiko-bot
2026-10-28 2:00 ` Tony Rodriguez
@ 2026-10-28 10:29 ` Stian Halseth
2 siblings, 0 replies; 6+ messages in thread
From: Stian Halseth @ 2026-10-28 10:29 UTC (permalink / raw)
To: Jan Engelhardt, andreas; +Cc: sparclinux, unixpro1970, glaubitz
[-- Attachment #1: Type: text/plain, Size: 15116 bytes --]
Hi Jan,
Thanks for the updated patch!
I see Sashiko is already making your day great, so I won't add too much on top
of that, but one thing that Sashiko didn't mention.
With fw_token cleared and no CACHE_ID, last_level_cache_is_valid() is false
for every CPU, so the scheduler's LLC size and the page allocator's per-cpu
sizing still don't see the L3; only sysfs gets it.
Setting CACHE_ID with a stable id (say the lowest cpu id behind the cache,
like x86 does with the APIC id) and leaving shared_cpu_map to the generic code
would fix that, and also gets rid of the hotplug issue and the fw_token
clearing.
/Stian
On Wed, 2026-10-28 at 02:00 +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
> onwards 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)
> ```
>
> ---
> v3:
> //current
> * Resolved the issue that only the first cache of any integer unit
> was looked at.
> * Resolved the issue that only the first integer unit of any strand
> was looked at.
> * Resolved the issue that only integer units were considered for caches.
> * No more duplicated cache entries under
> /sys/devices/system/cpu/cpu0/cache/indexN.
> * No longer keep unstable MDESC node ids around past their lifetime.
>
> v2:
> //Message-ID: <20261026193421.20404-1-jengelh@inai.de>
> * Rewalk MDESC rather than sourcing cache info from struct cpuinfo_sparc.
> * Support L3 caches found on SPARC T4 and later machine models.
>
> v1:
> //Message-ID: <20261018222723.43442-1-jengelh@inai.de>
>
> arch/sparc/include/asm/cpudata_64.h | 1 +
> arch/sparc/include/asm/mdesc.h | 2 +
> arch/sparc/kernel/mdesc.c | 215 ++++++++++++++++++++++++++++
> arch/sparc/kernel/prom_common.c | 53 +++++++
> 4 files changed, 271 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..0e400b451b4b 100644
> --- a/arch/sparc/kernel/mdesc.c
> +++ b/arch/sparc/kernel/mdesc.c
> @@ -11,6 +11,7 @@
> #include <linux/mm.h>
> #include <linux/miscdevice.h>
> #include <linux/memblock.h>
> +#include <linux/cacheinfo.h>
> #include <linux/export.h>
> #include <linux/refcount.h>
>
> @@ -1242,6 +1243,220 @@ void mdesc_fill_in_cpu_data(cpumask_t *mask)
> smp_fill_in_sib_core_maps();
> }
>
> +/**
> + * Return the MDESC node for a "cpu" object whose "id" property
> + * matches @cpuid.
> + */
> +static u64 mdesc_find_cpu(struct mdesc_handle *mdh, int cpuid)
> +{
> + u64 iter;
> + mdesc_for_each_node_by_name(mdh, iter, "cpu") {
> + const u64 *id = mdesc_get_property(mdh, iter, "id", NULL);
> + if (id != NULL && *id == cpuid)
> + return iter;
> + }
> + return MDESC_NODE_NULL;
> +}
> +
> +/**
> + * Obtain the properties of a given MDESC cache object node and fill in the
> + * Linux representation (struct cacheinfo) for it.
> + */
> +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);
> +
> + /*
> + * fw_token is only temporary. It will be cleared again before the
> + * MDESC is unpinned.
> + */
> + 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;
> +}
> +
> +/**
> + * Populate the sharedness bitmap of a given struct cacheinfo by walking up
> the
> + * cache chain again and finding all CPUs.
> + */
> +static void mdesc_cache_set_shared(struct mdesc_handle *mdh, u64
> cache_node,
> + struct cacheinfo *leaf)
> +{
> + unsigned int maxcpus = num_possible_cpus();
> + u64 sup_iter, cpu_iter;
> +
> + mdesc_for_each_arc(sup_iter, mdh, cache_node, MDESC_ARC_TYPE_BACK)
> {
> + u64 sup_node = mdesc_arc_target(mdh, sup_iter);
> + const char *tg_name = mdesc_node_name(mdh, sup_node);
> +
> + if (strcmp(tg_name, "cache") == 0)
> + mdesc_cache_set_shared(mdh, sup_node, leaf);
> + if (strcmp(tg_name, "exec-unit") != 0)
> + continue;
> +
> + mdesc_for_each_arc(cpu_iter, mdh, sup_node,
> MDESC_ARC_TYPE_BACK) {
> + u64 cpu_node = mdesc_arc_target(mdh, cpu_iter);
> + tg_name = mdesc_node_name(mdh, cpu_node);
> +
> + if (strcmp(tg_name, "cpu") != 0)
> + continue;
> + const u64 *id = mdesc_get_property(mdh, cpu_node,
> "id", NULL);
> + if (id == NULL)
> + continue;
> + if (id != NULL && *id < maxcpus)
> + cpumask_set_cpu(*id, &leaf-
> >shared_cpu_map);
> + }
> + }
> +}
> +
> +static bool seen_cache(const struct cacheinfo *leaf, size_t n, u64 node)
> +{
> + while (n-- > 0)
> + if ((uintptr_t)leaf[n].fw_token == node)
> + return true;
> + return false;
> +}
> +
> +/**
> + * For a given MDESC "cache" object node, copy properties, and recursively
> + * search for lower-level caches.
> + */
> +static int mdesc_cache_record(struct mdesc_handle *mdh, u64 cache_node,
> + unsigned int cpuid, unsigned int *leaf_count,
> + bool fill_it)
> +{
> + const u64 *level = mdesc_get_property(mdh, cache_node, "level",
> NULL);
> + struct cpu_cacheinfo *cc;
> + u64 iter;
> +
> + if (level == NULL)
> + return -ENOENT;
> + cc = get_cpu_cacheinfo(cpuid);
> + if (fill_it) {
> + if (*leaf_count >= cc->num_leaves) {
> + /*
> + * 1. Sudden MDESC change between init_cache_level
> + * and populate_cache_leaves, or 2. programming
> + * errors.
> + */
> + pr_err("%s: tried to populate more cacheinfos than
> "
> + "allocated.\n", __func__);
> + return -ELOOP;
> + }
> + if (!seen_cache(cc->info_list, *leaf_count, cache_node)) {
> + struct cacheinfo *ci = &cc-
> >info_list[(*leaf_count)++];
> + mdesc_cache_fill(mdh, cache_node, ci);
> + mdesc_cache_set_shared(mdh, cache_node, ci);
> + cc->cpu_map_populated = true;
> + }
> + } else {
> + ++cc->num_leaves;
> + if (cc->num_levels < *level)
> + cc->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;
> +}
> +
> +/**
> + * For a given Linux CPU @cpuid (hardware strand), find the corresponding
> "cpu"
> + * object node in the MDESC, and from there follow pointers to "exec-unit"
> + * object nodes and, subsequently, "cache" object nodes.
> + */
> +int mdesc_cache_walk(unsigned int cpuid, bool fill_it)
> +{
> + unsigned int leaf_count = 0;
> + struct mdesc_handle *mdh = mdesc_grab();
> + u64 cpu_node, eu_arc;
> + int ret = 0;
> +
> + if (mdh == NULL)
> + return -ENOENT;
> +
> +#ifndef CONFIG_SMP
> + /*
> + * On UP, cpu_data() has just one entry with index 0, but we want
> + * the values from the real physical cpu the kernel booted on.
> + */
> + cpuid = real_hard_smp_processor_id();
> +#endif
> +
> + cpu_node = mdesc_find_cpu(mdh, cpuid);
> + if (cpu_node == MDESC_NODE_NULL) {
> + mdesc_release(mdh);
> + return -ENOENT;
> + }
> +
> + mdesc_for_each_arc(eu_arc, mdh, cpu_node, MDESC_ARC_TYPE_FWD) {
> + u64 cache_arc;
> + u64 eu_node = mdesc_arc_target(mdh, eu_arc);
> + const char *node_name = mdesc_node_name(mdh, eu_node);
> +
> + if (strcmp(node_name, "exec-unit") != 0)
> + continue;
> +
> + mdesc_for_each_arc(cache_arc, mdh, eu_node,
> MDESC_ARC_TYPE_FWD) {
> + u64 cache_node = mdesc_arc_target(mdh, cache_arc);
> + node_name = mdesc_node_name(mdh, cache_node);
> + if (strcmp(node_name, "cache") != 0)
> + continue;
> + ret = mdesc_cache_record(mdh, cache_node, cpuid,
> + &leaf_count, fill_it);
> + if (ret != 0)
> + goto out;
> + }
> + }
> +
> + if (fill_it) {
> + /* Reduce count to actually populated slots. */
> + struct cpu_cacheinfo *cc = get_cpu_cacheinfo(cpuid);
> + cc->num_leaves = leaf_count;
> +
> + /* We have the sharedness bitmaps and don't need fw_token.
> */
> + for (size_t i = 0; i < cc->num_leaves; ++i)
> + cc->info_list[i].fw_token = NULL;
> + }
> + out:
> + 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..808dc044514f 100644
> --- a/arch/sparc/kernel/prom_common.c
> +++ b/arch/sparc/kernel/prom_common.c
> @@ -11,12 +11,15 @@
> */
>
> #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 <linux/sort.h>
> +#include <asm/mdesc.h>
> #include <asm/prom.h>
> #include <asm/oplib.h>
>
> @@ -112,6 +115,56 @@ int of_find_in_proplist(const char *list, const char
> *match, int len)
> }
> EXPORT_SYMBOL(of_find_in_proplist);
>
> +/**
> + * Called from from drivers/base/cacheinfo.c meant to set
> + * cpu_cacheinfo::num_leaves for allocation purposes.
> + */
> +int init_cache_level(unsigned int cpuid)
> +{
> +#ifdef CONFIG_SPARC64
> + if (tlb_type == hypervisor)
> + return mdesc_cache_walk(cpuid, false);
> +#endif
> + return -ENOENT;
> +}
> +
> +static int cache_leaf_compare(const void *va, const void *vb)
> +{
> + const struct cacheinfo *a = va, *b = vb;
> + int r = cmp_int(a->level, b->level);
> + return r != 0 ? r : cmp_int(a->type, b->type);
> +}
> +
> +static void cache_leaf_swap(void *va, void *vb, int size)
> +{
> + struct cacheinfo c;
> + memcpy(&c, va, size);
> + memcpy(va, vb, size);
> + memcpy(vb, &c, size);
> +}
> +
> +int populate_cache_leaves(unsigned int cpuid)
> +{
> + int ret;
> +#ifdef CONFIG_SPARC64
> + if (tlb_type == hypervisor) {
> + ret = mdesc_cache_walk(cpuid, true);
> + }
> + else
> +#endif
> + {
> + /* Room to put the call for an OF-based cache walk here */
> + ret = -ENOENT;
> + }
> + if (ret == 0) {
> + /* cacheinfo.c wants the last item to be the farthest cache
> */
> + struct cpu_cacheinfo *ci = get_cpu_cacheinfo(cpuid);
> + sort(ci->info_list, ci->num_leaves, sizeof(struct
> cacheinfo),
> + cache_leaf_compare, cache_leaf_swap);
> + }
> + 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] 6+ messages in thread
end of thread, other threads:[~2026-10-28 10:29 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-28 1:00 [PATCH v3] sparc: populate sysfs cache topology information on sun4v Jan Engelhardt
2026-10-28 1:21 ` sashiko-bot
2026-10-28 2:00 ` Tony Rodriguez
2026-10-28 2:20 ` Tony Rodriguez
2026-10-28 8:17 ` Jan Engelhardt
2026-10-28 10:29 ` 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.