From: Fenghua Yu <fenghuay@nvidia.com>
To: Reinette Chatre <reinette.chatre@intel.com>,
Tony Luck <tony.luck@intel.com>, Ben Horgan <ben.horgan@arm.com>,
James Morse <james.morse@arm.com>,
Dave Martin <Dave.Martin@arm.com>, Will Deacon <will@kernel.org>,
Catalin Marinas <catalin.marinas@arm.com>,
Shaopeng Tan <tan.shaopeng@fujitsu.com>,
Chen Yu <yu.c.chen@intel.com>, Babu Moger <babu.moger@amd.com>,
Drew Fustini <fustini@kernel.org>,
Vikram Sethi <vsethi@nvidia.com>,
Shanker Donthineni <sdonthineni@nvidia.com>,
Newton Liu <newtonl@nvidia.com>,
Richard Cheng <icheng@nvidia.com>
Cc: linux-kernel@vger.kernel.org,
linux-arm-kernel@lists.infradead.org,
Fenghua Yu <fenghuay@nvidia.com>
Subject: [PATCH RFC v2 19/19] selftests/resctrl: Add MB emulation test for ARM MPAM
Date: Mon, 31 Aug 2026 10:22:45 -0700 [thread overview]
Message-ID: <20260831172245.42253-20-fenghuay@nvidia.com> (raw)
In-Reply-To: <20260831172245.42253-1-fenghuay@nvidia.com>
Add a kselftest that exercises MB control emulation on ARM MPAM:
info/MB/control_mode, the nested MB_NODE control under
info/MB/schemata/MB/, and schemata visibility when switching legacy
(MB:) and native (MB_NODE:) modes. The info/ schemata tree stays nested
across the switch.
Signed-off-by: Fenghua Yu <fenghuay@nvidia.com>
---
.../selftests/resctrl/mb_emulation_test.c | 314 ++++++++++++++++++
tools/testing/selftests/resctrl/resctrl.h | 1 +
.../testing/selftests/resctrl/resctrl_tests.c | 1 +
3 files changed, 316 insertions(+)
create mode 100644 tools/testing/selftests/resctrl/mb_emulation_test.c
diff --git a/tools/testing/selftests/resctrl/mb_emulation_test.c b/tools/testing/selftests/resctrl/mb_emulation_test.c
new file mode 100644
index 000000000000..845aa46fd092
--- /dev/null
+++ b/tools/testing/selftests/resctrl/mb_emulation_test.c
@@ -0,0 +1,314 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MB (memory bandwidth) control emulation test (ARM MPAM)
+ *
+ * Exercises info/MB/control_mode, the nested MB_NODE control under
+ * info/MB/schemata/MB/, and schemata visibility when switching between
+ * legacy (MB:) and native (MB_NODE:) modes.
+ */
+#include <fcntl.h>
+#include <limits.h>
+
+#include "resctrl.h"
+
+#define MB_INFO INFO_PATH "/MB"
+#define MB_MODE_PATH MB_INFO "/control_mode"
+#define MB_SCHEMATA_DIR MB_INFO "/schemata"
+#define MB_CTRL_PATH MB_SCHEMATA_DIR "/MB"
+#define MB_NODE_NESTED_PATH MB_CTRL_PATH "/MB_NODE"
+#define MB_NODE_SIBLING_PATH MB_SCHEMATA_DIR "/MB_NODE"
+#define ROOT_SCHEMATA_PATH RESCTRL_PATH "/schemata"
+
+static int read_file(const char *path, char *buf, size_t buflen)
+{
+ ssize_t ret;
+ int fd, n;
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ return -errno;
+
+ ret = read(fd, buf, buflen - 1);
+ close(fd);
+ if (ret < 0)
+ return -errno;
+
+ n = (int)ret;
+ while (n > 0 && (buf[n - 1] == '\n' || buf[n - 1] == ' '))
+ n--;
+ buf[n] = '\0';
+
+ return 0;
+}
+
+static bool path_is_dir(const char *path)
+{
+ struct stat st;
+
+ if (stat(path, &st))
+ return false;
+
+ return S_ISDIR(st.st_mode);
+}
+
+static int mb_read_mode(char *buf, size_t len)
+{
+ return read_file(MB_MODE_PATH, buf, len);
+}
+
+static int mb_write_mode(const char *mode)
+{
+ char msg[32];
+ int fd, ret, len;
+
+ len = snprintf(msg, sizeof(msg), "%s\n", mode);
+ if (len < 0 || len >= (int)sizeof(msg))
+ return -EINVAL;
+
+ fd = open(MB_MODE_PATH, O_WRONLY);
+ if (fd < 0)
+ return -errno;
+
+ ret = write(fd, msg, len) == len ? 0 : -errno;
+ close(fd);
+
+ return ret;
+}
+
+static bool mb_mode_active_is(const char *mode_text, const char *mode)
+{
+ char pattern[32];
+
+ snprintf(pattern, sizeof(pattern), "[%s]", mode);
+ return strstr(mode_text, pattern);
+}
+
+static int mb_read_root_schemata(char *buf, size_t len)
+{
+ return read_file(ROOT_SCHEMATA_PATH, buf, len);
+}
+
+/*
+ * resctrl right-aligns schemata names with leading whitespace, so a control
+ * line looks like " MB:0=100...". Treat prefix as matching when it
+ * appears at the start of a line, ignoring any leading spaces or tabs.
+ */
+static const char *schemata_find_line(const char *schemata, const char *prefix)
+{
+ const char *p;
+
+ if (!schemata || !prefix)
+ return NULL;
+
+ for (p = strstr(schemata, prefix); p; p = strstr(p + 1, prefix)) {
+ const char *q = p;
+
+ while (q > schemata && (q[-1] == ' ' || q[-1] == '\t'))
+ q--;
+
+ if (q == schemata || q[-1] == '\n')
+ return p;
+ }
+
+ return NULL;
+}
+
+static bool schemata_has_prefix(const char *schemata, const char *prefix)
+{
+ return schemata_find_line(schemata, prefix);
+}
+
+static int mb_expect_nested_hierarchy(const char *why)
+{
+ if (!path_is_dir(MB_NODE_NESTED_PATH) || path_is_dir(MB_NODE_SIBLING_PATH)) {
+ ksft_print_msg("%s: MB_NODE should be nested, nested=%d sibling=%d\n",
+ why, path_is_dir(MB_NODE_NESTED_PATH),
+ path_is_dir(MB_NODE_SIBLING_PATH));
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int mb_test_mode_read(void)
+{
+ char mode[64];
+ int ret;
+
+ ret = mb_read_mode(mode, sizeof(mode));
+ if (ret)
+ return ret;
+
+ if (!mb_mode_active_is(mode, "legacy") && !mb_mode_active_is(mode, "native")) {
+ ksft_print_msg("mode file has no active mode: '%s'\n", mode);
+ return -EINVAL;
+ }
+
+ ksft_print_msg("mode file: %s\n", mode);
+ return 0;
+}
+
+static int mb_test_schemata_visibility(bool expect_mb_line, const char *mode)
+{
+ char schemata[4096];
+ bool has_mb, has_mb_node;
+ int ret;
+
+ ret = mb_read_root_schemata(schemata, sizeof(schemata));
+ if (ret)
+ return ret;
+
+ has_mb = schemata_has_prefix(schemata, "MB:");
+ has_mb_node = schemata_has_prefix(schemata, "MB_NODE:");
+
+ if (expect_mb_line) {
+ if (!has_mb) {
+ ksft_print_msg("%s: expected MB: line in schemata\n", mode);
+ return -EINVAL;
+ }
+ if (has_mb_node) {
+ ksft_print_msg("%s: MB_NODE: line should be hidden in schemata\n",
+ mode);
+ return -EINVAL;
+ }
+ } else {
+ if (has_mb) {
+ ksft_print_msg("%s: MB: line should be hidden in schemata\n",
+ mode);
+ return -EINVAL;
+ }
+ if (!has_mb_node) {
+ ksft_print_msg("%s: expected MB_NODE: line in schemata\n",
+ mode);
+ return -EINVAL;
+ }
+ }
+
+ ksft_print_msg("switched to %s: schemata MB:%d MB_NODE:%d\n",
+ mode, has_mb, has_mb_node);
+ return 0;
+}
+
+static int mb_restore_mode(const char *orig_mode)
+{
+ if (mb_mode_active_is(orig_mode, "legacy"))
+ return mb_write_mode("legacy");
+ if (mb_mode_active_is(orig_mode, "native"))
+ return mb_write_mode("native");
+
+ return 0;
+}
+
+static int mb_verify_active_mode(const char *mode)
+{
+ char buf[64];
+ int ret;
+
+ ret = mb_read_mode(buf, sizeof(buf));
+ if (ret)
+ return ret;
+
+ if (!mb_mode_active_is(buf, mode)) {
+ ksft_print_msg("mode switch to %s failed, mode file: '%s'\n",
+ mode, buf);
+ return -EINVAL;
+ }
+
+ ksft_print_msg("switched to %s: mode file='%s'\n", mode, buf);
+ return 0;
+}
+
+/*
+ * Switch to @mode and verify the mode file, the nested info/MB/schemata
+ * layout (unchanged by the switch), and root schemata visibility.
+ */
+static int mb_switch_and_check(const char *mode)
+{
+ bool legacy = !strcmp(mode, "legacy");
+ int ret;
+
+ ksft_print_msg("--- switching to %s mode ---\n", mode);
+
+ ret = mb_write_mode(mode);
+ if (ret) {
+ ksft_print_msg("failed to write %s mode\n", mode);
+ return ret;
+ }
+
+ ret = mb_verify_active_mode(mode);
+ if (ret)
+ return ret;
+
+ ret = mb_expect_nested_hierarchy(mode);
+ if (ret)
+ return ret;
+
+ return mb_test_schemata_visibility(legacy, mode);
+}
+
+static int mb_emulation_run_test(const struct resctrl_test *test,
+ const struct user_params *uparams)
+{
+ char orig_mode[64];
+ int ret;
+
+ (void)test;
+ (void)uparams;
+
+ ret = mb_expect_nested_hierarchy("initial");
+ if (ret)
+ return ret;
+
+ ret = mb_read_mode(orig_mode, sizeof(orig_mode));
+ if (ret) {
+ ksft_print_msg("failed to read %s\n", MB_MODE_PATH);
+ return ret;
+ }
+
+ ret = mb_test_mode_read();
+ if (ret)
+ goto out_restore;
+
+ /* Cycle legacy -> native -> legacy, checking each transition. */
+ ret = mb_switch_and_check("legacy");
+ if (ret)
+ goto out_restore;
+
+ ret = mb_switch_and_check("native");
+ if (ret)
+ goto out_restore;
+
+ ret = mb_switch_and_check("legacy");
+ if (ret)
+ goto out_restore;
+
+out_restore:
+ if (mb_restore_mode(orig_mode))
+ ksft_print_msg("warning: failed to restore original mode\n");
+
+ return ret;
+}
+
+static bool mb_emulation_feature_check(const struct resctrl_test *test)
+{
+ (void)test;
+
+ if (!resctrl_resource_exists("MB"))
+ return false;
+
+ if (!resource_info_file_exists("MB", "control_mode"))
+ return false;
+
+ if (!path_is_dir(MB_SCHEMATA_DIR) || !path_is_dir(MB_CTRL_PATH))
+ return false;
+
+ return path_is_dir(MB_NODE_NESTED_PATH);
+}
+
+struct resctrl_test mb_emulation_test = {
+ .name = "mb_emulation",
+ .group = "mb",
+ .resource = "MB",
+ .feature_check = mb_emulation_feature_check,
+ .run_test = mb_emulation_run_test,
+};
diff --git a/tools/testing/selftests/resctrl/resctrl.h b/tools/testing/selftests/resctrl/resctrl.h
index 175101022bf3..ad1c17c0b0bf 100644
--- a/tools/testing/selftests/resctrl/resctrl.h
+++ b/tools/testing/selftests/resctrl/resctrl.h
@@ -242,5 +242,6 @@ extern struct resctrl_test cmt_test;
extern struct resctrl_test l3_cat_test;
extern struct resctrl_test l3_noncont_cat_test;
extern struct resctrl_test l2_noncont_cat_test;
+extern struct resctrl_test mb_emulation_test;
#endif /* RESCTRL_H */
diff --git a/tools/testing/selftests/resctrl/resctrl_tests.c b/tools/testing/selftests/resctrl/resctrl_tests.c
index dbcd5eea9fbc..593f0ca5251b 100644
--- a/tools/testing/selftests/resctrl/resctrl_tests.c
+++ b/tools/testing/selftests/resctrl/resctrl_tests.c
@@ -17,6 +17,7 @@ volatile int *value_sink = &sink_target;
static struct resctrl_test *resctrl_tests[] = {
&mbm_test,
&mba_test,
+ &mb_emulation_test,
&cmt_test,
&l3_cat_test,
&l3_noncont_cat_test,
--
2.53.0
next prev parent reply other threads:[~2026-08-31 17:23 UTC|newest]
Thread overview: 23+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-31 17:22 [PATCH RFC v2 00/19] arm,fs/resctrl: ARM MPAM MB_NODE support Fenghua Yu
2026-08-31 17:22 ` [PATCH RFC v2 01/19] resctrl: De-hardcode L3 monitor infrastructure Fenghua Yu
2026-08-31 17:22 ` [PATCH RFC v2 02/19] resctrl: Expose MBA MBM counter assignment sysfs Fenghua Yu
2026-08-31 17:22 ` [PATCH RFC v2 03/19] resctrl: name node-scoped monitor domains mon_NODE_<id> Fenghua Yu
2026-08-31 17:22 ` [PATCH RFC v2 04/19] resctrl: Add node-scope MBM total event Fenghua Yu
2026-08-31 17:22 ` [PATCH RFC v2 05/19] resctrl: Make MBM paths resource-aware Fenghua Yu
2026-08-31 17:22 ` [PATCH RFC v2 06/19] arm_mpam: Support memory-level MSCs and ABMC per class Fenghua Yu
2026-08-31 17:22 ` [PATCH RFC v2 07/19] arm_mpam: Refine L3 topology and class selection Fenghua Yu
2026-08-31 17:22 ` [PATCH RFC v2 08/19] arm_mpam: Include all MSC components during domain setup Fenghua Yu
2026-08-31 17:22 ` [PATCH RFC v2 09/19] fs/resctrl: Take memory hotplug lock whenever taking CPU hotplug lock Fenghua Yu
2026-08-31 17:22 ` [PATCH RFC v2 10/19] arm_mpam: Handle CPU-less numa nodes Fenghua Yu
2026-08-31 17:22 ` [PATCH RFC v2 11/19] arm_mpam: Emulate MB control with node-scoped MB_NODE control Fenghua Yu
2026-08-31 17:22 ` [PATCH RFC v2 12/19] arm_mpam: resctrl: Add NUMA node notifier for domain online/offline Fenghua Yu
2026-09-07 11:12 ` Ben Horgan
2026-08-31 17:22 ` [PATCH RFC v2 13/19] resctrl: Add mbm_assign_scope_mode for native assignment file names Fenghua Yu
2026-08-31 17:22 ` [PATCH RFC v2 14/19] Documentation: resctrl: document mbm_assign_scope_mode Fenghua Yu
2026-08-31 17:22 ` [PATCH RFC v2 15/19] Documentation: arm64: mpam: document memory-level MB control and NUMA nodes Fenghua Yu
2026-08-31 17:22 ` [PATCH RFC v2 16/19] Documentation: resctrl: document NODE-scoped MBA domains and mon_NODE monitoring Fenghua Yu
2026-08-31 17:22 ` [PATCH RFC v2 17/19] Documentation: resctrl: document MB_NODE emulation example on ARM MPAM Fenghua Yu
2026-08-31 17:22 ` [PATCH RFC v2 18/19] arm_mpam: Add KUnit test for CPU-less NUMA node affinity Fenghua Yu
2026-08-31 17:22 ` Fenghua Yu [this message]
2026-09-01 9:37 ` [PATCH RFC v2 00/19] arm,fs/resctrl: ARM MPAM MB_NODE support Richard Cheng
2026-09-07 11:20 ` Ben Horgan
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260831172245.42253-20-fenghuay@nvidia.com \
--to=fenghuay@nvidia.com \
--cc=Dave.Martin@arm.com \
--cc=babu.moger@amd.com \
--cc=ben.horgan@arm.com \
--cc=catalin.marinas@arm.com \
--cc=fustini@kernel.org \
--cc=icheng@nvidia.com \
--cc=james.morse@arm.com \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=newtonl@nvidia.com \
--cc=reinette.chatre@intel.com \
--cc=sdonthineni@nvidia.com \
--cc=tan.shaopeng@fujitsu.com \
--cc=tony.luck@intel.com \
--cc=vsethi@nvidia.com \
--cc=will@kernel.org \
--cc=yu.c.chen@intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.