The reference bandwidth measurement is hard-wired to the Intel iMC:
discovery, event configuration and perf open all assume the iMC PMU
sysfs layout. Other architectures expose equivalent uncore PMUs that
could serve as the same kind of independent reference.

Introduce struct mem_bw_backend with two operations, detect() (is this
backend's PMU present?) and setup_counters() (populate bw_counters[]
for measuring the benchmark CPU), plus a backend table currently
containing only the iMC. Shared code selects a backend at init time
and no longer contains any PMU-specific knowledge.

Counter configuration moves from test init time to perf open time via
setup_counters(), because a backend may need the benchmark CPU to pick
per-socket PMU instances. Each counter now carries the CPU to open it
on. The added per-measurement discovery cost is a handful of sysfs
reads, negligible against the one second measurement window.

Add mem_bw_ref_available() so feature checks can verify that a
reference PMU exists: the MBA test is now skipped instead of failing
at init on machines without iMC counters (e.g. virtual machines).
Add mem_bw_ref_cleanup() to reset the backend selection between tests.

Signed-off-by: Richard Cheng <[email protected]>
---
 tools/testing/selftests/resctrl/mba_test.c    |   4 +-
 tools/testing/selftests/resctrl/mbm_test.c    |   1 +
 tools/testing/selftests/resctrl/resctrl.h     |   2 +
 tools/testing/selftests/resctrl/resctrl_val.c | 145 ++++++++++++++++--
 4 files changed, 141 insertions(+), 11 deletions(-)

diff --git a/tools/testing/selftests/resctrl/mba_test.c 
b/tools/testing/selftests/resctrl/mba_test.c
index 468ba8accc18..8b891d3e6650 100644
--- a/tools/testing/selftests/resctrl/mba_test.c
+++ b/tools/testing/selftests/resctrl/mba_test.c
@@ -164,6 +164,7 @@ static int check_results(void)
 
 static void mba_test_cleanup(void)
 {
+       mem_bw_ref_cleanup();
        remove(RESULT_FILE_NAME);
 }
 
