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