[PATCH RFC 14/15] arm_mpam: Support MSC accessibility derivation from RIS nodes

From: Yin Li

Date: Tue Aug 11 2026 - 10:00:16 EST


The DT accessibility derivation only handled MSCs whose parent node is
a cache or memory device. When the MSC is placed under a generic
container (e.g. directly under /soc), the parent gives no affinity hint
and there is no fallback.

Add a per-RIS fallback path for when the parent node is not a
recognised cache or memory device. For each available RIS child node,
affinity is resolved in two steps: first from an explicit 'cpus'
phandle-array on the RIS node, then from the 'arm,mpam-device' phandle.
The final accessibility mask is the union of all RIS masks; if any RIS
fails to resolve, the mask is cleared and the MSC fails to probe rather
than come up with a partial affinity.

Factor the DT-specific logic into mpam_dt_update_msc_accessibility() so
that update_msc_accessibility() dispatches cleanly between ACPI and DT
paths.

Co-developed-by: Huang Yiwei <huang.yiwei@xxxxxxxxxxxxxxxx>
Signed-off-by: Huang Yiwei <huang.yiwei@xxxxxxxxxxxxxxxx>
Signed-off-by: Yin Li <yin.li@xxxxxxxxxxxxxxxx>
---
drivers/resctrl/mpam_devices.c | 146 ++++++++++++++++++++++++++++++++++++-----
1 file changed, 131 insertions(+), 15 deletions(-)

diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c
index d8856864e89a..d0462239deeb 100644
--- a/drivers/resctrl/mpam_devices.c
+++ b/drivers/resctrl/mpam_devices.c
@@ -2156,6 +2156,136 @@ static int mpam_msc_setup_error_irq(struct mpam_msc *msc)
return 0;
}

+/*
+ * Resolve a RIS to its CPU affinity from its 'cpus' phandle-array.
+ * Returns -ENODEV when 'cpus' is absent (the caller may use another source),
+ * -EINVAL when any phandle is invalid, or 0 on full success.
+ */
+static int get_cpumask_from_ris_cpus(struct device_node *ris_np,
+ cpumask_t *affinity)
+{
+ int i, cpu_phandle_count;
+
+ cpu_phandle_count = of_count_phandle_with_args(ris_np, "cpus", NULL);
+ if (cpu_phandle_count <= 0)
+ return -ENODEV;
+
+ for (i = 0; i < cpu_phandle_count; i++) {
+ struct device_node *phandle_np __free(device_node) =
+ of_parse_phandle(ris_np, "cpus", i);
+ int cpu, matched = -1;
+
+ if (phandle_np) {
+ for_each_possible_cpu(cpu) {
+ struct device_node *cpu_node __free(device_node) =
+ of_get_cpu_node(cpu, NULL);
+
+ if (phandle_np == cpu_node) {
+ matched = cpu;
+ break;
+ }
+ }
+ }
+
+ if (matched < 0) {
+ pr_warn("MPAM: RIS %pOF cpus[%d] (%pOF) is not a possible CPU\n",
+ ris_np, i, phandle_np);
+ return -EINVAL;
+ }
+
+ cpumask_set_cpu(matched, affinity);
+ }
+
+ return 0;
+}
+
+/*
+ * Resolve a RIS to its CPU affinity from its 'arm,mpam-device' phandle.
+ * Returns -EINVAL when 'arm,mpam-device' is absent or the phandle is invalid.
+ * 0 on full success.
+ */
+static int get_cpumask_from_ris_phandle(struct device_node *ris,
+ cpumask_t *affinity)
+{
+ struct device_node *mpam_device __free(device_node) =
+ of_parse_phandle(ris, "arm,mpam-device", 0);
+
+ if (!mpam_device) {
+ pr_warn("MPAM: RIS %pOF has neither 'cpus' nor 'arm,mpam-device'\n",
+ ris);
+ return -EINVAL;
+ }
+
+ if (of_device_is_compatible(mpam_device, "cache"))
+ return get_cpumask_from_cache(mpam_device, affinity);
+
+ if (of_device_is_compatible(mpam_device, "memory")) {
+ cpumask_or(affinity, affinity, cpu_possible_mask);
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int get_cpumask_from_ris(struct device_node *ris, cpumask_t *affinity)
+{
+ int err;
+
+ err = get_cpumask_from_ris_cpus(ris, affinity);
+ if (err != -ENODEV)
+ return err;
+
+ return get_cpumask_from_ris_phandle(ris, affinity);
+}
+
+/*
+ * Returns -ENODEV when the parent is just a container (not a recognised device),
+ * so the caller falls back to the per-RIS description.
+ */
+static int get_cpumask_from_parent(struct mpam_msc *msc, cpumask_t *affinity)
+{
+ struct device_node *parent __free(device_node) =
+ of_get_parent(msc->pdev->dev.of_node);
+
+ if (parent == of_root) {
+ cpumask_copy(affinity, cpu_possible_mask);
+ return 0;
+ }
+
+ if (of_device_is_compatible(parent, "cache"))
+ return get_cpumask_from_cache(parent, affinity);
+
+ if (of_device_is_compatible(parent, "memory")) {
+ cpumask_copy(affinity, cpu_possible_mask);
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
+/*
+ * An MSC's CPU affinity is described either by its parent node or, when the
+ * parent is just a container, per-RIS inside the MSC. In the per-RIS case the
+ * accessibility is the union of the RIS affinities: every RIS must resolve, so
+ * a single failure clears the mask and stops and the MSC fails to probe rather
+ * than come up with a partial affinity.
+ */
+static void mpam_dt_update_msc_accessibility(struct mpam_msc *msc)
+{
+ cpumask_t *affinity = &msc->accessibility;
+
+ cpumask_clear(affinity);
+
+ if (get_cpumask_from_parent(msc, affinity) == -ENODEV) {
+ for_each_available_child_of_node_scoped(msc->pdev->dev.of_node, ris) {
+ if (get_cpumask_from_ris(ris, affinity)) {
+ cpumask_clear(affinity);
+ break;
+ }
+ }
+ }
+}
+
/*
* An MSC can control traffic from a set of CPUs, but may only be accessible
* from a (hopefully wider) set of CPUs. The common reason for this is power
@@ -2165,8 +2295,6 @@ static int mpam_msc_setup_error_irq(struct mpam_msc *msc)
*/
static void update_msc_accessibility(struct mpam_msc *msc)
{
- struct device *dev = &msc->pdev->dev;
- struct device_node *parent;
u32 affinity_id;
int err;

@@ -2182,19 +2310,7 @@ static void update_msc_accessibility(struct mpam_msc *msc)
return;
}

- /* Where an MSC can be accessed from depends on the path to of_node. */
- parent = of_get_parent(msc->pdev->dev.of_node);
- if (parent == of_root) {
- cpumask_copy(&msc->accessibility, cpu_possible_mask);
- } else {
- if (of_device_is_compatible(parent, "cache"))
- get_cpumask_from_cache(parent, &msc->accessibility);
- else if (of_device_is_compatible(parent, "memory"))
- cpumask_copy(&msc->accessibility, cpu_possible_mask);
- else
- dev_err_once(dev, "Cannot determine accessibility of MSC.\n");
- }
- of_node_put(parent);
+ mpam_dt_update_msc_accessibility(msc);
}

/*

--
2.34.1