mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [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®