@@ -210,7 +211,8 @@ static int mba_run_test(const struct resctrl_test *test, 
const struct user_param
 static bool mba_feature_check(const struct resctrl_test *test)
 {
        return test_resource_feature_check(test) &&
-              resctrl_mon_feature_exists("L3_MON", "mbm_local_bytes");
+              resctrl_mon_feature_exists("L3_MON", "mbm_local_bytes") &&
+              mem_bw_ref_available();
 }
 
 struct resctrl_test mba_test = {
diff --git a/tools/testing/selftests/resctrl/mbm_test.c 
b/tools/testing/selftests/resctrl/mbm_test.c
index 001a87608835..873acf6aa648 100644
--- a/tools/testing/selftests/resctrl/mbm_test.c
+++ b/tools/testing/selftests/resctrl/mbm_test.c
@@ -123,6 +123,7 @@ static int mbm_measure(const struct user_params *uparams,
 
 static void mbm_test_cleanup(void)
 {
+       mem_bw_ref_cleanup();
        remove(RESULT_FILE_NAME);
 }
 
diff --git a/tools/testing/selftests/resctrl/resctrl.h 
b/tools/testing/selftests/resctrl/resctrl.h
index 0de72327a085..2bd19c22a344 100644
--- a/tools/testing/selftests/resctrl/resctrl.h
+++ b/tools/testing/selftests/resctrl/resctrl.h
@@ -188,6 +188,8 @@ 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_mem_bw_ref(void);
+bool mem_bw_ref_available(void);
+void mem_bw_ref_cleanup(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 ee44fbae2cbf..a2b919bd4839 100644
--- a/tools/testing/selftests/resctrl/resctrl_val.c
+++ b/tools/testing/selftests/resctrl/resctrl_val.c
@@ -34,12 +34,29 @@ struct mem_bw_counter {
        struct perf_event_attr pe;
        struct membw_read_format return_value;
        int fd;
+       int cpu;
+};
+
+/*
+ * mem_bw_backend - Source of the independent reference bandwidth that
+ *                 validates resctrl MBM
+ * @name:              Human-readable PMU name
+ * @detect:            True if this backend's PMU is present on the system
+ * @setup_counters:    Populate bw_counters[] / nr_bw_counters with the perf
+ *                     events measuring memory bandwidth generated from
+ *                     @bench_cpu. Returns 0 on success, < 0 on failure.
+ */
+struct mem_bw_backend {
+       const char *name;
+       bool (*detect)(void);
+       int (*setup_counters)(int bench_cpu);
 };
 
 static char mbm_total_path[1024];
 static int nr_bw_counters;
 static struct mem_bw_counter bw_counters[MAX_BW_COUNTERS];
 static const struct resctrl_test *current_test;
+static const struct mem_bw_backend *mem_bw_backend;
 
 static void read_mem_bw_initialize_perf_event_attr(int i)
 {
@@ -94,10 +111,11 @@ static void get_read_event_and_umask(char *cas_count_cfg, 
int count)
        }
 }
 
-static int open_perf_read_event(int i, int cpu_no)
+static int open_perf_read_event(int i)
 {
        bw_counters[i].fd =
-               perf_event_open(&bw_counters[i].pe, -1, cpu_no, -1,
+               perf_event_open(&bw_counters[i].pe, -1,
+                               bw_counters[i].cpu, -1,
                                PERF_FLAG_FD_CLOEXEC);
 
        if (bw_counters[i].fd == -1) {
@@ -221,21 +239,108 @@ static int num_of_imcs(void)
        return count;
 }
 
-int initialize_mem_bw_ref(void)
+/* True if any x86 uncore iMC PMU ("uncore_imc_<n>") is present. */
+static bool intel_imc_present(void)
 {
-       int i;
+       struct dirent *ep;
+       bool found = false;
+       char *temp;
+       DIR *dp;
 
-       nr_bw_counters = num_of_imcs();
-       if (nr_bw_counters <= 0)
-               return nr_bw_counters;
+       dp = opendir(DYN_PMU_PATH);
+       if (!dp)
+               return false;
+       while ((ep = readdir(dp))) {
+               temp = strstr(ep->d_name, UNCORE_IMC);
+               if (!temp)
+                       continue;
+               temp += sizeof(UNCORE_IMC);
+               if (temp[0] >= '0' && temp[0] <= '9') {
+                       found = true;
+                       break;
+               }
+       }
+       closedir(dp);
+       return found;
+}
 
-       /* Initialize perf_event_attr structures for all iMC's */
-       for (i = 0; i < nr_bw_counters; i++)
+/*
+ * imc_setup_counters - Configure iMC CAS-count-read counters
+ * @bench_cpu: CPU the benchmark is bound to
+ *
+ * Discovers all iMC PMUs and configures one read-bandwidth counter per iMC,
+ * opened on the benchmark CPU (each iMC counts socket-wide regardless).
+ *
+ * Return: 0 on success, < 0 on failure.
+ */
+static int imc_setup_counters(int bench_cpu)
+{
+       int i, count;
+
+       count = num_of_imcs();
+       if (count <= 0)
+               return -1;
+
+       nr_bw_counters = count;
+       for (i = 0; i < nr_bw_counters; i++) {
                read_mem_bw_initialize_perf_event_attr(i);
+               bw_counters[i].cpu = bench_cpu;
+       }
 
        return 0;
 }
 
+static const struct mem_bw_backend mem_bw_backends[] = {
+       {
+               .name = "Intel iMC",
+               .detect = intel_imc_present,
+               .setup_counters = imc_setup_counters,
+       },
+};
+
+/*
+ * mem_bw_ref_available - Is an independent reference-bandwidth PMU present?
+ *
+ * Used by the MBA/MBM feature checks so the test is SKIPped (not failed) on
+ * hardware lacking a usable memory-bandwidth PMU.
+ */
+bool mem_bw_ref_available(void)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(mem_bw_backends); i++) {
+               if (mem_bw_backends[i].detect())
+                       return true;
+       }
+
+       return false;
+}
+
+/*
+ * initialize_mem_bw_ref - Select the reference-bandwidth backend for this 
system
+ *
+ * Counter discovery happens at perf-open time through the backend's
+ * setup_counters() since it can depend on the benchmark CPU.
+ *
+ * Return: 0 on success, < 0 if no reference PMU is available.
+ */
+int initialize_mem_bw_ref(void)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(mem_bw_backends); i++) {
+               if (mem_bw_backends[i].detect()) {
+                       mem_bw_backend = &mem_bw_backends[i];
+                       ksft_print_msg("Using %s as memory bandwidth 
reference\n",
+                                      mem_bw_backend->name);
+                       return 0;
+               }
+       }
+
+       ksft_print_msg("No memory bandwidth reference PMU found\n");
+       return -1;
+}
+
 static void perf_close_mem_bw_counters(void)
 {
        int i;
@@ -250,17 +355,27 @@ static void perf_close_mem_bw_counters(void)
  * perf_open_mem_bw_counters - Open perf fds for the reference-bandwidth 
counters
  * @cpu_no: CPU number that the benchmark PID is bound to
  *
+ * Counters are (re)configured by the selected backend first since their
+ * discovery can depend on the benchmark CPU (e.g. per-socket PMU instances).
+ *
  * Return: = 0 on success. < 0 on failure.
  */
 static int perf_open_mem_bw_counters(int cpu_no)
 {
        int i, ret;
 
+       if (!mem_bw_backend)
+               return -1;
+
+       ret = mem_bw_backend->setup_counters(cpu_no);
+       if (ret < 0)
+               return ret;
+
        for (i = 0; i < nr_bw_counters; i++)
                bw_counters[i].fd = -1;
 
        for (i = 0; i < nr_bw_counters; i++) {
-               ret = open_perf_read_event(i, cpu_no);
+               ret = open_perf_read_event(i);
                if (ret)
                        goto close_fds;
        }
@@ -345,6 +460,16 @@ void initialize_mem_bw_resctrl(const struct 
resctrl_val_param *param,
                param->ctrlgrp, domain_id);
 }
 
+/*
+ * mem_bw_ref_cleanup - Reset the selected reference-bandwidth backend
+ *
+ * Safe to call when no backend was selected.
+ */
+void mem_bw_ref_cleanup(void)
+{
+       mem_bw_backend = NULL;
+}
+
 /*
  * Open file to read MBM local bytes from resctrl FS
  */
-- 
2.43.0


Reply via email to