* [PATCH v10 14/15] arm_mpam: Use the request PARTIDs beyond the internal range
[not found] <20261019074609.2144405-1-2118537295@qq.com>
@ 2026-10-19 7:46 ` Jinjie Ruan
2026-10-19 8:28 ` Jinjie Ruan
2026-10-19 7:46 ` [PATCH v10 15/15] arm_mpam: resctrl: Add kunit tests for narrow PARTID helpers Jinjie Ruan
1 sibling, 1 reply; 3+ messages in thread
From: Jinjie Ruan @ 2026-10-19 7:46 UTC (permalink / raw)
To: babu.moger, ben.horgan, bp, catalin.marinas, dave.hansen,
Dave.Martin, fenghuay, gshan, hpa, james.morse, jic23, mingo,
reinette.chatre, ruanjinjie, tan.shaopeng, tglx, tony.luck,
zengheng4, x86, linux-kernel
From: Jinjie Ruan <ruanjinjie@huawei.com>
A class that implements PARTID narrowing maps several request PARTIDs
onto one internal PARTID. The internal PARTIDs hold its controls, and
the request PARTIDs can each be a monitoring group. This gives one
control group more monitoring groups than the PMG count alone allows.
The extra request PARTIDs can only be used when no class would lose a
non-aliasing control: its value can not be written to every PARTID of a
control group, whereas an aliasing control's value can. Every class is
considered, including the ones resctrl does not use, so that an unknown
class can still gain a resctrl schema later. This is decided once,
before resctrl_init(), and there is no user interface for it.
The driver stops reducing msc->partid_max, so the full request PARTID
range is available, and the resctrl glue code starts handing out the
extra ones. A control group's configuration is written to every request
PARTID that maps onto it.
Cc: James Morse <james.morse@arm.com>
Cc: Ben Horgan <ben.horgan@arm.com>
Cc: Reinette Chatre <reinette.chatre@intel.com>
Cc: Fenghua Yu <fenghuay@nvidia.com>
Suggested-by: James Morse <james.morse@arm.com>
Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
---
v9->v10:
- Wrap the intpartid mapping into the internal PARTID range when resctrl
is not managing the mapping
---
drivers/resctrl/mpam_devices.c | 25 +++++++++++-
drivers/resctrl/mpam_internal.h | 2 +
drivers/resctrl/mpam_resctrl.c | 72 ++++++++++++++++++++++++++++++++-
3 files changed, 96 insertions(+), 3 deletions(-)
diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c
index f81a2df11428..79bc3339a762 100644
--- a/drivers/resctrl/mpam_devices.c
+++ b/drivers/resctrl/mpam_devices.c
@@ -74,6 +74,18 @@ static DEFINE_SPINLOCK(partid_max_lock);
static u32 mpam_num_closid;
static u32 mpam_num_partid;
+void mpam_partid_map_init(u32 num_partid, u32 num_closid)
+{
+ mpam_num_partid = num_partid;
+ mpam_num_closid = num_closid;
+}
+
+void mpam_partid_map_exit(void)
+{
+ mpam_num_closid = 0;
+ mpam_num_partid = 0;
+}
+
/*
* Map a request PARTID onto its owning control group using modulo
* interleaving. Without resctrl active, every PARTID maps 1:1 onto itself.
@@ -959,6 +971,9 @@ static void mpam_ris_hw_probe(struct mpam_msc_ris *ris)
* A RIS that implements PARTID narrowing has one configuration per
* internal PARTID, and several request PARTIDs share it. Record both
* limits in the resource properties.
+ * Keep msc->partid_max at the full request PARTID range. The resctrl
+ * glue code will broadcast a control group's configuration to every
+ * request PARTID that maps onto that group's internal PARTID.
*/
props->partid_max = msc->partid_max;
props->intpartid_max = msc->partid_max;
@@ -969,7 +984,6 @@ static void mpam_ris_hw_probe(struct mpam_msc_ris *ris)
mpam_set_feature(mpam_feat_partid_nrw, props);
props->intpartid_max = min(props->intpartid_max, intpartid_max);
- msc->partid_max = min(msc->partid_max, intpartid_max);
}
}
@@ -1581,6 +1595,15 @@ static void mpam_reprogram_ris_partid(struct mpam_msc_ris *ris, u16 partid,
/* Update the intpartid mapping */
u16 intpartid = mpam_partid_to_intpartid(partid);
+ /*
+ * Without resctrl managing the mapping, the fallback maps
+ * every PARTID 1:1, which can exceed the internal PARTID
+ * range. Wrap onto an internal PARTID, like the narrowing
+ * mapping resctrl programs.
+ */
+ if (intpartid > rprops->intpartid_max)
+ intpartid %= (u32)rprops->intpartid_max + 1;
+
mpam_write_partsel_reg(msc, INTPARTID,
MPAMCFG_INTPARTID_INTERNAL | intpartid);
diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_internal.h
index a4f2fdf7ac95..b62a0ecdd69f 100644
--- a/drivers/resctrl/mpam_internal.h
+++ b/drivers/resctrl/mpam_internal.h
@@ -478,6 +478,8 @@ extern u16 mpam_partid_max;
extern u8 mpam_pmg_max;
u16 mpam_partid_to_intpartid(u16 partid);
+void mpam_partid_map_init(u32 num_partid, u32 num_closid);
+void mpam_partid_map_exit(void);
/* Scheduled work callback to enable mpam once all MSC have been probed */
void mpam_enable(struct work_struct *work);
diff --git a/drivers/resctrl/mpam_resctrl.c b/drivers/resctrl/mpam_resctrl.c
index d60dea846486..fb997b9d41d6 100644
--- a/drivers/resctrl/mpam_resctrl.c
+++ b/drivers/resctrl/mpam_resctrl.c
@@ -62,6 +62,9 @@ static DEFINE_MUTEX(domain_list_lock);
*/
static bool cdp_enabled;
+static u32 resctrl_num_closid = 1;
+static u32 resctrl_num_mon_partid = 1;
+
/* If resctrl is leveraging extra request PARTIDs for monitoring. */
static bool resctrl_using_extra_partids;
@@ -269,12 +272,12 @@ static bool mpam_resctrl_hide_cdp(enum resctrl_res_level rid)
*/
u32 resctrl_arch_get_num_closid(struct rdt_resource *ignored)
{
- return mpam_partid_max + 1;
+ return resctrl_num_closid;
}
u32 resctrl_arch_system_num_rmid_idx(void)
{
- return (mpam_pmg_max + 1) * (mpam_partid_max + 1);
+ return (mpam_pmg_max + 1) * resctrl_num_mon_partid;
}
/* The request PARTID and PMG a resctrl RMID maps onto. */
@@ -1931,6 +1934,65 @@ void mpam_resctrl_offline_cpu(unsigned int cpu)
}
}
+/*
+ * Decide how the PARTID space is presented to resctrl. This runs once the
+ * classes are known, and before resctrl_init() advertises any limits to
+ * user-space.
+ *
+ * The extra request PARTIDs on a class that implements narrowing can be used
+ * as monitoring groups. That is only safe when no class would lose a
+ * non-aliasing control: its value can not be written to each PARTID of a
+ * control group, whereas an aliasing control's value can. A class that
+ * implements narrowing doesn't have the problem, as its PARTIDs are already
+ * configured as one.
+ *
+ * Every class is considered, including the ones resctrl doesn't use. An
+ * unknown class may gain a resctrl schema later, and using the extra PARTIDs
+ * would prevent that.
+ */
+static void mpam_resctrl_configure_partids(void)
+{
+ u32 num_closid = (u32)mpam_partid_max + 1;
+ bool can_expand = false, blocked = false;
+ struct mpam_class *class;
+
+ guard(srcu)(&mpam_srcu);
+ list_for_each_entry_srcu(class, &mpam_classes, classes_list,
+ srcu_read_lock_held(&mpam_srcu)) {
+ struct mpam_props *cprops = &class->props;
+
+ num_closid = min(num_closid, (u32)cprops->intpartid_max + 1);
+
+ if (mpam_has_feature(mpam_feat_partid_nrw, cprops)) {
+ if (cprops->intpartid_max < cprops->partid_max)
+ can_expand = true;
+ } else if (mpam_props_has_non_aliasing_controls(cprops)) {
+ pr_debug("class %u has non-aliasing controls without narrowing\n",
+ class->level);
+ blocked = true;
+ }
+ }
+
+ if (!num_closid)
+ num_closid = 1;
+
+ resctrl_num_closid = num_closid;
+ if (can_expand && !blocked) {
+ resctrl_using_extra_partids = true;
+ resctrl_num_mon_partid = (u32)mpam_partid_max + 1;
+ } else {
+ resctrl_using_extra_partids = false;
+ resctrl_num_mon_partid = num_closid;
+ }
+
+ /* Tell the driver how the request PARTIDs map onto control groups. */
+ mpam_partid_map_init(resctrl_num_mon_partid, resctrl_num_closid);
+
+ pr_debug("PARTIDs: monitor=%u closid=%u extra=%u\n",
+ resctrl_num_mon_partid, resctrl_num_closid,
+ resctrl_using_extra_partids);
+}
+
int mpam_resctrl_setup(void)
{
int err = 0;
@@ -1952,6 +2014,9 @@ int mpam_resctrl_setup(void)
mpam_resctrl_pick_caches();
mpam_resctrl_pick_mba();
+ /* Decide how the request PARTIDs map onto the control groups */
+ mpam_resctrl_configure_partids();
+
/* Initialise the resctrl structures from the classes */
for_each_mpam_resctrl_control(res, rid) {
if (!res->class)
@@ -2007,6 +2072,9 @@ void mpam_resctrl_exit(void)
WRITE_ONCE(resctrl_enabled, false);
resctrl_exit();
+
+ /* resctrl is gone: forget how the PARTIDs were grouped. */
+ mpam_partid_map_exit();
}
static void mpam_resctrl_teardown_mon(struct mpam_resctrl_mon *mon, struct mpam_class *class)
--
2.43.0
^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH v10 15/15] arm_mpam: resctrl: Add kunit tests for narrow PARTID helpers
[not found] <20261019074609.2144405-1-2118537295@qq.com>
2026-10-19 7:46 ` [PATCH v10 14/15] arm_mpam: Use the request PARTIDs beyond the internal range Jinjie Ruan
@ 2026-10-19 7:46 ` Jinjie Ruan
1 sibling, 0 replies; 3+ messages in thread
From: Jinjie Ruan @ 2026-10-19 7:46 UTC (permalink / raw)
To: babu.moger, ben.horgan, bp, catalin.marinas, dave.hansen,
Dave.Martin, fenghuay, gshan, hpa, james.morse, jic23, mingo,
reinette.chatre, ruanjinjie, tan.shaopeng, tglx, tony.luck,
zengheng4, x86, linux-kernel
From: Jinjie Ruan <ruanjinjie@huawei.com>
Cover the RMID to request PARTID/PMG encoding, the request PARTID to
intPARTID modulo mapping, CLOSID membership checks across legacy, CDP,
narrowing and CDP+narrowing modes, and the (CLOSID, RMID) translation
helper.
The tests poke the partid map through mpam_partid_map_init() so they
do not depend on resctrl having been set up.
Enabled with CONFIG_MPAM_KUNIT_TEST; run with:
kunit.py run --arch=arm64 --kunitconfig=drivers/resctrl
Assisted-by: GLM:5.3
Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
---
drivers/resctrl/.kunitconfig | 4 +
drivers/resctrl/test_mpam_resctrl.c | 466 ++++++++++++++++++++++++++++
2 files changed, 470 insertions(+)
create mode 100644 drivers/resctrl/.kunitconfig
diff --git a/drivers/resctrl/.kunitconfig b/drivers/resctrl/.kunitconfig
new file mode 100644
index 000000000000..0cc78c0f7474
--- /dev/null
+++ b/drivers/resctrl/.kunitconfig
@@ -0,0 +1,4 @@
+CONFIG_KUNIT=y
+CONFIG_ARM64_MPAM=y
+CONFIG_RESCTRL_FS=y
+CONFIG_MPAM_KUNIT_TEST=y
diff --git a/drivers/resctrl/test_mpam_resctrl.c b/drivers/resctrl/test_mpam_resctrl.c
index b93d6ad87e43..f6620ea6f873 100644
--- a/drivers/resctrl/test_mpam_resctrl.c
+++ b/drivers/resctrl/test_mpam_resctrl.c
@@ -296,6 +296,462 @@ static void test_percent_to_max_rounding(struct kunit *test)
KUNIT_EXPECT_LE(test, 4 * num_rounded_up, 3 * total);
}
+/*
+ * Test cases for CDP and PARTID narrowing compatibility
+ */
+
+struct cdp_narrowing_test_case {
+ bool cdp_enabled;
+ bool narrowing_enabled;
+ u32 closid;
+ u32 rmid;
+ u8 pmg_max;
+ u32 expected_partid_d;
+ u32 expected_partid_i;
+ u8 expected_pmg_d;
+ u8 expected_pmg_i;
+ const char *desc;
+};
+
+static const struct cdp_narrowing_test_case cdp_narrowing_cases[] = {
+ {
+ .cdp_enabled = false,
+ .narrowing_enabled = false,
+ .closid = 5,
+ .rmid = 3,
+ .pmg_max = 127,
+ .expected_partid_d = 5,
+ .expected_partid_i = 5,
+ .expected_pmg_d = 3,
+ .expected_pmg_i = 3,
+ .desc = "CDP=0, narrow=0: basic mapping"
+ },
+ {
+ .cdp_enabled = false,
+ .narrowing_enabled = true,
+ .closid = 5,
+ .rmid = 258,
+ .pmg_max = 127,
+ .expected_partid_d = 2,
+ .expected_partid_i = 2,
+ .expected_pmg_d = 2,
+ .expected_pmg_i = 2,
+ .desc = "CDP=0, narrow=1: extract reqPARTID from rmid"
+ },
+ {
+ .cdp_enabled = true,
+ .narrowing_enabled = false,
+ .closid = 5,
+ .rmid = 3,
+ .pmg_max = 127,
+ .expected_partid_d = 10,
+ .expected_partid_i = 11,
+ .expected_pmg_d = 3,
+ .expected_pmg_i = 3,
+ .desc = "CDP=1, narrow=0: CDP DATA/CODE split"
+ },
+ {
+ .cdp_enabled = true,
+ .narrowing_enabled = true,
+ .closid = 5,
+ .rmid = 258,
+ .pmg_max = 127,
+ .expected_partid_d = 4,
+ .expected_partid_i = 5,
+ .expected_pmg_d = 2,
+ .expected_pmg_i = 2,
+ .desc = "CDP=1, narrow=1: both features combined"
+ },
+ {
+ .cdp_enabled = false,
+ .narrowing_enabled = false,
+ .closid = 65535,
+ .rmid = 0,
+ .pmg_max = 127,
+ .expected_partid_d = 65535,
+ .expected_partid_i = 65535,
+ .expected_pmg_d = 0,
+ .expected_pmg_i = 0,
+ .desc = "Boundary: max closid"
+ },
+ {
+ .cdp_enabled = false,
+ .narrowing_enabled = false,
+ .closid = 0,
+ .rmid = 127,
+ .pmg_max = 127,
+ .expected_partid_d = 0,
+ .expected_partid_i = 0,
+ .expected_pmg_d = 127,
+ .expected_pmg_i = 127,
+ .desc = "Boundary: max pmg"
+ },
+ {
+ .cdp_enabled = false,
+ .narrowing_enabled = true,
+ .closid = 0,
+ .rmid = 639,
+ .pmg_max = 159,
+ .expected_partid_d = 3,
+ .expected_partid_i = 3,
+ .expected_pmg_d = 159,
+ .expected_pmg_i = 159,
+ .desc = "Narrowing with pmg_max=159"
+ },
+ {
+ .cdp_enabled = true,
+ .narrowing_enabled = true,
+ .closid = 10,
+ .rmid = 1280,
+ .pmg_max = 127,
+ .expected_partid_d = 20,
+ .expected_partid_i = 21,
+ .expected_pmg_d = 0,
+ .expected_pmg_i = 0,
+ .desc = "CDP + narrowing with reqPARTID=10"
+ },
+};
+
+static void cdp_narrowing_test_desc(const struct cdp_narrowing_test_case *param,
+ char *desc)
+{
+ snprintf(desc, KUNIT_PARAM_DESC_SIZE, "%s", param->desc);
+}
+
+KUNIT_ARRAY_PARAM(cdp_narrowing, cdp_narrowing_cases, cdp_narrowing_test_desc);
+
+static void test_cdp_narrowing_mapping(struct kunit *test)
+{
+ const struct cdp_narrowing_test_case *param = test->param_value;
+ bool saved_cdp, saved_narrowing;
+ u32 partid_d, partid_i;
+ u8 saved_pmg_max;
+ u8 pmg_d, pmg_i;
+
+ saved_pmg_max = mpam_pmg_max;
+ saved_cdp = cdp_enabled;
+ saved_narrowing = resctrl_using_extra_partids;
+
+ mpam_pmg_max = param->pmg_max;
+ cdp_enabled = param->cdp_enabled;
+ resctrl_using_extra_partids = param->narrowing_enabled;
+
+ resctrl_closid_rmid_to_partid_pmg(param->closid, param->rmid,
+ &partid_d, &partid_i,
+ &pmg_d, &pmg_i);
+
+ KUNIT_EXPECT_EQ(test, partid_d, param->expected_partid_d);
+ KUNIT_EXPECT_EQ(test, partid_i, param->expected_partid_i);
+ KUNIT_EXPECT_EQ(test, pmg_d, param->expected_pmg_d);
+ KUNIT_EXPECT_EQ(test, pmg_i, param->expected_pmg_i);
+
+ mpam_pmg_max = saved_pmg_max;
+ cdp_enabled = saved_cdp;
+ resctrl_using_extra_partids = saved_narrowing;
+}
+
+static void test_cdp_narrowing_consistency(struct kunit *test)
+{
+ u32 partid_d1, partid_i1, partid_d2, partid_i2;
+ u8 pmg_d1, pmg_i1, pmg_d2, pmg_i2;
+ bool saved_cdp, saved_narrowing;
+ u8 saved_pmg_max;
+
+ saved_pmg_max = mpam_pmg_max;
+ saved_cdp = cdp_enabled;
+ saved_narrowing = resctrl_using_extra_partids;
+
+ mpam_pmg_max = 127;
+
+ cdp_enabled = true;
+ resctrl_using_extra_partids = false;
+ resctrl_closid_rmid_to_partid_pmg(10, 5, &partid_d1, &partid_i1,
+ &pmg_d1, &pmg_i1);
+
+ cdp_enabled = true;
+ resctrl_using_extra_partids = false;
+ resctrl_closid_rmid_to_partid_pmg(10, 7, &partid_d2, &partid_i2,
+ &pmg_d2, &pmg_i2);
+
+ KUNIT_EXPECT_EQ(test, partid_d1, partid_d2);
+ KUNIT_EXPECT_EQ(test, partid_i1, partid_i2);
+ KUNIT_EXPECT_EQ(test, partid_i1, partid_d1 + 1);
+
+ mpam_pmg_max = saved_pmg_max;
+ cdp_enabled = saved_cdp;
+ resctrl_using_extra_partids = saved_narrowing;
+}
+
+static void test_narrowing_rmid_encode_decode(struct kunit *test)
+{
+ bool saved_narrowing;
+ u8 pmg, decoded_pmg;
+ u8 saved_pmg_max;
+ u16 partid;
+ u32 rmid;
+
+ saved_pmg_max = mpam_pmg_max;
+ saved_narrowing = resctrl_using_extra_partids;
+
+ mpam_pmg_max = 127;
+ resctrl_using_extra_partids = true;
+
+ for (partid = 0; partid < 100; partid++) {
+ for (pmg = 0; pmg <= mpam_pmg_max; pmg += 10) {
+ rmid = partid * (mpam_pmg_max + 1) + pmg;
+
+ KUNIT_EXPECT_EQ(test, resctrl_rmid_to_partid(rmid), partid);
+ decoded_pmg = resctrl_rmid_to_pmg(rmid);
+ KUNIT_EXPECT_EQ(test, decoded_pmg, pmg);
+ }
+ }
+
+ mpam_pmg_max = saved_pmg_max;
+ resctrl_using_extra_partids = saved_narrowing;
+}
+
+static void test_narrowing_rmid_idx_encode(struct kunit *test)
+{
+ u32 closid, rmid, idx;
+ bool saved_narrowing;
+ u8 saved_pmg_max;
+
+ saved_pmg_max = mpam_pmg_max;
+ saved_narrowing = resctrl_using_extra_partids;
+
+ mpam_pmg_max = 127;
+
+ resctrl_using_extra_partids = false;
+ for (closid = 0; closid < 10; closid++) {
+ for (rmid = 0; rmid < 10; rmid++) {
+ idx = resctrl_arch_rmid_idx_encode(closid, rmid);
+ KUNIT_EXPECT_EQ(test, idx, closid * 128 + rmid);
+ }
+ }
+
+ resctrl_using_extra_partids = true;
+ for (closid = 0; closid < 10; closid++) {
+ for (rmid = 0; rmid < 1000; rmid += 100) {
+ idx = resctrl_arch_rmid_idx_encode(closid, rmid);
+ KUNIT_EXPECT_EQ(test, idx, rmid);
+ }
+ }
+
+ mpam_pmg_max = saved_pmg_max;
+ resctrl_using_extra_partids = saved_narrowing;
+}
+
+static void test_narrowing_rmid_idx_decode(struct kunit *test)
+{
+ u32 decoded_closid, decoded_rmid;
+ u32 idx, closid, rmid;
+ bool saved_narrowing;
+ u8 saved_pmg_max;
+
+ saved_pmg_max = mpam_pmg_max;
+ saved_narrowing = resctrl_using_extra_partids;
+
+ mpam_pmg_max = 127;
+
+ resctrl_using_extra_partids = false;
+ for (closid = 0; closid < 10; closid++) {
+ for (rmid = 0; rmid < 10; rmid++) {
+ idx = closid * 128 + rmid;
+ resctrl_arch_rmid_idx_decode(idx, &decoded_closid, &decoded_rmid);
+ KUNIT_EXPECT_EQ(test, decoded_closid, closid);
+ KUNIT_EXPECT_EQ(test, decoded_rmid, rmid);
+ }
+ }
+
+ resctrl_using_extra_partids = true;
+ mpam_partid_map_init(64, 32);
+ for (idx = 0; idx < 5000; idx += 500) {
+ u16 partid = resctrl_rmid_to_partid(idx);
+ u16 intpartid = mpam_partid_to_intpartid(partid);
+
+ resctrl_arch_rmid_idx_decode(idx, &decoded_closid, &decoded_rmid);
+ KUNIT_EXPECT_EQ(test, decoded_closid, intpartid);
+ KUNIT_EXPECT_EQ(test, decoded_rmid, idx);
+ }
+ mpam_partid_map_init(0, 0);
+
+ mpam_pmg_max = saved_pmg_max;
+ resctrl_using_extra_partids = saved_narrowing;
+}
+
+struct partid_mapping_case {
+ u16 partid;
+ u16 num_partid;
+ u16 num_closid;
+ u16 expected_intpartid;
+ const char *desc;
+};
+
+static const struct partid_mapping_case partid_mapping_cases[] = {
+ { 0, 128, 32, 0, "First PARTID maps to first intPARTID" },
+ { 31, 128, 32, 31, "Last intPARTID maps 1:1" },
+ { 32, 128, 32, 0, "First extra PARTID wraps to intPARTID 0" },
+ { 33, 128, 32, 1, "Second extra PARTID wraps to intPARTID 1" },
+ { 63, 128, 32, 31, "PARTID 63 wraps to intPARTID 31" },
+ { 64, 128, 32, 0, "PARTID 64 wraps to intPARTID 0" },
+ { 127, 128, 32, 31, "Max PARTID wraps to intPARTID 31" },
+ { 15, 64, 16, 15, "Small config: PARTID 15 → intPARTID 15" },
+ { 16, 64, 16, 0, "Small config: PARTID 16 wraps to 0" },
+ { 63, 64, 16, 15, "Small config: max PARTID wraps to 15" },
+};
+
+static void partid_mapping_desc(const struct partid_mapping_case *param,
+ char *desc)
+{
+ snprintf(desc, KUNIT_PARAM_DESC_SIZE, "%s", param->desc);
+}
+
+KUNIT_ARRAY_PARAM(partid_mapping, partid_mapping_cases, partid_mapping_desc);
+
+static void test_narrowing_partid_mapping(struct kunit *test)
+{
+ const struct partid_mapping_case *param = test->param_value;
+ u16 result;
+
+ mpam_partid_map_init(param->num_partid, param->num_closid);
+
+ result = mpam_partid_to_intpartid(param->partid);
+ KUNIT_EXPECT_EQ(test, result, param->expected_intpartid);
+
+ mpam_partid_map_init(0, 0);
+}
+
+struct partid_in_closid_case {
+ u16 partid;
+ u32 closid;
+ bool cdp_enabled;
+ bool narrowing_enabled;
+ u16 num_closid;
+ bool expected;
+ const char *desc;
+};
+
+static const struct partid_in_closid_case partid_in_closid_cases[] = {
+ { 10, 10, false, false, 32, true, "Legacy: exact match" },
+ { 10, 11, false, false, 32, false, "Legacy: no match" },
+ { 20, 10, true, false, 32, true, "CDP: even PARTID for closid 10" },
+ { 21, 10, true, false, 32, true, "CDP: odd PARTID for closid 10" },
+ { 22, 10, true, false, 32, false, "CDP: PARTID 22 not in closid 10" },
+ { 10, 10, false, true, 32, true, "Narrowing: PARTID 10 -> intPARTID 10" },
+ { 42, 10, false, true, 32, true, "Narrowing: PARTID 42 wraps to intPARTID 10" },
+ { 74, 10, false, true, 32, true, "Narrowing: PARTID 74 wraps to intPARTID 10" },
+ { 43, 10, false, true, 32, false, "Narrowing: PARTID 43 -> intPARTID 11" },
+ { 20, 10, true, true, 32, true, "CDP+narrow: even PARTID 20 -> intPARTID 10" },
+ { 21, 10, true, true, 32, true, "CDP+narrow: odd PARTID 21 -> intPARTID 10" },
+ { 84, 10, true, true, 32, true, "CDP+narrow: PARTID 84 wraps to intPARTID 10" },
+ { 86, 10, true, true, 32, false, "CDP+narrow: PARTID 86 -> intPARTID 11" },
+};
+
+static void partid_in_closid_desc(const struct partid_in_closid_case *param,
+ char *desc)
+{
+ snprintf(desc, KUNIT_PARAM_DESC_SIZE, "%s", param->desc);
+}
+
+KUNIT_ARRAY_PARAM(partid_in_closid, partid_in_closid_cases,
+ partid_in_closid_desc);
+
+static void test_narrowing_partid_in_closid(struct kunit *test)
+{
+ const struct partid_in_closid_case *param = test->param_value;
+ bool saved_cdp, saved_narrowing;
+ bool result;
+
+ saved_cdp = cdp_enabled;
+ saved_narrowing = resctrl_using_extra_partids;
+
+ cdp_enabled = param->cdp_enabled;
+ resctrl_using_extra_partids = param->narrowing_enabled;
+ mpam_partid_map_init(128, param->num_closid);
+
+ result = resctrl_partid_in_closid(param->partid, param->closid);
+ KUNIT_EXPECT_EQ(test, result, param->expected);
+
+ mpam_partid_map_init(0, 0);
+ cdp_enabled = saved_cdp;
+ resctrl_using_extra_partids = saved_narrowing;
+}
+
+static void test_narrowing_boundary_conditions(struct kunit *test)
+{
+ bool saved_narrowing;
+ u8 saved_pmg_max;
+ u32 rmid, idx;
+ u16 partid;
+ u8 pmg;
+
+ saved_pmg_max = mpam_pmg_max;
+ saved_narrowing = resctrl_using_extra_partids;
+
+ resctrl_using_extra_partids = true;
+
+ mpam_pmg_max = 255;
+ rmid = 255 * 256 + 255;
+ partid = resctrl_rmid_to_partid(rmid);
+ pmg = resctrl_rmid_to_pmg(rmid);
+ KUNIT_EXPECT_EQ(test, partid, 255);
+ KUNIT_EXPECT_EQ(test, pmg, 255);
+
+ mpam_pmg_max = 127;
+ rmid = 100 * 128 + 127;
+ partid = resctrl_rmid_to_partid(rmid);
+ pmg = resctrl_rmid_to_pmg(rmid);
+ KUNIT_EXPECT_EQ(test, partid, 100);
+ KUNIT_EXPECT_EQ(test, pmg, 127);
+
+ mpam_pmg_max = 0;
+ rmid = 50;
+ partid = resctrl_rmid_to_partid(rmid);
+ pmg = resctrl_rmid_to_pmg(rmid);
+ KUNIT_EXPECT_EQ(test, partid, 50);
+ KUNIT_EXPECT_EQ(test, pmg, 0);
+
+ mpam_pmg_max = 15;
+ idx = resctrl_arch_rmid_idx_encode(0, 1000);
+ KUNIT_EXPECT_EQ(test, idx, 1000);
+
+ mpam_pmg_max = saved_pmg_max;
+ resctrl_using_extra_partids = saved_narrowing;
+}
+
+static void test_narrowing_full_range_mapping(struct kunit *test)
+{
+ u16 partid, intpartid;
+ bool saved_narrowing;
+ u8 saved_pmg_max;
+ int count = 0;
+ u32 rmid;
+
+ saved_pmg_max = mpam_pmg_max;
+ saved_narrowing = resctrl_using_extra_partids;
+
+ mpam_pmg_max = 31;
+ resctrl_using_extra_partids = true;
+ mpam_partid_map_init(64, 16);
+
+ for (rmid = 0; rmid < 2048; rmid += 32) {
+ partid = resctrl_rmid_to_partid(rmid);
+ intpartid = mpam_partid_to_intpartid(partid);
+
+ KUNIT_EXPECT_LT(test, partid, 64);
+ KUNIT_EXPECT_LT(test, intpartid, 16);
+ KUNIT_EXPECT_EQ(test, intpartid, partid % 16);
+
+ count++;
+ }
+
+ KUNIT_EXPECT_GT(test, count, 0);
+
+ mpam_partid_map_init(0, 0);
+ mpam_pmg_max = saved_pmg_max;
+ resctrl_using_extra_partids = saved_narrowing;
+}
+
static struct kunit_case mpam_resctrl_test_cases[] = {
KUNIT_CASE(test_get_mba_granularity),
KUNIT_CASE_PARAM(test_mbw_max_to_percent, test_percent_value_gen_params),
@@ -304,6 +760,16 @@ static struct kunit_case mpam_resctrl_test_cases[] = {
KUNIT_CASE(test_percent_to_max_rounding),
KUNIT_CASE_PARAM(test_percent_max_roundtrip_stability,
test_all_bwa_wd_gen_params),
+ KUNIT_CASE_PARAM(test_cdp_narrowing_mapping, cdp_narrowing_gen_params),
+ KUNIT_CASE(test_cdp_narrowing_consistency),
+ KUNIT_CASE(test_narrowing_rmid_encode_decode),
+ KUNIT_CASE(test_narrowing_rmid_idx_encode),
+ KUNIT_CASE(test_narrowing_rmid_idx_decode),
+ KUNIT_CASE_PARAM(test_narrowing_partid_mapping, partid_mapping_gen_params),
+ KUNIT_CASE_PARAM(test_narrowing_partid_in_closid,
+ partid_in_closid_gen_params),
+ KUNIT_CASE(test_narrowing_boundary_conditions),
+ KUNIT_CASE(test_narrowing_full_range_mapping),
{}
};
--
2.43.0
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH v10 14/15] arm_mpam: Use the request PARTIDs beyond the internal range
2026-10-19 7:46 ` [PATCH v10 14/15] arm_mpam: Use the request PARTIDs beyond the internal range Jinjie Ruan
@ 2026-10-19 8:28 ` Jinjie Ruan
0 siblings, 0 replies; 3+ messages in thread
From: Jinjie Ruan @ 2026-10-19 8:28 UTC (permalink / raw)
To: babu.moger, ben.horgan, bp, catalin.marinas, dave.hansen,
Dave.Martin, fenghuay, gshan, hpa, james.morse, jic23, mingo,
reinette.chatre, ruanjinjie, tan.shaopeng, tglx, tony.luck,
zengheng4, x86, linux-kernel
This patch is part of the v10 series. For the main thread, see:
https://lore.kernel.org/all/tencent_28FE31D80A7A9AEEC43B22D0AD8894E30C06@qq.com/#t
在 2026/10/19 15:46, Jinjie Ruan 写道:
> From: Jinjie Ruan <ruanjinjie@huawei.com>
>
> A class that implements PARTID narrowing maps several request PARTIDs
> onto one internal PARTID. The internal PARTIDs hold its controls, and
> the request PARTIDs can each be a monitoring group. This gives one
> control group more monitoring groups than the PMG count alone allows.
>
> The extra request PARTIDs can only be used when no class would lose a
> non-aliasing control: its value can not be written to every PARTID of a
> control group, whereas an aliasing control's value can. Every class is
> considered, including the ones resctrl does not use, so that an unknown
> class can still gain a resctrl schema later. This is decided once,
> before resctrl_init(), and there is no user interface for it.
>
> The driver stops reducing msc->partid_max, so the full request PARTID
> range is available, and the resctrl glue code starts handing out the
> extra ones. A control group's configuration is written to every request
> PARTID that maps onto it.
>
> Cc: James Morse <james.morse@arm.com>
> Cc: Ben Horgan <ben.horgan@arm.com>
> Cc: Reinette Chatre <reinette.chatre@intel.com>
> Cc: Fenghua Yu <fenghuay@nvidia.com>
> Suggested-by: James Morse <james.morse@arm.com>
> Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
> ---
> v9->v10:
> - Wrap the intpartid mapping into the internal PARTID range when resctrl
> is not managing the mapping
> ---
> drivers/resctrl/mpam_devices.c | 25 +++++++++++-
> drivers/resctrl/mpam_internal.h | 2 +
> drivers/resctrl/mpam_resctrl.c | 72 ++++++++++++++++++++++++++++++++-
> 3 files changed, 96 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c
> index f81a2df11428..79bc3339a762 100644
> --- a/drivers/resctrl/mpam_devices.c
> +++ b/drivers/resctrl/mpam_devices.c
> @@ -74,6 +74,18 @@ static DEFINE_SPINLOCK(partid_max_lock);
> static u32 mpam_num_closid;
> static u32 mpam_num_partid;
>
> +void mpam_partid_map_init(u32 num_partid, u32 num_closid)
> +{
> + mpam_num_partid = num_partid;
> + mpam_num_closid = num_closid;
> +}
> +
> +void mpam_partid_map_exit(void)
> +{
> + mpam_num_closid = 0;
> + mpam_num_partid = 0;
> +}
> +
> /*
> * Map a request PARTID onto its owning control group using modulo
> * interleaving. Without resctrl active, every PARTID maps 1:1 onto itself.
> @@ -959,6 +971,9 @@ static void mpam_ris_hw_probe(struct mpam_msc_ris *ris)
> * A RIS that implements PARTID narrowing has one configuration per
> * internal PARTID, and several request PARTIDs share it. Record both
> * limits in the resource properties.
> + * Keep msc->partid_max at the full request PARTID range. The resctrl
> + * glue code will broadcast a control group's configuration to every
> + * request PARTID that maps onto that group's internal PARTID.
> */
> props->partid_max = msc->partid_max;
> props->intpartid_max = msc->partid_max;
> @@ -969,7 +984,6 @@ static void mpam_ris_hw_probe(struct mpam_msc_ris *ris)
>
> mpam_set_feature(mpam_feat_partid_nrw, props);
> props->intpartid_max = min(props->intpartid_max, intpartid_max);
> - msc->partid_max = min(msc->partid_max, intpartid_max);
> }
> }
>
> @@ -1581,6 +1595,15 @@ static void mpam_reprogram_ris_partid(struct mpam_msc_ris *ris, u16 partid,
> /* Update the intpartid mapping */
> u16 intpartid = mpam_partid_to_intpartid(partid);
>
> + /*
> + * Without resctrl managing the mapping, the fallback maps
> + * every PARTID 1:1, which can exceed the internal PARTID
> + * range. Wrap onto an internal PARTID, like the narrowing
> + * mapping resctrl programs.
> + */
> + if (intpartid > rprops->intpartid_max)
> + intpartid %= (u32)rprops->intpartid_max + 1;
> +
> mpam_write_partsel_reg(msc, INTPARTID,
> MPAMCFG_INTPARTID_INTERNAL | intpartid);
>
> diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_internal.h
> index a4f2fdf7ac95..b62a0ecdd69f 100644
> --- a/drivers/resctrl/mpam_internal.h
> +++ b/drivers/resctrl/mpam_internal.h
> @@ -478,6 +478,8 @@ extern u16 mpam_partid_max;
> extern u8 mpam_pmg_max;
>
> u16 mpam_partid_to_intpartid(u16 partid);
> +void mpam_partid_map_init(u32 num_partid, u32 num_closid);
> +void mpam_partid_map_exit(void);
>
> /* Scheduled work callback to enable mpam once all MSC have been probed */
> void mpam_enable(struct work_struct *work);
> diff --git a/drivers/resctrl/mpam_resctrl.c b/drivers/resctrl/mpam_resctrl.c
> index d60dea846486..fb997b9d41d6 100644
> --- a/drivers/resctrl/mpam_resctrl.c
> +++ b/drivers/resctrl/mpam_resctrl.c
> @@ -62,6 +62,9 @@ static DEFINE_MUTEX(domain_list_lock);
> */
> static bool cdp_enabled;
>
> +static u32 resctrl_num_closid = 1;
> +static u32 resctrl_num_mon_partid = 1;
> +
> /* If resctrl is leveraging extra request PARTIDs for monitoring. */
> static bool resctrl_using_extra_partids;
>
> @@ -269,12 +272,12 @@ static bool mpam_resctrl_hide_cdp(enum resctrl_res_level rid)
> */
> u32 resctrl_arch_get_num_closid(struct rdt_resource *ignored)
> {
> - return mpam_partid_max + 1;
> + return resctrl_num_closid;
> }
>
> u32 resctrl_arch_system_num_rmid_idx(void)
> {
> - return (mpam_pmg_max + 1) * (mpam_partid_max + 1);
> + return (mpam_pmg_max + 1) * resctrl_num_mon_partid;
> }
>
> /* The request PARTID and PMG a resctrl RMID maps onto. */
> @@ -1931,6 +1934,65 @@ void mpam_resctrl_offline_cpu(unsigned int cpu)
> }
> }
>
> +/*
> + * Decide how the PARTID space is presented to resctrl. This runs once the
> + * classes are known, and before resctrl_init() advertises any limits to
> + * user-space.
> + *
> + * The extra request PARTIDs on a class that implements narrowing can be used
> + * as monitoring groups. That is only safe when no class would lose a
> + * non-aliasing control: its value can not be written to each PARTID of a
> + * control group, whereas an aliasing control's value can. A class that
> + * implements narrowing doesn't have the problem, as its PARTIDs are already
> + * configured as one.
> + *
> + * Every class is considered, including the ones resctrl doesn't use. An
> + * unknown class may gain a resctrl schema later, and using the extra PARTIDs
> + * would prevent that.
> + */
> +static void mpam_resctrl_configure_partids(void)
> +{
> + u32 num_closid = (u32)mpam_partid_max + 1;
> + bool can_expand = false, blocked = false;
> + struct mpam_class *class;
> +
> + guard(srcu)(&mpam_srcu);
> + list_for_each_entry_srcu(class, &mpam_classes, classes_list,
> + srcu_read_lock_held(&mpam_srcu)) {
> + struct mpam_props *cprops = &class->props;
> +
> + num_closid = min(num_closid, (u32)cprops->intpartid_max + 1);
> +
> + if (mpam_has_feature(mpam_feat_partid_nrw, cprops)) {
> + if (cprops->intpartid_max < cprops->partid_max)
> + can_expand = true;
> + } else if (mpam_props_has_non_aliasing_controls(cprops)) {
> + pr_debug("class %u has non-aliasing controls without narrowing\n",
> + class->level);
> + blocked = true;
> + }
> + }
> +
> + if (!num_closid)
> + num_closid = 1;
> +
> + resctrl_num_closid = num_closid;
> + if (can_expand && !blocked) {
> + resctrl_using_extra_partids = true;
> + resctrl_num_mon_partid = (u32)mpam_partid_max + 1;
> + } else {
> + resctrl_using_extra_partids = false;
> + resctrl_num_mon_partid = num_closid;
> + }
> +
> + /* Tell the driver how the request PARTIDs map onto control groups. */
> + mpam_partid_map_init(resctrl_num_mon_partid, resctrl_num_closid);
> +
> + pr_debug("PARTIDs: monitor=%u closid=%u extra=%u\n",
> + resctrl_num_mon_partid, resctrl_num_closid,
> + resctrl_using_extra_partids);
> +}
> +
> int mpam_resctrl_setup(void)
> {
> int err = 0;
> @@ -1952,6 +2014,9 @@ int mpam_resctrl_setup(void)
> mpam_resctrl_pick_caches();
> mpam_resctrl_pick_mba();
>
> + /* Decide how the request PARTIDs map onto the control groups */
> + mpam_resctrl_configure_partids();
> +
> /* Initialise the resctrl structures from the classes */
> for_each_mpam_resctrl_control(res, rid) {
> if (!res->class)
> @@ -2007,6 +2072,9 @@ void mpam_resctrl_exit(void)
>
> WRITE_ONCE(resctrl_enabled, false);
> resctrl_exit();
> +
> + /* resctrl is gone: forget how the PARTIDs were grouped. */
> + mpam_partid_map_exit();
> }
>
> static void mpam_resctrl_teardown_mon(struct mpam_resctrl_mon *mon, struct mpam_class *class)
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-10-19 8:28 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
[not found] <20261019074609.2144405-1-2118537295@qq.com>
2026-10-19 7:46 ` [PATCH v10 14/15] arm_mpam: Use the request PARTIDs beyond the internal range Jinjie Ruan
2026-10-19 8:28 ` Jinjie Ruan
2026-10-19 7:46 ` [PATCH v10 15/15] arm_mpam: resctrl: Add kunit tests for narrow PARTID helpers Jinjie Ruan
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®