* [PATCH RFC 1/3] arch_topology: Add a die level to CPU topology
2026-09-20 3:18 [PATCH RFC 0/3] riscv: Add explicit die-level CPU topology Troy Mitchell
@ 2026-09-20 3:18 ` Troy Mitchell
2026-09-20 3:18 ` [PATCH RFC 2/3] arch_topology: Parse die nodes in /cpu-map Troy Mitchell
2026-09-20 3:18 ` [PATCH RFC 3/3] riscv: topology: Expose explicit CPU die topology Troy Mitchell
2 siblings, 0 replies; 6+ messages in thread
From: Troy Mitchell @ 2026-09-20 3:18 UTC (permalink / raw)
To: Sudeep Holla, Greg Kroah-Hartman, Rafael J. Wysocki,
Danilo Krummrich, Paul Walmsley, Palmer Dabbelt, Albert Ou,
Alexandre Ghiti
Cc: Catalin Marinas, Will Deacon, Mark Rutland, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, linux-kernel, driver-core,
linux-riscv, linux-arm-kernel, devicetree, Troy Mitchell
The generic topology model records packages and clusters but has no
representation for a die between them. Systems with multiple dies in one
package must therefore either report each die as a package or flatten
clusters across all dies. Both choices lose part of the physical hierarchy.
Add a die identifier and sibling mask between the package and cluster
levels. Initialize the identifier to -1 and only build die sibling masks
when firmware supplied a valid identifier. CPU topology paths without die
information, including the current ACPI parser and the fallback topology,
therefore keep the die unknown instead of inventing die 0.
Scope cluster sibling matching to a die so each die can number its
clusters independently, and maintain the new mask across CPU hotplug.
Signed-off-by: Troy Mitchell <troy.mitchell@linux.dev>
---
drivers/base/arch_topology.c | 20 +++++++++++++++++---
include/linux/arch_topology.h | 2 ++
2 files changed, 19 insertions(+), 3 deletions(-)
diff --git a/drivers/base/arch_topology.c b/drivers/base/arch_topology.c
index 8c5e47c28d9a3..ee82827be3351 100644
--- a/drivers/base/arch_topology.c
+++ b/drivers/base/arch_topology.c
@@ -766,6 +766,14 @@ void update_siblings_masks(unsigned int cpuid)
cpumask_set_cpu(cpuid, &cpu_topo->core_sibling);
cpumask_set_cpu(cpu, &cpuid_topo->core_sibling);
+ if (cpuid_topo->die_id != cpu_topo->die_id)
+ continue;
+
+ if (cpuid_topo->die_id >= 0) {
+ cpumask_set_cpu(cpu, &cpuid_topo->die_sibling);
+ cpumask_set_cpu(cpuid, &cpu_topo->die_sibling);
+ }
+
if (cpuid_topo->cluster_id != cpu_topo->cluster_id)
continue;
@@ -792,6 +800,9 @@ static void clear_cpu_topology(int cpu)
cpumask_clear(&cpu_topo->cluster_sibling);
cpumask_set_cpu(cpu, &cpu_topo->cluster_sibling);
+ cpumask_clear(&cpu_topo->die_sibling);
+ cpumask_set_cpu(cpu, &cpu_topo->die_sibling);
+
cpumask_clear(&cpu_topo->core_sibling);
cpumask_set_cpu(cpu, &cpu_topo->core_sibling);
cpumask_clear(&cpu_topo->thread_sibling);
@@ -808,6 +819,7 @@ void __init reset_cpu_topology(void)
cpu_topo->thread_id = -1;
cpu_topo->core_id = -1;
cpu_topo->cluster_id = -1;
+ cpu_topo->die_id = -1;
cpu_topo->package_id = -1;
clear_cpu_topology(cpu);
@@ -824,6 +836,8 @@ void remove_cpu_topology(unsigned int cpu)
cpumask_clear_cpu(cpu, topology_sibling_cpumask(sibling));
for_each_cpu(sibling, topology_cluster_cpumask(cpu))
cpumask_clear_cpu(cpu, topology_cluster_cpumask(sibling));
+ for_each_cpu(sibling, &cpu_topology[cpu].die_sibling)
+ cpumask_clear_cpu(cpu, &cpu_topology[sibling].die_sibling);
for_each_cpu(sibling, topology_llc_cpumask(cpu))
cpumask_clear_cpu(cpu, topology_llc_cpumask(sibling));
@@ -969,9 +983,9 @@ void store_cpu_topology(unsigned int cpuid)
cpuid_topo->core_id = cpuid;
cpuid_topo->package_id = cpu_to_node(cpuid);
- pr_debug("CPU%u: package %d core %d thread %d\n",
- cpuid, cpuid_topo->package_id, cpuid_topo->core_id,
- cpuid_topo->thread_id);
+ pr_debug("CPU%u: package %d die %d core %d thread %d\n",
+ cpuid, cpuid_topo->package_id, cpuid_topo->die_id,
+ cpuid_topo->core_id, cpuid_topo->thread_id);
topology_populated:
update_siblings_masks(cpuid);
diff --git a/include/linux/arch_topology.h b/include/linux/arch_topology.h
index ebd7f8935f969..2a5591bb95075 100644
--- a/include/linux/arch_topology.h
+++ b/include/linux/arch_topology.h
@@ -63,10 +63,12 @@ struct cpu_topology {
int thread_id;
int core_id;
int cluster_id;
+ int die_id;
int package_id;
cpumask_t thread_sibling;
cpumask_t core_sibling;
cpumask_t cluster_sibling;
+ cpumask_t die_sibling;
cpumask_t llc_sibling;
};
--
2.55.0
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH RFC 2/3] arch_topology: Parse die nodes in /cpu-map
2026-09-20 3:18 [PATCH RFC 0/3] riscv: Add explicit die-level CPU topology Troy Mitchell
2026-09-20 3:18 ` [PATCH RFC 1/3] arch_topology: Add a die level to " Troy Mitchell
@ 2026-09-20 3:18 ` Troy Mitchell
2026-09-21 11:14 ` Sudeep Holla
2026-09-20 3:18 ` [PATCH RFC 3/3] riscv: topology: Expose explicit CPU die topology Troy Mitchell
2 siblings, 1 reply; 6+ messages in thread
From: Troy Mitchell @ 2026-09-20 3:18 UTC (permalink / raw)
To: Sudeep Holla, Greg Kroah-Hartman, Rafael J. Wysocki,
Danilo Krummrich, Paul Walmsley, Palmer Dabbelt, Albert Ou,
Alexandre Ghiti
Cc: Catalin Marinas, Will Deacon, Mark Rutland, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, linux-kernel, driver-core,
linux-riscv, linux-arm-kernel, devicetree, Troy Mitchell
The devicetree CPU map can distinguish packages and clusters but cannot
describe multiple dies in one package. Such systems have to flatten all
clusters into the package, making cluster identifiers package-wide and
hiding the die boundary.
Recognize an optional sequence of dieN nodes between a socket and its
clusters, and propagate the die identifier to every CPU below it. Enumerate
clusters independently below each die.
When a CPU map has no dieN nodes, continue parsing its existing package
and cluster hierarchy but pass -1 as the die identifier. Legacy maps
therefore remain compatible without being reported as an explicitly
described die 0.
Signed-off-by: Troy Mitchell <troy.mitchell@linux.dev>
---
drivers/base/arch_topology.c | 48 +++++++++++++++++++++++++++++++++++---------
1 file changed, 38 insertions(+), 10 deletions(-)
diff --git a/drivers/base/arch_topology.c b/drivers/base/arch_topology.c
index ee82827be3351..67a984f3ba8d9 100644
--- a/drivers/base/arch_topology.c
+++ b/drivers/base/arch_topology.c
@@ -498,7 +498,7 @@ static int __init get_cpu_for_node(struct device_node *node)
}
static int __init parse_core(struct device_node *core, int package_id,
- int cluster_id, int core_id)
+ int die_id, int cluster_id, int core_id)
{
char name[20];
bool leaf = true;
@@ -517,6 +517,7 @@ static int __init parse_core(struct device_node *core, int package_id,
cpu = get_cpu_for_node(t);
if (cpu >= 0) {
cpu_topology[cpu].package_id = package_id;
+ cpu_topology[cpu].die_id = die_id;
cpu_topology[cpu].cluster_id = cluster_id;
cpu_topology[cpu].core_id = core_id;
cpu_topology[cpu].thread_id = i;
@@ -538,6 +539,7 @@ static int __init parse_core(struct device_node *core, int package_id,
}
cpu_topology[cpu].package_id = package_id;
+ cpu_topology[cpu].die_id = die_id;
cpu_topology[cpu].cluster_id = cluster_id;
cpu_topology[cpu].core_id = core_id;
} else if (leaf && cpu != -ENODEV) {
@@ -549,7 +551,7 @@ static int __init parse_core(struct device_node *core, int package_id,
}
static int __init parse_cluster(struct device_node *cluster, int package_id,
- int cluster_id, int depth)
+ int die_id, int cluster_id, int depth)
{
char name[20];
bool leaf = true;
@@ -572,7 +574,7 @@ static int __init parse_cluster(struct device_node *cluster, int package_id,
break;
leaf = false;
- ret = parse_cluster(c, package_id, i, depth + 1);
+ ret = parse_cluster(c, package_id, die_id, i, depth + 1);
if (depth > 0)
pr_warn("Topology for clusters of clusters not yet supported\n");
if (ret != 0)
@@ -598,7 +600,8 @@ static int __init parse_cluster(struct device_node *cluster, int package_id,
}
if (leaf) {
- ret = parse_core(c, package_id, cluster_id, core_id++);
+ ret = parse_core(c, package_id, die_id, cluster_id,
+ core_id++);
if (ret != 0)
return ret;
} else {
@@ -616,6 +619,34 @@ static int __init parse_cluster(struct device_node *cluster, int package_id,
return 0;
}
+static int __init parse_die(struct device_node *parent, int package_id)
+{
+ char name[20];
+ bool has_die = false;
+ int die_id = 0, ret;
+
+ do {
+ snprintf(name, sizeof(name), "die%d", die_id);
+ struct device_node *d __free(device_node) =
+ of_get_child_by_name(parent, name);
+
+ if (!d)
+ break;
+
+ has_die = true;
+ ret = parse_cluster(d, package_id, die_id, -1, 0);
+ if (ret)
+ return ret;
+
+ die_id++;
+ } while (1);
+
+ if (!has_die)
+ ret = parse_cluster(parent, package_id, -1, -1, 0);
+
+ return ret;
+}
+
static int __init parse_socket(struct device_node *socket)
{
char name[20];
@@ -631,7 +662,7 @@ static int __init parse_socket(struct device_node *socket)
break;
has_socket = true;
- ret = parse_cluster(c, package_id, -1, 0);
+ ret = parse_die(c, package_id);
if (ret != 0)
return ret;
@@ -639,7 +670,7 @@ static int __init parse_socket(struct device_node *socket)
} while (1);
if (!has_socket)
- ret = parse_cluster(socket, 0, -1, 0);
+ ret = parse_die(socket, 0);
/*
* Reset the max_smt_thread_num to 1 on failure. Since on failure
@@ -667,10 +698,7 @@ static int __init parse_dt_topology(void)
return 0;
}
- /*
- * When topology is provided cpu-map is essentially a root
- * cluster with restricted subnodes.
- */
+ /* cpu-map is the root of the processor topology hierarchy. */
struct device_node *map __free(device_node) =
of_get_child_by_name(cn, "cpu-map");
--
2.55.0
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH RFC 3/3] riscv: topology: Expose explicit CPU die topology
2026-09-20 3:18 [PATCH RFC 0/3] riscv: Add explicit die-level CPU topology Troy Mitchell
2026-09-20 3:18 ` [PATCH RFC 1/3] arch_topology: Add a die level to " Troy Mitchell
2026-09-20 3:18 ` [PATCH RFC 2/3] arch_topology: Parse die nodes in /cpu-map Troy Mitchell
@ 2026-09-20 3:18 ` Troy Mitchell
2 siblings, 0 replies; 6+ messages in thread
From: Troy Mitchell @ 2026-09-20 3:18 UTC (permalink / raw)
To: Sudeep Holla, Greg Kroah-Hartman, Rafael J. Wysocki,
Danilo Krummrich, Paul Walmsley, Palmer Dabbelt, Albert Ou,
Alexandre Ghiti
Cc: Catalin Marinas, Will Deacon, Mark Rutland, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, linux-kernel, driver-core,
linux-riscv, linux-arm-kernel, devicetree, Troy Mitchell
RISC-V uses the generic CPU topology model but does not provide die
topology accessors. The generic sysfs topology code consequently cannot
expose die identifiers or die sibling masks when firmware describes them.
Simply adding the accessors would create die_id, die_cpus, and
die_cpus_list on every RISC-V CPU. That would also expose -1 and a
single-CPU mask when neither DT nor ACPI supplied a die hierarchy.
Add an architecture-selectable visibility predicate for the die sysfs
attributes. Its default preserves the existing sysfs ABI on architectures
that already expose die topology. Make RISC-V expose the attributes only
when the generic topology parser recorded a non-negative die ID.
Legacy DTs, the current ACPI path, and the fallback topology leave die_id
at -1, so RISC-V does not create die sysfs files for them.
Signed-off-by: Troy Mitchell <troy.mitchell@linux.dev>
---
arch/riscv/include/asm/topology.h | 4 ++++
drivers/base/topology.c | 28 +++++++++++++++++++++++++++-
include/linux/topology.h | 3 +++
3 files changed, 34 insertions(+), 1 deletion(-)
diff --git a/arch/riscv/include/asm/topology.h b/arch/riscv/include/asm/topology.h
index fe1a8bf6902d9..5d99cb39f2819 100644
--- a/arch/riscv/include/asm/topology.h
+++ b/arch/riscv/include/asm/topology.h
@@ -8,6 +8,10 @@
#include <asm/numa.h>
#endif
+#define topology_die_id(cpu) (cpu_topology[cpu].die_id)
+#define topology_die_cpumask(cpu) (&cpu_topology[cpu].die_sibling)
+#define topology_die_sysfs_visible(cpu) (topology_die_id(cpu) >= 0)
+
/* Replace task scheduler's default frequency-invariant accounting */
#define arch_scale_freq_tick topology_scale_freq_tick
#define arch_set_freq_scale topology_set_freq_scale
diff --git a/drivers/base/topology.c b/drivers/base/topology.c
index c890e2a5b4280..74d5bb979b1b8 100644
--- a/drivers/base/topology.c
+++ b/drivers/base/topology.c
@@ -171,16 +171,42 @@ static struct attribute *default_attrs[] = {
static umode_t topology_is_visible(struct kobject *kobj,
struct attribute *attr, int unused)
{
- if (attr == &dev_attr_ppin.attr && !topology_ppin(kobj_to_dev(kobj)->id))
+ unsigned int cpu = kobj_to_dev(kobj)->id;
+
+#ifdef TOPOLOGY_DIE_SYSFS
+ if (attr == &dev_attr_die_id.attr &&
+ !topology_die_sysfs_visible(cpu))
+ return 0;
+#endif
+
+ if (attr == &dev_attr_ppin.attr && !topology_ppin(cpu))
return 0;
return attr->mode;
}
+#ifdef TOPOLOGY_DIE_SYSFS
+static umode_t topology_bin_is_visible(struct kobject *kobj,
+ const struct bin_attribute *attr, int unused)
+{
+ unsigned int cpu = kobj_to_dev(kobj)->id;
+
+ if ((attr == &bin_attr_die_cpus ||
+ attr == &bin_attr_die_cpus_list) &&
+ !topology_die_sysfs_visible(cpu))
+ return 0;
+
+ return attr->attr.mode;
+}
+#endif
+
static const struct attribute_group topology_attr_group = {
.attrs = default_attrs,
.bin_attrs = bin_attrs,
.is_visible = topology_is_visible,
+#ifdef TOPOLOGY_DIE_SYSFS
+ .is_bin_visible = topology_bin_is_visible,
+#endif
.name = "topology"
};
diff --git a/include/linux/topology.h b/include/linux/topology.h
index 709a2dcf4c738..9c2c9096aff36 100644
--- a/include/linux/topology.h
+++ b/include/linux/topology.h
@@ -179,6 +179,9 @@ static inline int cpu_to_mem(int cpu)
#if defined(topology_die_id) && defined(topology_die_cpumask)
#define TOPOLOGY_DIE_SYSFS
+#ifndef topology_die_sysfs_visible
+#define topology_die_sysfs_visible(cpu) ((void)(cpu), true)
+#endif
#endif
#if defined(topology_cluster_id) && defined(topology_cluster_cpumask)
#define TOPOLOGY_CLUSTER_SYSFS
--
2.55.0
^ permalink raw reply related [flat|nested] 6+ messages in thread