* [PATCH v5 0/5] selftests/resctrl: Add MBM and MBA test support for AMD
@ 2026-10-22 13:25 Swapnil Sapkal
2026-10-22 13:25 ` [PATCH v5 1/5] selftests/resctrl: Rename variables and functions to generic names Swapnil Sapkal
` (4 more replies)
0 siblings, 5 replies; 11+ messages in thread
From: Swapnil Sapkal @ 2026-10-22 13:25 UTC (permalink / raw)
To: tony.luck, reinette.chatre, babu.moger, fenghua.yu, shuah
Cc: Dave.Martin, james.morse, peternewman, eranian, tan.shaopeng,
linux-kernel, linux-kselftest, x86, Swapnil Sapkal
The MBM (Memory Bandwidth Monitoring) and MBA (Memory Bandwidth
Allocation) resctrl selftests validate the bandwidth numbers reported by
resctrl against the memory controller performance counters. Today this
only works on Intel, where the counters are the iMC (Integrated Memory
Controller) PMUs and the tests are gated behind an ARCH_INTEL vendor
check.
This series teaches the tests about the AMD UMC (Unified Memory
Controller) PMUs and enables MBM and MBA on AMD.
The work is done in a few steps:
1. Rename the Intel specific imc/IMC identifiers to the generic
mc/MC so the code can serve both vendors.
2. Detect the vendor and select the memory controller PMU sysfs name
accordingly, keeping the rest of the code generic.
3. Add the AMD UMC support. Unlike Intel, the UMC PMU does not expose
the CAS command events via sysfs, so the read event is programmed
directly (EventCode 0x0a, umask 0x1). All vendor detection is
contained in enumerate_mcs() and dispatched to per-vendor routines.
On AMD each UMC is a per-NUMA-node PMU, so only the UMCs local to
the benchmark CPU are opened; remote-node UMCs would otherwise fail
perf_event_open() with -ENODEV.
4. Sweep the AMD MBA schemata using absolute bandwidth values (4 GB/s
to 40 GB/s) instead of the Intel style percentages (10% to 100%).
5. Extend the vendor_specific gate of the tests to Intel and AMD so
they run on AMD as well. The gate is kept (rather than removed)
so vendors that enumerate_mcs() does not know about are skipped
instead of reported as a failure.
Testing
-------
Tested on AMD Zen5 (single-numa and multi-NUMA); MBM and MBA tests
pass on both.
This series applies on top of the kselftest tree's "next" branch.
v4: https://lore.kernel.org/all/20260810184806.1089507-1-swapnil.sapkal@amd.com/
Changes since v4:
- Rebased onto the kselftest "next" branch. The memory bandwidth
counters there are now kept in a dynamically managed linked list
(instead of a fixed array), so the whole series was reworked on top of
that infrastructure. The enumeration routine is now enumerate_mcs()
and the AMD path allocates and links each UMC counter.
- No other functional changes relative to v4.
Babu Moger (4):
selftests/resctrl: Rename variables and functions to generic names
selftests/resctrl: Detect vendor to select memory controller sysfs
name
selftests/resctrl: Add support for MBM and MBA tests on AMD
selftests/resctrl: Enable MBM and MBA tests on AMD
Swapnil Sapkal (1):
selftests/resctrl: Use bandwidth values for AMD MBA schemata
tools/testing/selftests/resctrl/mba_test.c | 107 +++--
tools/testing/selftests/resctrl/mbm_test.c | 26 +-
tools/testing/selftests/resctrl/resctrl.h | 6 +-
tools/testing/selftests/resctrl/resctrl_val.c | 384 +++++++++++-------
4 files changed, 334 insertions(+), 189 deletions(-)
--
2.43.0
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v5 1/5] selftests/resctrl: Rename variables and functions to generic names
2026-10-22 13:25 [PATCH v5 0/5] selftests/resctrl: Add MBM and MBA test support for AMD Swapnil Sapkal
@ 2026-10-22 13:25 ` Swapnil Sapkal
2026-10-28 18:38 ` Reinette Chatre
2026-10-22 13:25 ` [PATCH v5 2/5] selftests/resctrl: Detect vendor to select memory controller sysfs name Swapnil Sapkal
` (3 subsequent siblings)
4 siblings, 1 reply; 11+ messages in thread
From: Swapnil Sapkal @ 2026-10-22 13:25 UTC (permalink / raw)
To: tony.luck, reinette.chatre, babu.moger, fenghua.yu, shuah
Cc: Dave.Martin, james.morse, peternewman, eranian, tan.shaopeng,
linux-kernel, linux-kselftest, x86, Swapnil Sapkal
From: Babu Moger <babu.moger@amd.com>
For Intel, the memory controller is called Integrated Memory Controller
(IMC). For AMD, it is called Unified Memory Controller (UMC).
Rename the variables and functions from imc (Integrated Memory
Controller) to the generic mc (Memory Controller) in preparation for
the support of MBM and MBA tests on AMD.
No functional change intended.
Assisted-by: Claude:claude-opus-4.8
Signed-off-by: Babu Moger <babu.moger@amd.com>
Co-developed-by: Swapnil Sapkal <swapnil.sapkal@amd.com>
Signed-off-by: Swapnil Sapkal <swapnil.sapkal@amd.com>
---
v4->v5:
- Rebased onto the kselftest/next branch and reworked on top of the
upstream refactor that moves the memory bandwidth counters into a
dynamically managed linked list; enumerate_imcs() is renamed to
enumerate_mcs() accordingly.
v3->v4:
- Rebased onto v7.2-rc7. Reworked to match the upstream resctrl_val.c
refactor that reads the memory bandwidth counters into a single
read-event array.
- Renamed the remaining Intel specific references missed in v3: the
get_mem_bw_imc() helper, the imc_counter_config comments and the
'Mem_BW' bandwidth label. (Reinette)
tools/testing/selftests/resctrl/mba_test.c | 28 +-
tools/testing/selftests/resctrl/mbm_test.c | 24 +-
tools/testing/selftests/resctrl/resctrl.h | 6 +-
tools/testing/selftests/resctrl/resctrl_val.c | 256 +++++++++---------
4 files changed, 157 insertions(+), 157 deletions(-)
diff --git a/tools/testing/selftests/resctrl/mba_test.c b/tools/testing/selftests/resctrl/mba_test.c
index 4bb1a82eb195..17ae73d3476c 100644
--- a/tools/testing/selftests/resctrl/mba_test.c
+++ b/tools/testing/selftests/resctrl/mba_test.c
@@ -23,7 +23,7 @@ static int mba_init(const struct resctrl_test *test,
{
int ret;
- ret = initialize_read_mem_bw_imc();
+ ret = initialize_read_mem_bw_mc();
if (ret)
return ret;
@@ -73,7 +73,7 @@ static int mba_measure(const struct user_params *uparams,
return measure_read_mem_bw(uparams, param, bm_pid);
}
-static bool show_mba_info(unsigned long *bw_imc, unsigned long *bw_resc)
+static bool show_mba_info(unsigned long *bw_mc, unsigned long *bw_resc)
{
unsigned int allocation;
bool ret = false;
@@ -83,27 +83,27 @@ static bool show_mba_info(unsigned long *bw_imc, unsigned long *bw_resc)
/* Memory bandwidth from 100% down to 10% */
for (allocation = 0; allocation < ALLOCATION_MAX / ALLOCATION_STEP;
allocation++) {
- unsigned long sum_bw_imc = 0, sum_bw_resc = 0;
- long avg_bw_imc, avg_bw_resc;
+ unsigned long sum_bw_mc = 0, sum_bw_resc = 0;
+ long avg_bw_mc, avg_bw_resc;
int avg_diff_per;
float avg_diff;
for (runs = NUM_OF_RUNS * allocation;
runs < NUM_OF_RUNS * allocation + NUM_OF_RUNS ; runs++) {
- sum_bw_imc += bw_imc[runs];
+ sum_bw_mc += bw_mc[runs];
sum_bw_resc += bw_resc[runs];
}
- avg_bw_imc = sum_bw_imc / NUM_OF_RUNS;
+ avg_bw_mc = sum_bw_mc / NUM_OF_RUNS;
avg_bw_resc = sum_bw_resc / NUM_OF_RUNS;
- if (avg_bw_imc < THROTTLE_THRESHOLD || avg_bw_resc < THROTTLE_THRESHOLD) {
+ if (avg_bw_mc < THROTTLE_THRESHOLD || avg_bw_resc < THROTTLE_THRESHOLD) {
ksft_print_msg("Bandwidth below threshold (%d MiB). Dropping results from MBA schemata %u.\n",
THROTTLE_THRESHOLD,
ALLOCATION_MIN + ALLOCATION_STEP * allocation);
continue;
}
- avg_diff = (float)labs(avg_bw_resc - avg_bw_imc) / avg_bw_imc;
+ avg_diff = (float)labs(avg_bw_resc - avg_bw_mc) / avg_bw_mc;
avg_diff_per = (int)(avg_diff * 100);
ksft_print_msg("%s Check MBA diff within %d%% for schemata %u\n",
@@ -113,7 +113,7 @@ static bool show_mba_info(unsigned long *bw_imc, unsigned long *bw_resc)
ALLOCATION_MIN + ALLOCATION_STEP * allocation);
ksft_print_msg("avg_diff_per: %d%%\n", avg_diff_per);
- ksft_print_msg("avg_bw_imc: %lu\n", avg_bw_imc);
+ ksft_print_msg("avg_bw_mc: %lu\n", avg_bw_mc);
ksft_print_msg("avg_bw_resc: %lu\n", avg_bw_resc);
if (avg_diff_per > MAX_DIFF_PERCENT)
ret = true;
@@ -130,7 +130,7 @@ static bool show_mba_info(unsigned long *bw_imc, unsigned long *bw_resc)
static int check_results(void)
{
unsigned long bw_resc[NUM_OF_RUNS * ALLOCATION_MAX / ALLOCATION_STEP];
- unsigned long bw_imc[NUM_OF_RUNS * ALLOCATION_MAX / ALLOCATION_STEP];
+ unsigned long bw_mc[NUM_OF_RUNS * ALLOCATION_MAX / ALLOCATION_STEP];
char *token_array[8], output[] = RESULT_FILE_NAME, temp[512];
int runs;
FILE *fp;
@@ -152,8 +152,8 @@ static int check_results(void)
token = strtok(NULL, ":\t");
}
- /* Field 3 is perf imc value */
- bw_imc[runs] = strtoul(token_array[3], NULL, 0);
+ /* Field 3 is perf mc value */
+ bw_mc[runs] = strtoul(token_array[3], NULL, 0);
/* Field 5 is resctrl value */
bw_resc[runs] = strtoul(token_array[5], NULL, 0);
runs++;
@@ -161,12 +161,12 @@ static int check_results(void)
fclose(fp);
- return show_mba_info(bw_imc, bw_resc);
+ return show_mba_info(bw_mc, bw_resc);
}
static void mba_test_cleanup(void)
{
- cleanup_read_mem_bw_imc();
+ cleanup_read_mem_bw_mc();
remove(RESULT_FILE_NAME);
}
diff --git a/tools/testing/selftests/resctrl/mbm_test.c b/tools/testing/selftests/resctrl/mbm_test.c
index 68c89f50a34a..e24c6237a22f 100644
--- a/tools/testing/selftests/resctrl/mbm_test.c
+++ b/tools/testing/selftests/resctrl/mbm_test.c
@@ -15,21 +15,21 @@
#define NUM_OF_RUNS 5
static int
-show_bw_info(unsigned long *bw_imc, unsigned long *bw_resc, size_t span)
+show_bw_info(unsigned long *bw_mc, unsigned long *bw_resc, size_t span)
{
- unsigned long sum_bw_imc = 0, sum_bw_resc = 0;
- long avg_bw_imc = 0, avg_bw_resc = 0;
+ unsigned long sum_bw_mc = 0, sum_bw_resc = 0;
+ long avg_bw_mc = 0, avg_bw_resc = 0;
int runs, ret, avg_diff_per;
float avg_diff = 0;
for (runs = 0; runs < NUM_OF_RUNS; runs++) {
- sum_bw_imc += bw_imc[runs];
+ sum_bw_mc += bw_mc[runs];
sum_bw_resc += bw_resc[runs];
}
- avg_bw_imc = sum_bw_imc / NUM_OF_RUNS;
+ avg_bw_mc = sum_bw_mc / NUM_OF_RUNS;
avg_bw_resc = sum_bw_resc / NUM_OF_RUNS;
- avg_diff = (float)labs(avg_bw_resc - avg_bw_imc) / avg_bw_imc;
+ avg_diff = (float)labs(avg_bw_resc - avg_bw_mc) / avg_bw_mc;
avg_diff_per = (int)(avg_diff * 100);
ret = avg_diff_per > MAX_DIFF_PERCENT;
@@ -38,7 +38,7 @@ show_bw_info(unsigned long *bw_imc, unsigned long *bw_resc, size_t span)
ksft_print_msg("avg_diff_per: %d%%\n", avg_diff_per);
if (span)
ksft_print_msg("Span (MB): %zu\n", span / MB);
- ksft_print_msg("avg_bw_imc: %lu\n", avg_bw_imc);
+ ksft_print_msg("avg_bw_mc: %lu\n", avg_bw_mc);
ksft_print_msg("avg_bw_resc: %lu\n", avg_bw_resc);
return ret;
@@ -46,7 +46,7 @@ show_bw_info(unsigned long *bw_imc, unsigned long *bw_resc, size_t span)
static int check_results(size_t span)
{
- unsigned long bw_imc[NUM_OF_RUNS], bw_resc[NUM_OF_RUNS];
+ unsigned long bw_mc[NUM_OF_RUNS], bw_resc[NUM_OF_RUNS];
char temp[1024], *token_array[8];
char output[] = RESULT_FILE_NAME;
int runs, ret;
@@ -72,11 +72,11 @@ static int check_results(size_t span)
}
bw_resc[runs] = strtoul(token_array[5], NULL, 0);
- bw_imc[runs] = strtoul(token_array[3], NULL, 0);
+ bw_mc[runs] = strtoul(token_array[3], NULL, 0);
runs++;
}
- ret = show_bw_info(bw_imc, bw_resc, span);
+ ret = show_bw_info(bw_mc, bw_resc, span);
fclose(fp);
@@ -89,7 +89,7 @@ static int mbm_init(const struct resctrl_test *test,
{
int ret;
- ret = initialize_read_mem_bw_imc();
+ ret = initialize_read_mem_bw_mc();
if (ret)
return ret;
@@ -125,7 +125,7 @@ static int mbm_measure(const struct user_params *uparams,
static void mbm_test_cleanup(void)
{
- cleanup_read_mem_bw_imc();
+ cleanup_read_mem_bw_mc();
remove(RESULT_FILE_NAME);
}
diff --git a/tools/testing/selftests/resctrl/resctrl.h b/tools/testing/selftests/resctrl/resctrl.h
index a7556cdae0de..cb8d68292a21 100644
--- a/tools/testing/selftests/resctrl/resctrl.h
+++ b/tools/testing/selftests/resctrl/resctrl.h
@@ -50,7 +50,7 @@
/*
* Memory bandwidth (in MiB) below which the bandwidth comparisons
- * between iMC and resctrl are considered unreliable. For example RAS
+ * between MC and resctrl are considered unreliable. For example RAS
* features or memory performance features that generate memory traffic
* may drive accesses that are counted differently by performance counters
* and MBM respectively, for instance generating "overhead" traffic which
@@ -183,8 +183,8 @@ unsigned char *alloc_buffer(size_t buf_size, bool memflush);
void mem_flush(unsigned char *buf, size_t buf_size);
void fill_cache_read(unsigned char *buf, size_t buf_size, bool once);
ssize_t get_fill_buf_size(int cpu_no, const char *cache_type);
-int initialize_read_mem_bw_imc(void);
-void cleanup_read_mem_bw_imc(void);
+int initialize_read_mem_bw_mc(void);
+void cleanup_read_mem_bw_mc(void);
int measure_read_mem_bw(const struct user_params *uparams,
struct resctrl_val_param *param, pid_t bm_pid);
void initialize_mem_bw_resctrl(const struct resctrl_val_param *param,
diff --git a/tools/testing/selftests/resctrl/resctrl_val.c b/tools/testing/selftests/resctrl/resctrl_val.c
index 7e7d0f9cc249..c9cac957726b 100644
--- a/tools/testing/selftests/resctrl/resctrl_val.c
+++ b/tools/testing/selftests/resctrl/resctrl_val.c
@@ -26,7 +26,7 @@ struct membw_read_format {
__u64 id; /* if PERF_FORMAT_ID */
};
-struct imc_counter_config {
+struct mc_counter_config {
struct list_head entry;
__u32 type;
__u64 event;
@@ -36,40 +36,40 @@ struct imc_counter_config {
};
static char mbm_total_path[1024];
-LIST_HEAD(imc_counters_list);
+LIST_HEAD(mc_counters_list);
static const struct resctrl_test *current_test;
-static void read_mem_bw_initialize_perf_event_attr(struct imc_counter_config *imc_counter)
+static void read_mem_bw_initialize_perf_event_attr(struct mc_counter_config *mc_counter)
{
- memset(&imc_counter->pe, 0, sizeof(struct perf_event_attr));
- imc_counter->pe.type = imc_counter->type;
- imc_counter->pe.size = sizeof(struct perf_event_attr);
- imc_counter->pe.disabled = 1;
- imc_counter->pe.inherit = 1;
- imc_counter->pe.exclude_guest = 0;
- imc_counter->pe.config = imc_counter->umask << 8 | imc_counter->event;
- imc_counter->pe.sample_type = PERF_SAMPLE_IDENTIFIER;
- imc_counter->pe.read_format =
+ memset(&mc_counter->pe, 0, sizeof(struct perf_event_attr));
+ mc_counter->pe.type = mc_counter->type;
+ mc_counter->pe.size = sizeof(struct perf_event_attr);
+ mc_counter->pe.disabled = 1;
+ mc_counter->pe.inherit = 1;
+ mc_counter->pe.exclude_guest = 0;
+ mc_counter->pe.config = mc_counter->umask << 8 | mc_counter->event;
+ mc_counter->pe.sample_type = PERF_SAMPLE_IDENTIFIER;
+ mc_counter->pe.read_format =
PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING;
}
-static void read_mem_bw_ioctl_perf_event_ioc_reset_enable(struct imc_counter_config *imc_counter)
+static void read_mem_bw_ioctl_perf_event_ioc_reset_enable(struct mc_counter_config *mc_counter)
{
- ioctl(imc_counter->fd, PERF_EVENT_IOC_RESET, 0);
- ioctl(imc_counter->fd, PERF_EVENT_IOC_ENABLE, 0);
+ ioctl(mc_counter->fd, PERF_EVENT_IOC_RESET, 0);
+ ioctl(mc_counter->fd, PERF_EVENT_IOC_ENABLE, 0);
}
-static void read_mem_bw_ioctl_perf_event_ioc_disable(struct imc_counter_config *imc_counter)
+static void read_mem_bw_ioctl_perf_event_ioc_disable(struct mc_counter_config *mc_counter)
{
- ioctl(imc_counter->fd, PERF_EVENT_IOC_DISABLE, 0);
+ ioctl(mc_counter->fd, PERF_EVENT_IOC_DISABLE, 0);
}
/*
* get_read_event_and_umask: Parse config into event and umask
* @cas_count_cfg: Config
- * @imc_counter: iMC counter config
+ * @mc_counter: MC counter config
*/
-static void get_read_event_and_umask(char *cas_count_cfg, struct imc_counter_config *imc_counter)
+static void get_read_event_and_umask(char *cas_count_cfg, struct mc_counter_config *mc_counter)
{
char *token[MAX_TOKENS];
int i = 0;
@@ -83,20 +83,20 @@ static void get_read_event_and_umask(char *cas_count_cfg, struct imc_counter_con
if (!token[i])
break;
if (strcmp(token[i], "event") == 0)
- imc_counter->event = strtol(token[i + 1], NULL, 16);
+ mc_counter->event = strtol(token[i + 1], NULL, 16);
if (strcmp(token[i], "umask") == 0)
- imc_counter->umask = strtol(token[i + 1], NULL, 16);
+ mc_counter->umask = strtol(token[i + 1], NULL, 16);
}
}
-static int open_perf_read_event(int cpu_no, struct imc_counter_config *imc_counter)
+static int open_perf_read_event(int cpu_no, struct mc_counter_config *mc_counter)
{
- imc_counter->fd = perf_event_open(&imc_counter->pe, -1, cpu_no, -1,
+ mc_counter->fd = perf_event_open(&mc_counter->pe, -1, cpu_no, -1,
PERF_FLAG_FD_CLOEXEC);
- if (imc_counter->fd == -1) {
+ if (mc_counter->fd == -1) {
fprintf(stderr, "Error opening leader %llx\n",
- imc_counter->pe.config);
+ mc_counter->pe.config);
return -1;
}
@@ -104,10 +104,10 @@ static int open_perf_read_event(int cpu_no, struct imc_counter_config *imc_count
return 0;
}
-static int parse_imc_read_bw_events(char *imc_dir, unsigned int type)
+static int parse_mc_read_bw_events(char *mc_dir, unsigned int type)
{
- char imc_events_dir[PATH_MAX], imc_counter_cfg[PATH_MAX];
- struct imc_counter_config *imc_counter;
+ char mc_events_dir[PATH_MAX], mc_counter_cfg[PATH_MAX];
+ struct mc_counter_config *mc_counter;
bool found_event = false;
char cas_count_cfg[1024];
struct dirent *ep;
@@ -117,14 +117,14 @@ static int parse_imc_read_bw_events(char *imc_dir, unsigned int type)
FILE *fp;
DIR *dp;
- path_len = snprintf(imc_events_dir, sizeof(imc_events_dir), "%sevents",
- imc_dir);
- if (path_len >= sizeof(imc_events_dir)) {
- ksft_print_msg("Unable to create path to %sevents\n", imc_dir);
+ path_len = snprintf(mc_events_dir, sizeof(mc_events_dir), "%sevents",
+ mc_dir);
+ if (path_len >= sizeof(mc_events_dir)) {
+ ksft_print_msg("Unable to create path to %sevents\n", mc_dir);
goto out;
}
- dp = opendir(imc_events_dir);
+ dp = opendir(mc_events_dir);
if (!dp) {
ksft_perror("Unable to open PMU events directory");
goto out;
@@ -140,37 +140,37 @@ static int parse_imc_read_bw_events(char *imc_dir, unsigned int type)
strchr(ep->d_name, '.'))
continue;
- path_len = snprintf(imc_counter_cfg, sizeof(imc_counter_cfg),
- "%s/%s", imc_events_dir, ep->d_name);
- if (path_len >= sizeof(imc_counter_cfg)) {
+ path_len = snprintf(mc_counter_cfg, sizeof(mc_counter_cfg),
+ "%s/%s", mc_events_dir, ep->d_name);
+ if (path_len >= sizeof(mc_counter_cfg)) {
ksft_print_msg("Unable to create path to %s/%s\n",
- imc_events_dir, ep->d_name);
+ mc_events_dir, ep->d_name);
goto out_close;
}
- fp = fopen(imc_counter_cfg, "r");
+ fp = fopen(mc_counter_cfg, "r");
if (!fp) {
- ksft_perror("Failed to open iMC config file");
+ ksft_perror("Failed to open MC config file");
goto out_close;
}
num_cfg = fscanf(fp, "%1023s", cas_count_cfg);
fclose(fp);
if (num_cfg <= 0) {
- ksft_perror("Could not get iMC cas count read");
+ ksft_perror("Could not get MC cas count read");
goto out_close;
}
- imc_counter = calloc(1, sizeof(*imc_counter));
- if (!imc_counter) {
- ksft_perror("Unable to allocate memory for iMC counters");
+ mc_counter = calloc(1, sizeof(*mc_counter));
+ if (!mc_counter) {
+ ksft_perror("Unable to allocate memory for MC counters");
goto out_close;
}
- imc_counter->type = type;
- get_read_event_and_umask(cas_count_cfg, imc_counter);
- list_add(&imc_counter->entry, &imc_counters_list);
+ mc_counter->type = type;
+ get_read_event_and_umask(cas_count_cfg, mc_counter);
+ list_add(&mc_counter->entry, &mc_counters_list);
found_event = true;
}
if (!found_event) {
- ksft_print_msg("Unable to find events in %s\n", imc_events_dir);
+ ksft_print_msg("Unable to find events in %s\n", mc_events_dir);
goto out_close;
}
ret = 0;
@@ -178,41 +178,41 @@ static int parse_imc_read_bw_events(char *imc_dir, unsigned int type)
closedir(dp);
out:
if (ret)
- cleanup_read_mem_bw_imc();
+ cleanup_read_mem_bw_mc();
return ret;
}
-/* Get type and config of an iMC counter's read event. */
-static int read_from_imc_dir(char *imc_dir)
+/* Get type and config of an MC counter's read event. */
+static int read_from_mc_dir(char *mc_dir)
{
- char imc_counter_type[PATH_MAX];
+ char mc_counter_type[PATH_MAX];
unsigned int type;
int path_len;
FILE *fp;
int ret;
- /* Get type of iMC counter */
- path_len = snprintf(imc_counter_type, sizeof(imc_counter_type),
- "%s%s", imc_dir, "type");
- if (path_len >= sizeof(imc_counter_type)) {
+ /* Get type of MC counter */
+ path_len = snprintf(mc_counter_type, sizeof(mc_counter_type),
+ "%s%s", mc_dir, "type");
+ if (path_len >= sizeof(mc_counter_type)) {
ksft_print_msg("Unable to create path to %s%s\n",
- imc_dir, "type");
+ mc_dir, "type");
return -1;
}
- fp = fopen(imc_counter_type, "r");
+ fp = fopen(mc_counter_type, "r");
if (!fp) {
- ksft_perror("Failed to open iMC counter type file");
+ ksft_perror("Failed to open MC counter type file");
return -1;
}
ret = fscanf(fp, "%u", &type);
fclose(fp);
if (ret <= 0) {
- ksft_perror("Could not get iMC type");
+ ksft_perror("Could not get MC type");
return -1;
}
- ret = parse_imc_read_bw_events(imc_dir, type);
+ ret = parse_mc_read_bw_events(mc_dir, type);
if (ret) {
ksft_print_msg("Unable to parse bandwidth event and umask\n");
return ret;
@@ -222,8 +222,8 @@ static int read_from_imc_dir(char *imc_dir)
}
/*
- * Discover the properties of the available iMC (Integrated Memory Controller)
- * counters in support of needed performance measurement via perf. For each iMC
+ * Discover the properties of the available MC (Memory Controller)
+ * counters in support of needed performance measurement via perf. For each MC
* counter get it's type and config. Also obtain each counter's event and umask
* for the memory read events that will be measured.
*
@@ -231,9 +231,9 @@ static int read_from_imc_dir(char *imc_dir)
*
* Return: >= 0 on success. < 0 on failure.
*/
-static int enumerate_imcs(void)
+static int enumerate_mcs(void)
{
- char imc_dir[512], *temp;
+ char mc_dir[512], *temp;
struct dirent *ep;
int ret;
DIR *dp;
@@ -246,7 +246,7 @@ static int enumerate_imcs(void)
continue;
/*
- * imc counters are named as "uncore_imc_<n>", hence
+ * mc counters are named as "uncore_imc_<n>", hence
* increment the pointer to point to <n>. Note that
* sizeof(UNCORE_IMC) would count for null character as
* well and hence the last underscore character in
@@ -260,9 +260,9 @@ static int enumerate_imcs(void)
* first character is a numerical digit or not.
*/
if (temp[0] >= '0' && temp[0] <= '9') {
- sprintf(imc_dir, "%s/%s/", DYN_PMU_PATH,
+ sprintf(mc_dir, "%s/%s/", DYN_PMU_PATH,
ep->d_name);
- ret = read_from_imc_dir(imc_dir);
+ ret = read_from_mc_dir(mc_dir);
if (ret) {
closedir(dp);
@@ -271,8 +271,8 @@ static int enumerate_imcs(void)
}
}
closedir(dp);
- if (list_empty(&imc_counters_list)) {
- ksft_print_msg("Unable to find iMC counters\n");
+ if (list_empty(&mc_counters_list)) {
+ ksft_print_msg("Unable to find MC counters\n");
return -1;
}
@@ -285,58 +285,58 @@ static int enumerate_imcs(void)
return 0;
}
-int initialize_read_mem_bw_imc(void)
+int initialize_read_mem_bw_mc(void)
{
- struct imc_counter_config *imc_counter;
+ struct mc_counter_config *mc_counter;
int ret;
- ret = enumerate_imcs();
+ ret = enumerate_mcs();
if (ret < 0)
return ret;
- /* Initialize perf_event_attr structures for all iMC's */
- list_for_each_entry(imc_counter, &imc_counters_list, entry)
- read_mem_bw_initialize_perf_event_attr(imc_counter);
+ /* Initialize perf_event_attr structures for all MC's */
+ list_for_each_entry(mc_counter, &mc_counters_list, entry)
+ read_mem_bw_initialize_perf_event_attr(mc_counter);
return 0;
}
-void cleanup_read_mem_bw_imc(void)
+void cleanup_read_mem_bw_mc(void)
{
- struct imc_counter_config *imc_counter, *tmp;
+ struct mc_counter_config *mc_counter, *tmp;
- list_for_each_entry_safe(imc_counter, tmp, &imc_counters_list, entry) {
- list_del(&imc_counter->entry);
- free(imc_counter);
+ list_for_each_entry_safe(mc_counter, tmp, &mc_counters_list, entry) {
+ list_del(&mc_counter->entry);
+ free(mc_counter);
}
}
-static void perf_close_imc_read_mem_bw(void)
+static void perf_close_mc_read_mem_bw(void)
{
- struct imc_counter_config *imc_counter;
+ struct mc_counter_config *mc_counter;
- list_for_each_entry(imc_counter, &imc_counters_list, entry) {
- if (imc_counter->fd != -1)
- close(imc_counter->fd);
+ list_for_each_entry(mc_counter, &mc_counters_list, entry) {
+ if (mc_counter->fd != -1)
+ close(mc_counter->fd);
}
}
/*
- * perf_open_imc_read_mem_bw - Open perf fds for IMCs
+ * perf_open_mc_read_mem_bw - Open perf fds for MCs
* @cpu_no: CPU number that the benchmark PID is bound to
*
* Return: = 0 on success. < 0 on failure.
*/
-static int perf_open_imc_read_mem_bw(int cpu_no)
+static int perf_open_mc_read_mem_bw(int cpu_no)
{
- struct imc_counter_config *imc_counter;
+ struct mc_counter_config *mc_counter;
int ret;
- list_for_each_entry(imc_counter, &imc_counters_list, entry)
- imc_counter->fd = -1;
+ list_for_each_entry(mc_counter, &mc_counters_list, entry)
+ mc_counter->fd = -1;
- list_for_each_entry(imc_counter, &imc_counters_list, entry) {
- ret = open_perf_read_event(cpu_no, imc_counter);
+ list_for_each_entry(mc_counter, &mc_counters_list, entry) {
+ ret = open_perf_read_event(cpu_no, mc_counter);
if (ret)
goto close_fds;
}
@@ -344,54 +344,54 @@ static int perf_open_imc_read_mem_bw(int cpu_no)
return 0;
close_fds:
- perf_close_imc_read_mem_bw();
+ perf_close_mc_read_mem_bw();
return -1;
}
/*
- * do_imc_read_mem_bw_test - Perform memory bandwidth test
+ * do_mc_read_mem_bw_test - Perform memory bandwidth test
*
* Runs memory bandwidth test over one second period. Also, handles starting
- * and stopping of the IMC perf counters around the test.
+ * and stopping of the MC perf counters around the test.
*/
-static void do_imc_read_mem_bw_test(void)
+static void do_mc_read_mem_bw_test(void)
{
- struct imc_counter_config *imc_counter;
+ struct mc_counter_config *mc_counter;
- list_for_each_entry(imc_counter, &imc_counters_list, entry)
- read_mem_bw_ioctl_perf_event_ioc_reset_enable(imc_counter);
+ list_for_each_entry(mc_counter, &mc_counters_list, entry)
+ read_mem_bw_ioctl_perf_event_ioc_reset_enable(mc_counter);
sleep(1);
/* Stop counters after a second to get results. */
- list_for_each_entry(imc_counter, &imc_counters_list, entry)
- read_mem_bw_ioctl_perf_event_ioc_disable(imc_counter);
+ list_for_each_entry(mc_counter, &mc_counters_list, entry)
+ read_mem_bw_ioctl_perf_event_ioc_disable(mc_counter);
}
/*
- * get_read_mem_bw_imc - Memory read bandwidth as reported by iMC counters
+ * get_read_mem_bw_mc - Memory read bandwidth as reported by MC counters
*
* Memory read bandwidth utilized by a process on a socket can be calculated
- * using iMC counters' read events. Perf events are used to read these
+ * using MC counters' read events. Perf events are used to read these
* counters.
*
* Return: = 0 on success. < 0 on failure.
*/
-static int get_read_mem_bw_imc(float *bw_imc)
+static int get_read_mem_bw_mc(float *bw_mc)
{
float reads = 0, of_mul_read = 1;
- struct imc_counter_config *r;
+ struct mc_counter_config *r;
/*
- * Log read event values from all iMC counters into
- * struct imc_counter_config.
+ * Log read event values from all MC counters into
+ * struct mc_counter_config.
* Take overflow into consideration before calculating total bandwidth.
*/
- list_for_each_entry(r, &imc_counters_list, entry) {
+ list_for_each_entry(r, &mc_counters_list, entry) {
struct membw_read_format measurement;
if (read(r->fd, &measurement, sizeof(measurement)) == -1) {
- ksft_perror("Couldn't get read bandwidth through iMC");
+ ksft_perror("Couldn't get read bandwidth through MC");
return -1;
}
@@ -405,7 +405,7 @@ static int get_read_mem_bw_imc(float *bw_imc)
reads += measurement.value * of_mul_read * SCALE;
}
- *bw_imc = reads;
+ *bw_mc = reads;
return 0;
}
@@ -509,19 +509,19 @@ void signal_handler_unregister(void)
* print_results_bw: the memory bandwidth results are stored in a file
* @filename: file that stores the results
* @bm_pid: child pid that runs benchmark
- * @bw_imc: perf imc counter value
+ * @bw_mc: perf mc counter value
* @bw_resc: memory bandwidth value
*
* Return: 0 on success, < 0 on error.
*/
-static int print_results_bw(char *filename, pid_t bm_pid, float bw_imc,
+static int print_results_bw(char *filename, pid_t bm_pid, float bw_mc,
unsigned long bw_resc)
{
- unsigned long diff = fabs(bw_imc - bw_resc);
+ unsigned long diff = fabs(bw_mc - bw_resc);
FILE *fp;
if (strcmp(filename, "stdio") == 0 || strcmp(filename, "stderr") == 0) {
- printf("Pid: %d \t Mem_BW_iMC: %f \t ", (int)bm_pid, bw_imc);
+ printf("Pid: %d \t Mem_BW_MC: %f \t ", (int)bm_pid, bw_mc);
printf("Mem_BW_resc: %lu \t Difference: %lu\n", bw_resc, diff);
} else {
fp = fopen(filename, "a");
@@ -530,8 +530,8 @@ static int print_results_bw(char *filename, pid_t bm_pid, float bw_imc,
return -1;
}
- if (fprintf(fp, "Pid: %d \t Mem_BW_iMC: %f \t Mem_BW_resc: %lu \t Difference: %lu\n",
- (int)bm_pid, bw_imc, bw_resc, diff) <= 0) {
+ if (fprintf(fp, "Pid: %d \t Mem_BW_MC: %f \t Mem_BW_resc: %lu \t Difference: %lu\n",
+ (int)bm_pid, bw_mc, bw_resc, diff) <= 0) {
ksft_print_msg("Could not log results\n");
fclose(fp);
@@ -550,7 +550,7 @@ static int print_results_bw(char *filename, pid_t bm_pid, float bw_imc,
* @bm_pid: PID that runs the benchmark
*
* Measure memory bandwidth from resctrl and from another source which is
- * perf imc value or could be something else if perf imc event is not
+ * perf mc value or could be something else if perf mc event is not
* available. Compare the two values to validate resctrl value. It takes
* 1 sec to measure the data.
* resctrl does not distinguish between read and write operations so
@@ -561,42 +561,42 @@ int measure_read_mem_bw(const struct user_params *uparams,
{
unsigned long bw_resc, bw_resc_start, bw_resc_end;
FILE *mem_bw_fp;
- float bw_imc;
+ float bw_mc;
int ret;
mem_bw_fp = open_mem_bw_resctrl(mbm_total_path);
if (!mem_bw_fp)
return -1;
- ret = perf_open_imc_read_mem_bw(uparams->cpu);
+ ret = perf_open_mc_read_mem_bw(uparams->cpu);
if (ret < 0)
goto close_fp;
ret = get_mem_bw_resctrl(mem_bw_fp, &bw_resc_start);
if (ret < 0)
- goto close_imc;
+ goto close_mc;
rewind(mem_bw_fp);
- do_imc_read_mem_bw_test();
+ do_mc_read_mem_bw_test();
ret = get_mem_bw_resctrl(mem_bw_fp, &bw_resc_end);
if (ret < 0)
- goto close_imc;
+ goto close_mc;
- ret = get_read_mem_bw_imc(&bw_imc);
+ ret = get_read_mem_bw_mc(&bw_mc);
if (ret < 0)
- goto close_imc;
+ goto close_mc;
- perf_close_imc_read_mem_bw();
+ perf_close_mc_read_mem_bw();
fclose(mem_bw_fp);
bw_resc = (bw_resc_end - bw_resc_start) / MB;
- return print_results_bw(param->filename, bm_pid, bw_imc, bw_resc);
+ return print_results_bw(param->filename, bm_pid, bw_mc, bw_resc);
-close_imc:
- perf_close_imc_read_mem_bw();
+close_mc:
+ perf_close_mc_read_mem_bw();
close_fp:
fclose(mem_bw_fp);
return ret;
--
2.43.0
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v5 2/5] selftests/resctrl: Detect vendor to select memory controller sysfs name
2026-10-22 13:25 [PATCH v5 0/5] selftests/resctrl: Add MBM and MBA test support for AMD Swapnil Sapkal
2026-10-22 13:25 ` [PATCH v5 1/5] selftests/resctrl: Rename variables and functions to generic names Swapnil Sapkal
@ 2026-10-22 13:25 ` Swapnil Sapkal
2026-10-28 18:39 ` Reinette Chatre
2026-10-22 13:25 ` [PATCH v5 3/5] selftests/resctrl: Add support for MBM and MBA tests on AMD Swapnil Sapkal
` (2 subsequent siblings)
4 siblings, 1 reply; 11+ messages in thread
From: Swapnil Sapkal @ 2026-10-22 13:25 UTC (permalink / raw)
To: tony.luck, reinette.chatre, babu.moger, fenghua.yu, shuah
Cc: Dave.Martin, james.morse, peternewman, eranian, tan.shaopeng,
linux-kernel, linux-kselftest, x86, Swapnil Sapkal
From: Babu Moger <babu.moger@amd.com>
The test detects the number of memory controllers by scanning the perf
sysfs (DYN_PMU_PATH) for the vendor specific memory controller PMU name.
On Intel this PMU is named "uncore_imc_<n>".
Detect the vendor and select the memory controller PMU name accordingly
so that the rest of the code stays generic and can be extended to other
vendors. Bail out on vendors that are not yet supported.
While at it, match against the full "uncore_imc_" string and use
strlen() instead of sizeof() to advance past the prefix. This is clearer
than relying on sizeof() counting the terminating NULL to make up for the
trailing underscore.
Assisted-by: Claude:claude-opus-4.8
Signed-off-by: Babu Moger <babu.moger@amd.com>
Co-developed-by: Swapnil Sapkal <swapnil.sapkal@amd.com>
Signed-off-by: Swapnil Sapkal <swapnil.sapkal@amd.com>
---
v4->v5:
- Rebased onto kselftest/next; the vendor detection now lives in
enumerate_mcs() (previously num_of_mem_controllers()).
v3->v4:
- Reworded the subject and changelog. (Reinette)
- Dropped the local vendor variable and call get_vendor() directly.
(Reinette)
tools/testing/selftests/resctrl/resctrl_val.c | 19 ++++++++++++-------
1 file changed, 12 insertions(+), 7 deletions(-)
diff --git a/tools/testing/selftests/resctrl/resctrl_val.c b/tools/testing/selftests/resctrl/resctrl_val.c
index c9cac957726b..fdbbe2155cea 100644
--- a/tools/testing/selftests/resctrl/resctrl_val.c
+++ b/tools/testing/selftests/resctrl/resctrl_val.c
@@ -10,7 +10,7 @@
*/
#include "resctrl.h"
-#define UNCORE_IMC "uncore_imc"
+#define UNCORE_IMC "uncore_imc_"
#define READ_FILE_NAME "cas_count_read"
#define DYN_PMU_PATH "/sys/bus/event_source/devices"
#define SCALE 0.00006103515625
@@ -235,24 +235,29 @@ static int enumerate_mcs(void)
{
char mc_dir[512], *temp;
struct dirent *ep;
+ char *sysfs_name;
int ret;
DIR *dp;
+ if (get_vendor() == ARCH_INTEL) {
+ sysfs_name = UNCORE_IMC;
+ } else {
+ ksft_print_msg("Unsupported vendor\n");
+ return -1;
+ }
+
dp = opendir(DYN_PMU_PATH);
if (dp) {
while ((ep = readdir(dp))) {
- temp = strstr(ep->d_name, UNCORE_IMC);
+ temp = strstr(ep->d_name, sysfs_name);
if (!temp)
continue;
/*
* mc counters are named as "uncore_imc_<n>", hence
- * increment the pointer to point to <n>. Note that
- * sizeof(UNCORE_IMC) would count for null character as
- * well and hence the last underscore character in
- * uncore_imc'_' need not be counted.
+ * increment the pointer to point to <n>.
*/
- temp = temp + sizeof(UNCORE_IMC);
+ temp = temp + strlen(sysfs_name);
/*
* Some directories under "DYN_PMU_PATH" could have
--
2.43.0
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v5 3/5] selftests/resctrl: Add support for MBM and MBA tests on AMD
2026-10-22 13:25 [PATCH v5 0/5] selftests/resctrl: Add MBM and MBA test support for AMD Swapnil Sapkal
2026-10-22 13:25 ` [PATCH v5 1/5] selftests/resctrl: Rename variables and functions to generic names Swapnil Sapkal
2026-10-22 13:25 ` [PATCH v5 2/5] selftests/resctrl: Detect vendor to select memory controller sysfs name Swapnil Sapkal
@ 2026-10-22 13:25 ` Swapnil Sapkal
2026-10-28 18:41 ` Reinette Chatre
2026-10-22 13:25 ` [PATCH v5 4/5] selftests/resctrl: Use bandwidth values for AMD MBA schemata Swapnil Sapkal
2026-10-22 13:25 ` [PATCH v5 5/5] selftests/resctrl: Enable MBM and MBA tests on AMD Swapnil Sapkal
4 siblings, 1 reply; 11+ messages in thread
From: Swapnil Sapkal @ 2026-10-22 13:25 UTC (permalink / raw)
To: tony.luck, reinette.chatre, babu.moger, fenghua.yu, shuah
Cc: Dave.Martin, james.morse, peternewman, eranian, tan.shaopeng,
linux-kernel, linux-kselftest, x86, Swapnil Sapkal
From: Babu Moger <babu.moger@amd.com>
The MBM and MBA tests validate the resctrl memory bandwidth numbers
against the memory controller performance counters. On Intel these are
the iMC (Integrated Memory Controller) counters and the read event and
umask are discovered from the PMU's sysfs event files.
Commit 25e56847821f ("perf/x86/amd/uncore: Add memory controller
support") added support for the UMC (Unified Memory Controller) perf
events on AMD. Unlike Intel, the UMC PMU does not expose the CAS command
events via sysfs, so program the read event directly:
Number of CAS commands sent for reads: EventCode = 0x0a, umask = 0x1.
Keep all the vendor detection contained in enumerate_mcs() and dispatch
to a vendor specific routine to initialize the counter configuration.
Only reading the counter's perf "type" is common to both vendors, so
factor that out into a shared helper.
On AMD each memory controller (UMC) is exposed as its own PMU
(amd_umc_<n>) that is tied to a specific NUMA node, whereas on Intel
opening an iMC event on the benchmark CPU always resolves to the local
memory controllers. A UMC PMU can only be opened from a CPU that belongs
to the same node and returns -ENODEV otherwise. Since the benchmark runs
on a single CPU, open the counters that are local to that CPU and skip
the UMCs on remote nodes. This both avoids the perf_event_open() failure
and matches the local traffic accounted by mbm_local_bytes.
Assisted-by: Claude:claude-opus-4.8
Signed-off-by: Babu Moger <babu.moger@amd.com>
Co-developed-by: Swapnil Sapkal <swapnil.sapkal@amd.com>
Signed-off-by: Swapnil Sapkal <swapnil.sapkal@amd.com>
---
v4->v5:
- Rebased onto kselftest/next and reworked for the linked-list counter
management: read_from_amd_mc_dir() allocates a counter and adds it to
the list, and the remote-node skipping iterates the list.
- Vendor detection and dispatch now live in enumerate_mcs().
v3->v4:
- Contained all vendor detection in the enumeration routine and
dispatched to per-vendor routines, factoring the common counter 'type'
read into a shared helper. (Reinette)
- Adapted to the upstream read-only bandwidth refactor.
- Handled the AMD per-node UMC PMU topology: open only the UMCs local to
the benchmark CPU and skip remote-node UMCs that would otherwise fail
perf_event_open() with -ENODEV on multi-node systems.
- Improved the perf_event_open() error reporting (print the errno with
strerror()).
tools/testing/selftests/resctrl/resctrl_val.c | 135 ++++++++++++++----
1 file changed, 111 insertions(+), 24 deletions(-)
diff --git a/tools/testing/selftests/resctrl/resctrl_val.c b/tools/testing/selftests/resctrl/resctrl_val.c
index fdbbe2155cea..38cf58c41be1 100644
--- a/tools/testing/selftests/resctrl/resctrl_val.c
+++ b/tools/testing/selftests/resctrl/resctrl_val.c
@@ -11,6 +11,7 @@
#include "resctrl.h"
#define UNCORE_IMC "uncore_imc_"
+#define AMD_UMC "amd_umc_"
#define READ_FILE_NAME "cas_count_read"
#define DYN_PMU_PATH "/sys/bus/event_source/devices"
#define SCALE 0.00006103515625
@@ -94,12 +95,8 @@ static int open_perf_read_event(int cpu_no, struct mc_counter_config *mc_counter
mc_counter->fd = perf_event_open(&mc_counter->pe, -1, cpu_no, -1,
PERF_FLAG_FD_CLOEXEC);
- if (mc_counter->fd == -1) {
- fprintf(stderr, "Error opening leader %llx\n",
- mc_counter->pe.config);
-
- return -1;
- }
+ if (mc_counter->fd == -1)
+ return -errno;
return 0;
}
@@ -183,16 +180,14 @@ static int parse_mc_read_bw_events(char *mc_dir, unsigned int type)
return ret;
}
-/* Get type and config of an MC counter's read event. */
-static int read_from_mc_dir(char *mc_dir)
+/* Get the perf "type" of a memory controller counter. Shared by all vendors. */
+static int get_mc_counter_type(const char *mc_dir, unsigned int *type)
{
char mc_counter_type[PATH_MAX];
- unsigned int type;
int path_len;
FILE *fp;
int ret;
- /* Get type of MC counter */
path_len = snprintf(mc_counter_type, sizeof(mc_counter_type),
"%s%s", mc_dir, "type");
if (path_len >= sizeof(mc_counter_type)) {
@@ -206,12 +201,29 @@ static int read_from_mc_dir(char *mc_dir)
return -1;
}
- ret = fscanf(fp, "%u", &type);
+ ret = fscanf(fp, "%u", type);
fclose(fp);
if (ret <= 0) {
ksft_perror("Could not get MC type");
return -1;
}
+
+ return 0;
+}
+
+/*
+ * Intel: get type and config of an iMC counter's read event. The read event
+ * and umask are discovered from the PMU's sysfs event files.
+ */
+static int read_from_intel_mc_dir(char *mc_dir)
+{
+ unsigned int type;
+ int ret;
+
+ ret = get_mc_counter_type(mc_dir, &type);
+ if (ret)
+ return ret;
+
ret = parse_mc_read_bw_events(mc_dir, type);
if (ret) {
ksft_print_msg("Unable to parse bandwidth event and umask\n");
@@ -222,10 +234,42 @@ static int read_from_mc_dir(char *mc_dir)
}
/*
- * Discover the properties of the available MC (Memory Controller)
- * counters in support of needed performance measurement via perf. For each MC
- * counter get it's type and config. Also obtain each counter's event and umask
- * for the memory read events that will be measured.
+ * AMD: get type and config of a UMC counter's read event. The UMC PMU does
+ * not expose the CAS command events via sysfs, so program them directly:
+ * Number of CAS commands sent for reads: EventCode = 0x0a, umask = 0x1
+ */
+static int read_from_amd_mc_dir(char *mc_dir)
+{
+ struct mc_counter_config *mc_counter;
+ unsigned int type;
+ int ret;
+
+ ret = get_mc_counter_type(mc_dir, &type);
+ if (ret)
+ return ret;
+
+ mc_counter = calloc(1, sizeof(*mc_counter));
+ if (!mc_counter) {
+ ksft_perror("Unable to allocate memory for MC counters");
+ return -1;
+ }
+
+ mc_counter->type = type;
+ mc_counter->event = 0xa;
+ mc_counter->umask = 0x1;
+ list_add(&mc_counter->entry, &mc_counters_list);
+
+ return 0;
+}
+
+/*
+ * Discover the properties of the available MC (Memory Controller) counters.
+ * On Intel the memory controller is called Integrated Memory Controller (iMC)
+ * and on AMD it is called Unified Memory Controller (UMC). Discover the
+ * properties of the available counters in support of needed performance
+ * measurement via perf. For each MC counter get it's type and config. Also
+ * obtain each counter's event and umask for the memory read events that will
+ * be measured.
*
* Enumerate all these details into a linked list of structures.
*
@@ -233,6 +277,7 @@ static int read_from_mc_dir(char *mc_dir)
*/
static int enumerate_mcs(void)
{
+ int (*read_mc_dir)(char *mc_dir);
char mc_dir[512], *temp;
struct dirent *ep;
char *sysfs_name;
@@ -241,6 +286,10 @@ static int enumerate_mcs(void)
if (get_vendor() == ARCH_INTEL) {
sysfs_name = UNCORE_IMC;
+ read_mc_dir = read_from_intel_mc_dir;
+ } else if (get_vendor() == ARCH_AMD) {
+ sysfs_name = AMD_UMC;
+ read_mc_dir = read_from_amd_mc_dir;
} else {
ksft_print_msg("Unsupported vendor\n");
return -1;
@@ -254,8 +303,9 @@ static int enumerate_mcs(void)
continue;
/*
- * mc counters are named as "uncore_imc_<n>", hence
- * increment the pointer to point to <n>.
+ * MC counters are named as "uncore_imc_<n>" on Intel
+ * and "amd_umc_<n>" on AMD, hence increment the pointer
+ * to point to <n>.
*/
temp = temp + strlen(sysfs_name);
@@ -267,7 +317,7 @@ static int enumerate_mcs(void)
if (temp[0] >= '0' && temp[0] <= '9') {
sprintf(mc_dir, "%s/%s/", DYN_PMU_PATH,
ep->d_name);
- ret = read_from_mc_dir(mc_dir);
+ ret = read_mc_dir(mc_dir);
if (ret) {
closedir(dp);
@@ -277,7 +327,9 @@ static int enumerate_mcs(void)
}
closedir(dp);
if (list_empty(&mc_counters_list)) {
- ksft_print_msg("Unable to find MC counters\n");
+ ksft_print_msg("Unable to find memory controller counters\n");
+ if (get_vendor() == ARCH_AMD)
+ ksft_print_msg("Try loading amd_uncore module\n");
return -1;
}
@@ -335,15 +387,40 @@ static void perf_close_mc_read_mem_bw(void)
static int perf_open_mc_read_mem_bw(int cpu_no)
{
struct mc_counter_config *mc_counter;
- int ret;
+ int ret, opened = 0;
list_for_each_entry(mc_counter, &mc_counters_list, entry)
mc_counter->fd = -1;
list_for_each_entry(mc_counter, &mc_counters_list, entry) {
ret = open_perf_read_event(cpu_no, mc_counter);
- if (ret)
- goto close_fds;
+ if (!ret) {
+ opened++;
+ continue;
+ }
+
+ /*
+ * On AMD each memory controller (UMC) has its own PMU that is
+ * tied to a specific NUMA node, unlike Intel where opening an
+ * iMC event on the benchmark CPU always resolves to the local
+ * memory controllers. A UMC PMU can only be opened from a CPU
+ * that belongs to the same node and returns -ENODEV otherwise.
+ * The benchmark runs on a single CPU, so skip the UMCs that
+ * belong to remote nodes and measure only the memory
+ * controllers local to the benchmark. This matches what
+ * mbm_local_bytes accounts for on the resctrl side.
+ */
+ if (ret == -ENODEV && get_vendor() == ARCH_AMD)
+ continue;
+
+ ksft_print_msg("Error opening memory bandwidth performance counter: %s\n",
+ strerror(-ret));
+ goto close_fds;
+ }
+
+ if (!opened) {
+ ksft_print_msg("Could not open any memory controller counters\n");
+ goto close_fds;
}
return 0;
@@ -363,14 +440,20 @@ static void do_mc_read_mem_bw_test(void)
{
struct mc_counter_config *mc_counter;
- list_for_each_entry(mc_counter, &mc_counters_list, entry)
+ list_for_each_entry(mc_counter, &mc_counters_list, entry) {
+ if (mc_counter->fd == -1)
+ continue;
read_mem_bw_ioctl_perf_event_ioc_reset_enable(mc_counter);
+ }
sleep(1);
/* Stop counters after a second to get results. */
- list_for_each_entry(mc_counter, &mc_counters_list, entry)
+ list_for_each_entry(mc_counter, &mc_counters_list, entry) {
+ if (mc_counter->fd == -1)
+ continue;
read_mem_bw_ioctl_perf_event_ioc_disable(mc_counter);
+ }
}
/*
@@ -395,6 +478,10 @@ static int get_read_mem_bw_mc(float *bw_mc)
list_for_each_entry(r, &mc_counters_list, entry) {
struct membw_read_format measurement;
+ /* Skip memory controllers that were not opened (remote node). */
+ if (r->fd == -1)
+ continue;
+
if (read(r->fd, &measurement, sizeof(measurement)) == -1) {
ksft_perror("Couldn't get read bandwidth through MC");
return -1;
--
2.43.0
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v5 4/5] selftests/resctrl: Use bandwidth values for AMD MBA schemata
2026-10-22 13:25 [PATCH v5 0/5] selftests/resctrl: Add MBM and MBA test support for AMD Swapnil Sapkal
` (2 preceding siblings ...)
2026-10-22 13:25 ` [PATCH v5 3/5] selftests/resctrl: Add support for MBM and MBA tests on AMD Swapnil Sapkal
@ 2026-10-22 13:25 ` Swapnil Sapkal
2026-10-28 18:45 ` Reinette Chatre
2026-10-22 13:25 ` [PATCH v5 5/5] selftests/resctrl: Enable MBM and MBA tests on AMD Swapnil Sapkal
4 siblings, 1 reply; 11+ messages in thread
From: Swapnil Sapkal @ 2026-10-22 13:25 UTC (permalink / raw)
To: tony.luck, reinette.chatre, babu.moger, fenghua.yu, shuah
Cc: Dave.Martin, james.morse, peternewman, eranian, tan.shaopeng,
linux-kernel, linux-kselftest, x86, Swapnil Sapkal
Intel MBA (Memory Bandwidth Allocation) throttles memory bandwidth using
a percentage, so the MBA test sweeps the schemata from 10% to 100% in
steps of 10%. AMD MBA instead uses an absolute memory bandwidth value.
Sweep the AMD MBA schemata from 4 GB/s to 40 GB/s in steps of 4 GB/s.
The hardware represents these in units of 1/8 GB/s, so the test writes
32, 64, ... up to 320. The number of allocations swept (10), and hence
the number of measurements, stays the same as Intel.
Select the minimum, maximum and step of the sweep based on the vendor.
Assisted-by: Claude:claude-opus-4.8
Signed-off-by: Swapnil Sapkal <swapnil.sapkal@amd.com>
---
v4->v5:
- No functional change; rebased onto kselftest/next.
v3->v4:
- New patch in v4.
- Added build-time assertions that both the Intel and AMD schemata
sweeps yield NUM_OF_ALLOCS allocations, and a bounds check in
check_results(), so the fixed-size result arrays cannot overflow.
tools/testing/selftests/resctrl/mba_test.c | 79 ++++++++++++++++++----
1 file changed, 66 insertions(+), 13 deletions(-)
diff --git a/tools/testing/selftests/resctrl/mba_test.c b/tools/testing/selftests/resctrl/mba_test.c
index 17ae73d3476c..947735e26099 100644
--- a/tools/testing/selftests/resctrl/mba_test.c
+++ b/tools/testing/selftests/resctrl/mba_test.c
@@ -13,10 +13,51 @@
#define RESULT_FILE_NAME "result_mba"
#define NUM_OF_RUNS 5
#define MAX_DIFF_PERCENT 15
+
+/* Intel MBA is specified as a percentage: 10%, 20% ... 100%. */
#define ALLOCATION_MAX 100
#define ALLOCATION_MIN 10
#define ALLOCATION_STEP 10
+/*
+ * AMD MBA is not a percentage but an absolute memory bandwidth value. The
+ * test sweeps the bandwidth from 4 GB/s to 40 GB/s in steps of 4 GB/s,
+ * which the hardware represents in units of 1/8 GB/s (i.e. 4 GB/s -> 32,
+ * 8 GB/s -> 64, ... 40 GB/s -> 320).
+ */
+#define AMD_ALLOCATION_MAX 320
+#define AMD_ALLOCATION_MIN 32
+#define AMD_ALLOCATION_STEP 32
+
+/* Number of MBA schemata allocations to sweep (same for both vendors). */
+#define NUM_OF_ALLOCS (ALLOCATION_MAX / ALLOCATION_STEP)
+
+/*
+ * The fixed-size result arrays in check_results() and the aggregation loop in
+ * show_mba_info() are sized and driven by NUM_OF_ALLOCS, so each vendor's sweep
+ * must produce exactly NUM_OF_ALLOCS allocations. Enforce this at build time so
+ * that changing a vendor's min/max/step can never overflow those arrays.
+ */
+_Static_assert((ALLOCATION_MAX - ALLOCATION_MIN) / ALLOCATION_STEP + 1 == NUM_OF_ALLOCS,
+ "Intel MBA sweep must yield NUM_OF_ALLOCS allocations");
+_Static_assert((AMD_ALLOCATION_MAX - AMD_ALLOCATION_MIN) / AMD_ALLOCATION_STEP + 1 == NUM_OF_ALLOCS,
+ "AMD MBA sweep must yield NUM_OF_ALLOCS allocations");
+
+static unsigned int mba_alloc_min(void)
+{
+ return get_vendor() == ARCH_AMD ? AMD_ALLOCATION_MIN : ALLOCATION_MIN;
+}
+
+static unsigned int mba_alloc_max(void)
+{
+ return get_vendor() == ARCH_AMD ? AMD_ALLOCATION_MAX : ALLOCATION_MAX;
+}
+
+static unsigned int mba_alloc_step(void)
+{
+ return get_vendor() == ARCH_AMD ? AMD_ALLOCATION_STEP : ALLOCATION_STEP;
+}
+
static int mba_init(const struct resctrl_test *test,
const struct user_params *uparams,
const struct resctrl_val_param *param, int domain_id)
@@ -33,15 +74,17 @@ static int mba_init(const struct resctrl_test *test,
}
/*
- * Change schemata percentage from 100 to 10%. Write schemata to specified
- * con_mon grp, mon_grp in resctrl FS.
+ * Sweep the MBA schemata from the minimum to the maximum allocation. On
+ * Intel the allocation is a percentage (10% .. 100%); on AMD it is an
+ * absolute memory bandwidth value (4 GB/s .. 40 GB/s). Write schemata to
+ * the specified con_mon grp, mon_grp in resctrl FS.
* For each allocation, run 5 times in order to get average values.
*/
static int mba_setup(const struct resctrl_test *test,
const struct user_params *uparams,
struct resctrl_val_param *p)
{
- static unsigned int allocation = ALLOCATION_MIN;
+ static unsigned int allocation;
static int runs_per_allocation;
char allocation_str[64];
int ret;
@@ -53,16 +96,20 @@ static int mba_setup(const struct resctrl_test *test,
if (runs_per_allocation++ != 0)
return 0;
- if (allocation > ALLOCATION_MAX)
+ /* Set the initial allocation once the vendor is known. */
+ if (allocation == 0)
+ allocation = mba_alloc_min();
+
+ if (allocation > mba_alloc_max())
return END_OF_TESTS;
- sprintf(allocation_str, "%d", allocation);
+ sprintf(allocation_str, "%u", allocation);
ret = write_schemata(p->ctrlgrp, allocation_str, uparams->cpu, test->resource);
if (ret < 0)
return ret;
- allocation += ALLOCATION_STEP;
+ allocation += mba_alloc_step();
return 0;
}
@@ -75,14 +122,15 @@ static int mba_measure(const struct user_params *uparams,
static bool show_mba_info(unsigned long *bw_mc, unsigned long *bw_resc)
{
+ unsigned int alloc_min = mba_alloc_min();
+ unsigned int alloc_step = mba_alloc_step();
unsigned int allocation;
bool ret = false;
int runs;
ksft_print_msg("Results are displayed in (MB)\n");
- /* Memory bandwidth from 100% down to 10% */
- for (allocation = 0; allocation < ALLOCATION_MAX / ALLOCATION_STEP;
- allocation++) {
+ /* Memory bandwidth for each of the swept MBA allocations */
+ for (allocation = 0; allocation < NUM_OF_ALLOCS; allocation++) {
unsigned long sum_bw_mc = 0, sum_bw_resc = 0;
long avg_bw_mc, avg_bw_resc;
int avg_diff_per;
@@ -99,7 +147,7 @@ static bool show_mba_info(unsigned long *bw_mc, unsigned long *bw_resc)
if (avg_bw_mc < THROTTLE_THRESHOLD || avg_bw_resc < THROTTLE_THRESHOLD) {
ksft_print_msg("Bandwidth below threshold (%d MiB). Dropping results from MBA schemata %u.\n",
THROTTLE_THRESHOLD,
- ALLOCATION_MIN + ALLOCATION_STEP * allocation);
+ alloc_min + alloc_step * allocation);
continue;
}
@@ -110,7 +158,7 @@ static bool show_mba_info(unsigned long *bw_mc, unsigned long *bw_resc)
avg_diff_per > MAX_DIFF_PERCENT ?
"Fail:" : "Pass:",
MAX_DIFF_PERCENT,
- ALLOCATION_MIN + ALLOCATION_STEP * allocation);
+ alloc_min + alloc_step * allocation);
ksft_print_msg("avg_diff_per: %d%%\n", avg_diff_per);
ksft_print_msg("avg_bw_mc: %lu\n", avg_bw_mc);
@@ -129,8 +177,8 @@ static bool show_mba_info(unsigned long *bw_mc, unsigned long *bw_resc)
static int check_results(void)
{
- unsigned long bw_resc[NUM_OF_RUNS * ALLOCATION_MAX / ALLOCATION_STEP];
- unsigned long bw_mc[NUM_OF_RUNS * ALLOCATION_MAX / ALLOCATION_STEP];
+ unsigned long bw_resc[NUM_OF_RUNS * NUM_OF_ALLOCS];
+ unsigned long bw_mc[NUM_OF_RUNS * NUM_OF_ALLOCS];
char *token_array[8], output[] = RESULT_FILE_NAME, temp[512];
int runs;
FILE *fp;
@@ -147,6 +195,11 @@ static int check_results(void)
char *token = strtok(temp, ":\t");
int fields = 0;
+ if (runs >= NUM_OF_RUNS * NUM_OF_ALLOCS) {
+ ksft_print_msg("Got more results than expected\n");
+ break;
+ }
+
while (token) {
token_array[fields++] = token;
token = strtok(NULL, ":\t");
--
2.43.0
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v5 5/5] selftests/resctrl: Enable MBM and MBA tests on AMD
2026-10-22 13:25 [PATCH v5 0/5] selftests/resctrl: Add MBM and MBA test support for AMD Swapnil Sapkal
` (3 preceding siblings ...)
2026-10-22 13:25 ` [PATCH v5 4/5] selftests/resctrl: Use bandwidth values for AMD MBA schemata Swapnil Sapkal
@ 2026-10-22 13:25 ` Swapnil Sapkal
2026-10-28 18:45 ` Reinette Chatre
4 siblings, 1 reply; 11+ messages in thread
From: Swapnil Sapkal @ 2026-10-22 13:25 UTC (permalink / raw)
To: tony.luck, reinette.chatre, babu.moger, fenghua.yu, shuah
Cc: Dave.Martin, james.morse, peternewman, eranian, tan.shaopeng,
linux-kernel, linux-kselftest, x86, Swapnil Sapkal
From: Babu Moger <babu.moger@amd.com>
Now that the UMC (Unified Memory Controller) perf events can be used to
validate the resctrl memory bandwidth numbers on AMD, extend the
vendor_specific gate of the MBM and MBA tests to also cover AMD so that
they run on AMD in addition to Intel.
The tests stay restricted to Intel and AMD: enumerate_mcs()
only knows about the iMC and UMC counters, so keeping the gate (rather
than making the tests universal) leaves other vendors skipped instead of
failing.
Assisted-by: Claude:claude-opus-4.8
Signed-off-by: Babu Moger <babu.moger@amd.com>
Co-developed-by: Swapnil Sapkal <swapnil.sapkal@amd.com>
Signed-off-by: Swapnil Sapkal <swapnil.sapkal@amd.com>
---
v4->v5:
- Rebased onto kselftest/next; updated the changelog reference from
num_of_mem_controllers() to enumerate_mcs().
v3->v4:
- Rebased onto v7.2-rc7.
- Extend the vendor_specific gate to ARCH_INTEL | ARCH_AMD instead of
removing it, so unsupported vendors are skipped rather than reported
as a failure.
tools/testing/selftests/resctrl/mba_test.c | 2 +-
tools/testing/selftests/resctrl/mbm_test.c | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/tools/testing/selftests/resctrl/mba_test.c b/tools/testing/selftests/resctrl/mba_test.c
index 947735e26099..95250cfa5cd8 100644
--- a/tools/testing/selftests/resctrl/mba_test.c
+++ b/tools/testing/selftests/resctrl/mba_test.c
@@ -272,7 +272,7 @@ static bool mba_feature_check(const struct resctrl_test *test)
struct resctrl_test mba_test = {
.name = "MBA",
.resource = "MB",
- .vendor_specific = ARCH_INTEL,
+ .vendor_specific = ARCH_INTEL | ARCH_AMD,
.feature_check = mba_feature_check,
.run_test = mba_run_test,
.cleanup = mba_test_cleanup,
diff --git a/tools/testing/selftests/resctrl/mbm_test.c b/tools/testing/selftests/resctrl/mbm_test.c
index e24c6237a22f..b2c5fd0ab8e3 100644
--- a/tools/testing/selftests/resctrl/mbm_test.c
+++ b/tools/testing/selftests/resctrl/mbm_test.c
@@ -178,7 +178,7 @@ static bool mbm_feature_check(const struct resctrl_test *test)
struct resctrl_test mbm_test = {
.name = "MBM",
.resource = "MB",
- .vendor_specific = ARCH_INTEL,
+ .vendor_specific = ARCH_INTEL | ARCH_AMD,
.feature_check = mbm_feature_check,
.run_test = mbm_run_test,
.cleanup = mbm_test_cleanup,
--
2.43.0
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v5 1/5] selftests/resctrl: Rename variables and functions to generic names
2026-10-22 13:25 ` [PATCH v5 1/5] selftests/resctrl: Rename variables and functions to generic names Swapnil Sapkal
@ 2026-10-28 18:38 ` Reinette Chatre
0 siblings, 0 replies; 11+ messages in thread
From: Reinette Chatre @ 2026-10-28 18:38 UTC (permalink / raw)
To: Swapnil Sapkal, tony.luck, babu.moger, fenghua.yu, shuah
Cc: Dave.Martin, james.morse, peternewman, eranian, tan.shaopeng,
linux-kernel, linux-kselftest, x86
Hi Swapnil,
On 10/22/26 6:25 AM, Swapnil Sapkal wrote:
> From: Babu Moger <babu.moger@amd.com>
>
> For Intel, the memory controller is called Integrated Memory Controller
> (IMC). For AMD, it is called Unified Memory Controller (UMC).
>
> Rename the variables and functions from imc (Integrated Memory
> Controller) to the generic mc (Memory Controller) in preparation for
> the support of MBM and MBA tests on AMD.
>
> No functional change intended.
>
> Assisted-by: Claude:claude-opus-4.8
There was a decision at LPC that this tag is no longer needed. This was
formalized with the following recent commit:
1e035297ffda ("docs: remove Assisted-by references")
Please remove the "Assisted-by:" tag from all patches and instead add
a statement to the cover letter about how AI was used during creation of
this series.
> Signed-off-by: Babu Moger <babu.moger@amd.com>
> Co-developed-by: Swapnil Sapkal <swapnil.sapkal@amd.com>
> Signed-off-by: Swapnil Sapkal <swapnil.sapkal@amd.com>
> ---
> tools/testing/selftests/resctrl/mba_test.c | 28 +-
> tools/testing/selftests/resctrl/mbm_test.c | 24 +-
> tools/testing/selftests/resctrl/resctrl.h | 6 +-
> tools/testing/selftests/resctrl/resctrl_val.c | 256 +++++++++---------
> 4 files changed, 157 insertions(+), 157 deletions(-)
>
> diff --git a/tools/testing/selftests/resctrl/resctrl_val.c b/tools/testing/selftests/resctrl/resctrl_val.c
> index 7e7d0f9cc249..c9cac957726b 100644
> --- a/tools/testing/selftests/resctrl/resctrl_val.c
> +++ b/tools/testing/selftests/resctrl/resctrl_val.c
...
> @@ -83,20 +83,20 @@ static void get_read_event_and_umask(char *cas_count_cfg, struct imc_counter_con
> if (!token[i])
> break;
> if (strcmp(token[i], "event") == 0)
> - imc_counter->event = strtol(token[i + 1], NULL, 16);
> + mc_counter->event = strtol(token[i + 1], NULL, 16);
> if (strcmp(token[i], "umask") == 0)
> - imc_counter->umask = strtol(token[i + 1], NULL, 16);
> + mc_counter->umask = strtol(token[i + 1], NULL, 16);
> }
> }
>
> -static int open_perf_read_event(int cpu_no, struct imc_counter_config *imc_counter)
> +static int open_perf_read_event(int cpu_no, struct mc_counter_config *mc_counter)
> {
> - imc_counter->fd = perf_event_open(&imc_counter->pe, -1, cpu_no, -1,
> + mc_counter->fd = perf_event_open(&mc_counter->pe, -1, cpu_no, -1,
> PERF_FLAG_FD_CLOEXEC);
nit: please fixup the alignment with open parenthesis
>
> - if (imc_counter->fd == -1) {
> + if (mc_counter->fd == -1) {
> fprintf(stderr, "Error opening leader %llx\n",
> - imc_counter->pe.config);
> + mc_counter->pe.config);
>
> return -1;
> }
...
> @@ -344,54 +344,54 @@ static int perf_open_imc_read_mem_bw(int cpu_no)
> return 0;
>
> close_fds:
> - perf_close_imc_read_mem_bw();
> + perf_close_mc_read_mem_bw();
> return -1;
> }
>
> /*
> - * do_imc_read_mem_bw_test - Perform memory bandwidth test
> + * do_mc_read_mem_bw_test - Perform memory bandwidth test
> *
> * Runs memory bandwidth test over one second period. Also, handles starting
> - * and stopping of the IMC perf counters around the test.
> + * and stopping of the MC perf counters around the test.
> */
> -static void do_imc_read_mem_bw_test(void)
> +static void do_mc_read_mem_bw_test(void)
> {
> - struct imc_counter_config *imc_counter;
> + struct mc_counter_config *mc_counter;
>
> - list_for_each_entry(imc_counter, &imc_counters_list, entry)
> - read_mem_bw_ioctl_perf_event_ioc_reset_enable(imc_counter);
> + list_for_each_entry(mc_counter, &mc_counters_list, entry)
> + read_mem_bw_ioctl_perf_event_ioc_reset_enable(mc_counter);
>
> sleep(1);
>
> /* Stop counters after a second to get results. */
> - list_for_each_entry(imc_counter, &imc_counters_list, entry)
> - read_mem_bw_ioctl_perf_event_ioc_disable(imc_counter);
> + list_for_each_entry(mc_counter, &mc_counters_list, entry)
> + read_mem_bw_ioctl_perf_event_ioc_disable(mc_counter);
> }
>
> /*
> - * get_read_mem_bw_imc - Memory read bandwidth as reported by iMC counters
> + * get_read_mem_bw_mc - Memory read bandwidth as reported by MC counters
> *
> * Memory read bandwidth utilized by a process on a socket can be calculated
> - * using iMC counters' read events. Perf events are used to read these
> + * using MC counters' read events. Perf events are used to read these
> * counters.
nit: please move this dangling "counters." to the previous line.
> *
> * Return: = 0 on success. < 0 on failure.
> */
> -static int get_read_mem_bw_imc(float *bw_imc)
> +static int get_read_mem_bw_mc(float *bw_mc)
| Reviewed-by: Reinette Chatre <reinette.chatre@intel.com>
Reinette
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v5 2/5] selftests/resctrl: Detect vendor to select memory controller sysfs name
2026-10-22 13:25 ` [PATCH v5 2/5] selftests/resctrl: Detect vendor to select memory controller sysfs name Swapnil Sapkal
@ 2026-10-28 18:39 ` Reinette Chatre
0 siblings, 0 replies; 11+ messages in thread
From: Reinette Chatre @ 2026-10-28 18:39 UTC (permalink / raw)
To: Swapnil Sapkal, tony.luck, babu.moger, shuah
Cc: Dave.Martin, james.morse, peternewman, eranian, tan.shaopeng,
linux-kernel, linux-kselftest, x86
Hi Swapnil,
On 10/22/26 6:25 AM, Swapnil Sapkal wrote:
> diff --git a/tools/testing/selftests/resctrl/resctrl_val.c b/tools/testing/selftests/resctrl/resctrl_val.c
> index c9cac957726b..fdbbe2155cea 100644
> --- a/tools/testing/selftests/resctrl/resctrl_val.c
> +++ b/tools/testing/selftests/resctrl/resctrl_val.c
> @@ -10,7 +10,7 @@
> */
> #include "resctrl.h"
>
> -#define UNCORE_IMC "uncore_imc"
> +#define UNCORE_IMC "uncore_imc_"
When considering the next patch introduces "AMD_UMC", could
"UNCORE_IMC" be renamed to "INTEL_IMC" to better support how
this series ends up managing the architectural differences?
> #define READ_FILE_NAME "cas_count_read"
> #define DYN_PMU_PATH "/sys/bus/event_source/devices"
> #define SCALE 0.00006103515625
Reinette
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v5 3/5] selftests/resctrl: Add support for MBM and MBA tests on AMD
2026-10-22 13:25 ` [PATCH v5 3/5] selftests/resctrl: Add support for MBM and MBA tests on AMD Swapnil Sapkal
@ 2026-10-28 18:41 ` Reinette Chatre
0 siblings, 0 replies; 11+ messages in thread
From: Reinette Chatre @ 2026-10-28 18:41 UTC (permalink / raw)
To: Swapnil Sapkal, tony.luck, babu.moger, shuah
Cc: Dave.Martin, james.morse, peternewman, eranian, tan.shaopeng,
linux-kernel, linux-kselftest, x86
Hi Swapnil,
> diff --git a/tools/testing/selftests/resctrl/resctrl_val.c b/tools/testing/selftests/resctrl/resctrl_val.c
> index fdbbe2155cea..38cf58c41be1 100644
> --- a/tools/testing/selftests/resctrl/resctrl_val.c
> +++ b/tools/testing/selftests/resctrl/resctrl_val.c
> @@ -233,6 +277,7 @@ static int read_from_mc_dir(char *mc_dir)
> */
> static int enumerate_mcs(void)
> {
> + int (*read_mc_dir)(char *mc_dir);
Could this be named read_from_mc_dir to avoid the unnecessary churn?
> char mc_dir[512], *temp;
> struct dirent *ep;
> char *sysfs_name;
> @@ -241,6 +286,10 @@ static int enumerate_mcs(void)
>
> if (get_vendor() == ARCH_INTEL) {
> sysfs_name = UNCORE_IMC;
> + read_mc_dir = read_from_intel_mc_dir;
> + } else if (get_vendor() == ARCH_AMD) {
> + sysfs_name = AMD_UMC;
> + read_mc_dir = read_from_amd_mc_dir;
> } else {
> ksft_print_msg("Unsupported vendor\n");
> return -1;
...
}
> @@ -335,15 +387,40 @@ static void perf_close_mc_read_mem_bw(void)
> static int perf_open_mc_read_mem_bw(int cpu_no)
> {
> struct mc_counter_config *mc_counter;
> - int ret;
> + int ret, opened = 0;
>
> list_for_each_entry(mc_counter, &mc_counters_list, entry)
> mc_counter->fd = -1;
>
> list_for_each_entry(mc_counter, &mc_counters_list, entry) {
> ret = open_perf_read_event(cpu_no, mc_counter);
> - if (ret)
> - goto close_fds;
> + if (!ret) {
> + opened++;
> + continue;
> + }
> +
> + /*
> + * On AMD each memory controller (UMC) has its own PMU that is
> + * tied to a specific NUMA node, unlike Intel where opening an
> + * iMC event on the benchmark CPU always resolves to the local
> + * memory controllers. A UMC PMU can only be opened from a CPU
> + * that belongs to the same node and returns -ENODEV otherwise.
> + * The benchmark runs on a single CPU, so skip the UMCs that
> + * belong to remote nodes and measure only the memory
> + * controllers local to the benchmark. This matches what
> + * mbm_local_bytes accounts for on the resctrl side.
> + */
> + if (ret == -ENODEV && get_vendor() == ARCH_AMD)
> + continue;
> +
> + ksft_print_msg("Error opening memory bandwidth performance counter: %s\n",
> + strerror(-ret));
> + goto close_fds;
> + }
> +
> + if (!opened) {
> + ksft_print_msg("Could not open any memory controller counters\n");
> + goto close_fds;
> }
As the comment above highlights, the test runs on a single CPU. It is thus not
possible for the test to change which MC it needs to obtain data from.
perf_open_mc_read_mem_bw() is called several times during the test run and
repeated attempts at accessing MC that are known to fail is unnecessary.
Considering that -ENODEV can be relied on to indicate that the MC is not
valid for the test run, could it instead be used to drop that MC from
mc_counters_list? This will avoid the subsequent unnecessary attempts at
accessing these MC and also simplify the users that walk mc_counters_list
during the test run since they can continue to rely on all MC being valid
without needing to keep checking if the fd is valid.
I suggest switching the list_for_each_entry() to list_for_each_entry_safe()
and drop the MC's from the list on ENODEV. This also eliminates the need for the
"opened" tracking that can be replaced with a list_empty(&mc_counters_list).
>
> return 0;
> @@ -363,14 +440,20 @@ static void do_mc_read_mem_bw_test(void)
> {
> struct mc_counter_config *mc_counter;
>
> - list_for_each_entry(mc_counter, &mc_counters_list, entry)
> + list_for_each_entry(mc_counter, &mc_counters_list, entry) {
> + if (mc_counter->fd == -1)
> + continue;
> read_mem_bw_ioctl_perf_event_ioc_reset_enable(mc_counter);
> + }
>
> sleep(1);
>
> /* Stop counters after a second to get results. */
> - list_for_each_entry(mc_counter, &mc_counters_list, entry)
> + list_for_each_entry(mc_counter, &mc_counters_list, entry) {
> + if (mc_counter->fd == -1)
> + continue;
> read_mem_bw_ioctl_perf_event_ioc_disable(mc_counter);
> + }
> }
>
> /*
> @@ -395,6 +478,10 @@ static int get_read_mem_bw_mc(float *bw_mc)
> list_for_each_entry(r, &mc_counters_list, entry) {
> struct membw_read_format measurement;
>
> + /* Skip memory controllers that were not opened (remote node). */
> + if (r->fd == -1)
> + continue;
> +
> if (read(r->fd, &measurement, sizeof(measurement)) == -1) {
> ksft_perror("Couldn't get read bandwidth through MC");
> return -1;
With unusable MC dropped from mc_counters_list all the above fd checks are
unnecessary.
Reinette
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v5 4/5] selftests/resctrl: Use bandwidth values for AMD MBA schemata
2026-10-22 13:25 ` [PATCH v5 4/5] selftests/resctrl: Use bandwidth values for AMD MBA schemata Swapnil Sapkal
@ 2026-10-28 18:45 ` Reinette Chatre
0 siblings, 0 replies; 11+ messages in thread
From: Reinette Chatre @ 2026-10-28 18:45 UTC (permalink / raw)
To: Swapnil Sapkal, tony.luck, babu.moger, fenghua.yu, shuah
Cc: Dave.Martin, james.morse, peternewman, eranian, tan.shaopeng,
linux-kernel, linux-kselftest, x86
Hi Swapnil,
On 10/22/26 6:25 AM, Swapnil Sapkal wrote:
> diff --git a/tools/testing/selftests/resctrl/mba_test.c b/tools/testing/selftests/resctrl/mba_test.c
> index 17ae73d3476c..947735e26099 100644
> --- a/tools/testing/selftests/resctrl/mba_test.c
> +++ b/tools/testing/selftests/resctrl/mba_test.c
> @@ -13,10 +13,51 @@
> #define RESULT_FILE_NAME "result_mba"
> #define NUM_OF_RUNS 5
> #define MAX_DIFF_PERCENT 15
> +
> +/* Intel MBA is specified as a percentage: 10%, 20% ... 100%. */
The percentage based interface is the generic interface and not Intel specific.
You can drop "Intel" from references to this interface.
> #define ALLOCATION_MAX 100
> #define ALLOCATION_MIN 10
> #define ALLOCATION_STEP 10
>
> +/*
> + * AMD MBA is not a percentage but an absolute memory bandwidth value. The
> + * test sweeps the bandwidth from 4 GB/s to 40 GB/s in steps of 4 GB/s,
> + * which the hardware represents in units of 1/8 GB/s (i.e. 4 GB/s -> 32,
> + * 8 GB/s -> 64, ... 40 GB/s -> 320).
> + */
> +#define AMD_ALLOCATION_MAX 320
The AMD maximum is enumerated from hardware. How can it be ensured this is
the appropriate maximum for all AMD systems?
> +#define AMD_ALLOCATION_MIN 32
> +#define AMD_ALLOCATION_STEP 32
> +
> +/* Number of MBA schemata allocations to sweep (same for both vendors). */
> +#define NUM_OF_ALLOCS (ALLOCATION_MAX / ALLOCATION_STEP)
> +
> +/*
> + * The fixed-size result arrays in check_results() and the aggregation loop in
> + * show_mba_info() are sized and driven by NUM_OF_ALLOCS, so each vendor's sweep
> + * must produce exactly NUM_OF_ALLOCS allocations. Enforce this at build time so
> + * that changing a vendor's min/max/step can never overflow those arrays.
> + */
> +_Static_assert((ALLOCATION_MAX - ALLOCATION_MIN) / ALLOCATION_STEP + 1 == NUM_OF_ALLOCS,
> + "Intel MBA sweep must yield NUM_OF_ALLOCS allocations");
Considering how NUM_ALLOCS is defined, could this ever be encountered?
Could this instead be
#define NUM_ALLOCS 10
to ensure the sanity checks is accurate in both scenarios without deriving a
architecture neutral count from Intel counts?
> +_Static_assert((AMD_ALLOCATION_MAX - AMD_ALLOCATION_MIN) / AMD_ALLOCATION_STEP + 1 == NUM_OF_ALLOCS,
> + "AMD MBA sweep must yield NUM_OF_ALLOCS allocations");
Could _Static_assert be the more flexible static_assert() (and include assert.h)?
> +
> +static unsigned int mba_alloc_min(void)
> +{
> + return get_vendor() == ARCH_AMD ? AMD_ALLOCATION_MIN : ALLOCATION_MIN;
> +}
> +
> +static unsigned int mba_alloc_max(void)
> +{
> + return get_vendor() == ARCH_AMD ? AMD_ALLOCATION_MAX : ALLOCATION_MAX;
> +}
> +
> +static unsigned int mba_alloc_step(void)
> +{
> + return get_vendor() == ARCH_AMD ? AMD_ALLOCATION_STEP : ALLOCATION_STEP;
> +}
> +
> static int mba_init(const struct resctrl_test *test,
> const struct user_params *uparams,
> const struct resctrl_val_param *param, int domain_id)
> @@ -33,15 +74,17 @@ static int mba_init(const struct resctrl_test *test,
> }
>
> /*
> - * Change schemata percentage from 100 to 10%. Write schemata to specified
> - * con_mon grp, mon_grp in resctrl FS.
> + * Sweep the MBA schemata from the minimum to the maximum allocation. On
> + * Intel the allocation is a percentage (10% .. 100%); on AMD it is an
> + * absolute memory bandwidth value (4 GB/s .. 40 GB/s). Write schemata to
> + * the specified con_mon grp, mon_grp in resctrl FS.
> * For each allocation, run 5 times in order to get average values.
> */
> static int mba_setup(const struct resctrl_test *test,
> const struct user_params *uparams,
> struct resctrl_val_param *p)
> {
> - static unsigned int allocation = ALLOCATION_MIN;
> + static unsigned int allocation;
> static int runs_per_allocation;
> char allocation_str[64];
> int ret;
> @@ -53,16 +96,20 @@ static int mba_setup(const struct resctrl_test *test,
> if (runs_per_allocation++ != 0)
> return 0;
>
> - if (allocation > ALLOCATION_MAX)
> + /* Set the initial allocation once the vendor is known. */
> + if (allocation == 0)
> + allocation = mba_alloc_min();
This does not look right. "once the vendor is known" does not look to be the
real reason here since vendor is known at this point. This just seems to be
a workaround to assign a function return to a static? It seems risky though since
0 is a valid minimum for AMD and that would cause problems if ever used with this
implementation.
show_mba_info() already treats the allocation as an index as opposed to an
accumulated value. Could this be simplified by using an index while also making
it consistent with show_mba_info()?
Consider, for example, something like:
static int runs_per_allocation;
static unsigned int alloc_idx;
char allocation_str[64];
unsigned int allocation;
int ret;
...
if (alloc_idx >= NUM_OF_ALLOCS)
return END_OF_TESTS;
allocation = mba_alloc_min() + mba_alloc_step() * alloc_idx;
alloc_idx++;
sprintf(allocation_str, "%u", allocation);
I think having everything bounded by NUM_ALLOCS instead of mba_setup()
bounded on accumulated allocation would make this overall easier to
understand.
This would make mba_alloc_max() unnecessary.
> +
> + if (allocation > mba_alloc_max())
> return END_OF_TESTS;
>
> - sprintf(allocation_str, "%d", allocation);
> + sprintf(allocation_str, "%u", allocation);
>
> ret = write_schemata(p->ctrlgrp, allocation_str, uparams->cpu, test->resource);
> if (ret < 0)
> return ret;
>
> - allocation += ALLOCATION_STEP;
> + allocation += mba_alloc_step();
>
> return 0;
> }
> @@ -75,14 +122,15 @@ static int mba_measure(const struct user_params *uparams,
>
> static bool show_mba_info(unsigned long *bw_mc, unsigned long *bw_resc)
> {
> + unsigned int alloc_min = mba_alloc_min();
> + unsigned int alloc_step = mba_alloc_step();
Please maintain reverse fir order.
> unsigned int allocation;
> bool ret = false;
> int runs;
>
> ksft_print_msg("Results are displayed in (MB)\n");
> - /* Memory bandwidth from 100% down to 10% */
> - for (allocation = 0; allocation < ALLOCATION_MAX / ALLOCATION_STEP;
> - allocation++) {
> + /* Memory bandwidth for each of the swept MBA allocations */
> + for (allocation = 0; allocation < NUM_OF_ALLOCS; allocation++) {
As reference to earlier comment, here allocation is an index limited by NUM_OF_ALLOCS
> unsigned long sum_bw_mc = 0, sum_bw_resc = 0;
> long avg_bw_mc, avg_bw_resc;
> int avg_diff_per;
> @@ -99,7 +147,7 @@ static bool show_mba_info(unsigned long *bw_mc, unsigned long *bw_resc)
> if (avg_bw_mc < THROTTLE_THRESHOLD || avg_bw_resc < THROTTLE_THRESHOLD) {
> ksft_print_msg("Bandwidth below threshold (%d MiB). Dropping results from MBA schemata %u.\n",
> THROTTLE_THRESHOLD,
> - ALLOCATION_MIN + ALLOCATION_STEP * allocation);
> + alloc_min + alloc_step * allocation);
> continue;
> }
>
> @@ -110,7 +158,7 @@ static bool show_mba_info(unsigned long *bw_mc, unsigned long *bw_resc)
> avg_diff_per > MAX_DIFF_PERCENT ?
> "Fail:" : "Pass:",
> MAX_DIFF_PERCENT,
> - ALLOCATION_MIN + ALLOCATION_STEP * allocation);
> + alloc_min + alloc_step * allocation);
>
> ksft_print_msg("avg_diff_per: %d%%\n", avg_diff_per);
> ksft_print_msg("avg_bw_mc: %lu\n", avg_bw_mc);
> @@ -129,8 +177,8 @@ static bool show_mba_info(unsigned long *bw_mc, unsigned long *bw_resc)
>
> static int check_results(void)
> {
> - unsigned long bw_resc[NUM_OF_RUNS * ALLOCATION_MAX / ALLOCATION_STEP];
> - unsigned long bw_mc[NUM_OF_RUNS * ALLOCATION_MAX / ALLOCATION_STEP];
> + unsigned long bw_resc[NUM_OF_RUNS * NUM_OF_ALLOCS];
> + unsigned long bw_mc[NUM_OF_RUNS * NUM_OF_ALLOCS];
Please re-order as part of this change to maintain reverse fir.
> char *token_array[8], output[] = RESULT_FILE_NAME, temp[512];
> int runs;
> FILE *fp;
> @@ -147,6 +195,11 @@ static int check_results(void)
> char *token = strtok(temp, ":\t");
> int fields = 0;
>
> + if (runs >= NUM_OF_RUNS * NUM_OF_ALLOCS) {
> + ksft_print_msg("Got more results than expected\n");
> + break;
The message is worded as a failure but the test continues. This is potentially
confusing. If it is a failure then test is expected to stop with an error, if it
is not then the wording of the message could be adjusted to not imply so.
Even so, this change does not seem related to enabling support needed by AMD
systems?
> + }
> +
> while (token) {
> token_array[fields++] = token;
> token = strtok(NULL, ":\t");
Reinette
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v5 5/5] selftests/resctrl: Enable MBM and MBA tests on AMD
2026-10-22 13:25 ` [PATCH v5 5/5] selftests/resctrl: Enable MBM and MBA tests on AMD Swapnil Sapkal
@ 2026-10-28 18:45 ` Reinette Chatre
0 siblings, 0 replies; 11+ messages in thread
From: Reinette Chatre @ 2026-10-28 18:45 UTC (permalink / raw)
To: Swapnil Sapkal, tony.luck, babu.moger, shuah
Cc: Dave.Martin, james.morse, peternewman, eranian, tan.shaopeng,
linux-kernel, linux-kselftest, x86
Hi Swapnil,
On 10/22/26 6:25 AM, Swapnil Sapkal wrote:
...> diff --git a/tools/testing/selftests/resctrl/mbm_test.c b/tools/testing/selftests/resctrl/mbm_test.c
> index e24c6237a22f..b2c5fd0ab8e3 100644
> --- a/tools/testing/selftests/resctrl/mbm_test.c
> +++ b/tools/testing/selftests/resctrl/mbm_test.c
> @@ -178,7 +178,7 @@ static bool mbm_feature_check(const struct resctrl_test *test)
> struct resctrl_test mbm_test = {
> .name = "MBM",
> .resource = "MB",
> - .vendor_specific = ARCH_INTEL,
> + .vendor_specific = ARCH_INTEL | ARCH_AMD,
> .feature_check = mbm_feature_check,
> .run_test = mbm_run_test,
> .cleanup = mbm_test_cleanup,
The issue reported by sahiko looks real to me:
https://sashiko.dev/#/patchset/20261022132517.46805-1-swapnil.sapkal%40amd.com
Reinette
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-10-28 18:45 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-22 13:25 [PATCH v5 0/5] selftests/resctrl: Add MBM and MBA test support for AMD Swapnil Sapkal
2026-10-22 13:25 ` [PATCH v5 1/5] selftests/resctrl: Rename variables and functions to generic names Swapnil Sapkal
2026-10-28 18:38 ` Reinette Chatre
2026-10-22 13:25 ` [PATCH v5 2/5] selftests/resctrl: Detect vendor to select memory controller sysfs name Swapnil Sapkal
2026-10-28 18:39 ` Reinette Chatre
2026-10-22 13:25 ` [PATCH v5 3/5] selftests/resctrl: Add support for MBM and MBA tests on AMD Swapnil Sapkal
2026-10-28 18:41 ` Reinette Chatre
2026-10-22 13:25 ` [PATCH v5 4/5] selftests/resctrl: Use bandwidth values for AMD MBA schemata Swapnil Sapkal
2026-10-28 18:45 ` Reinette Chatre
2026-10-22 13:25 ` [PATCH v5 5/5] selftests/resctrl: Enable MBM and MBA tests on AMD Swapnil Sapkal
2026-10-28 18:45 ` Reinette Chatre
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®