[PATCH v4 06/10] selftests/resctrl: Add MBM test

From: Fenghua Yu
Date: Fri Dec 21 2018 - 19:26:52 EST


From: Arshiya Hayatkhan Pathan <arshiya.hayatkhan.pathan@xxxxxxxxx>

MBM (Memory Bandwidth Monitoring) test is the first implemented selftest.
It starts a stressful memory bandwidth benchmark and assigns the
bandwidth pid in a resctrl monitoring group. Read and compare perf IMC
counter and MBM total bytes for the benchmark. The numbers should be
close enough to pass the test.

Default benchmark is built-in fill_buf. But users can specify their
own benchmark by option "-b".

We can add memory bandwidth monitoring for multiple processes in the
future.

Signed-off-by: Arshiya Hayatkhan Pathan <arshiya.hayatkhan.pathan@xxxxxxxxx>
Signed-off-by: Sai Praneeth Prakhya <sai.praneeth.prakhya@xxxxxxxxx>,
Signed-off-by: Fenghua Yu <fenghua.yu@xxxxxxxxx>
---
tools/testing/selftests/resctrl/Makefile | 8 +-
tools/testing/selftests/resctrl/mbm_test.c | 145 ++++++++++++++++++
tools/testing/selftests/resctrl/resctrl.h | 3 +
.../testing/selftests/resctrl/resctrl_tests.c | 123 +++++++++++++++
tools/testing/selftests/resctrl/resctrl_val.c | 2 +
5 files changed, 280 insertions(+), 1 deletion(-)
create mode 100644 tools/testing/selftests/resctrl/mbm_test.c
create mode 100644 tools/testing/selftests/resctrl/resctrl_tests.c

diff --git a/tools/testing/selftests/resctrl/Makefile b/tools/testing/selftests/resctrl/Makefile
index bd5c5418961e..51165f9f8a9d 100644
--- a/tools/testing/selftests/resctrl/Makefile
+++ b/tools/testing/selftests/resctrl/Makefile
@@ -1,10 +1,16 @@
CC = gcc
CFLAGS = -g -Wall

+all: resctrl_tests
+
*.o: *.c
$(CC) $(CFLAGS) -c *.c

+resctrl_tests: *.o
+ $(CC) $(CFLAGS) -o resctrl_tests resctrl_tests.o resctrlfs.o \
+ membw.o fill_buf.o mbm_test.o
+
.PHONY: clean

clean:
- $(RM) *.o *~
+ $(RM) *.o *~ resctrl_tests
diff --git a/tools/testing/selftests/resctrl/mbm_test.c b/tools/testing/selftests/resctrl/mbm_test.c
new file mode 100644
index 000000000000..fc1b8bab1c71
--- /dev/null
+++ b/tools/testing/selftests/resctrl/mbm_test.c
@@ -0,0 +1,145 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Memory Bandwidth Monitoring (MBM) test
+ *
+ * Copyright (C) 2018 Intel Corporation
+ *
+ * Authors:
+ * Arshiya Hayatkhan Pathan <arshiya.hayatkhan.pathan@xxxxxxxxx>
+ * Sai Praneeth Prakhya <sai.praneeth.prakhya@xxxxxxxxx>,
+ * Fenghua Yu <fenghua.yu@xxxxxxxxx>
+ */
+#include "resctrl.h"
+
+#define RESULT_FILE_NAME "result_mbm"
+#define MAX_DIFF 300
+#define NUM_OF_RUNS 5
+
+static void
+show_bw_info(unsigned long *bw_imc, unsigned long *bw_resc, int span)
+{
+ unsigned long avg_bw_imc = 0, avg_bw_resc = 0;
+ unsigned long sum_bw_imc = 0, sum_bw_resc = 0;
+ long avg_diff = 0;
+ int runs;
+
+ /*
+ * Discard the first value which is inaccurate due to monitoring setup
+ * transition phase.
+ */
+ for (runs = 1; runs < NUM_OF_RUNS ; runs++) {
+ sum_bw_imc += bw_imc[runs];
+ sum_bw_resc += bw_resc[runs];
+ }
+
+ avg_bw_imc = sum_bw_imc / 4;
+ avg_bw_resc = sum_bw_resc / 4;
+ avg_diff = avg_bw_resc - avg_bw_imc;
+
+ printf("\nSpan (MB): %d \t", span);
+ printf("avg_bw_imc: %lu\t", avg_bw_imc);
+ printf("avg_bw_resc: %lu\t", avg_bw_resc);
+ printf("avg_diff: %lu\t", labs(avg_diff));
+
+ if (labs(avg_diff) > MAX_DIFF)
+ printf(" failed\n");
+ else
+ printf(" passed\n");
+}
+
+static int check_results(int span)
+{
+ unsigned long bw_imc[NUM_OF_RUNS], bw_resc[NUM_OF_RUNS];
+ char temp[1024], *token_array[8];
+ char output[] = RESULT_FILE_NAME;
+ int runs;
+ FILE *fp;
+
+ printf("\nChecking for pass/fail\n");
+
+ fp = fopen(output, "r");
+ if (!fp) {
+ perror("Error in opening file\n");
+
+ return errno;
+ }
+
+ runs = 0;
+ while (fgets(temp, 1024, fp)) {
+ char *token = strtok(temp, ":\t");
+ int i = 0;
+
+ while (token) {
+ token_array[i++] = token;
+ token = strtok(NULL, ":\t");
+ }
+
+ bw_resc[runs] = atol(token_array[5]);
+ bw_imc[runs] = atol(token_array[3]);
+ runs++;
+ }
+
+ show_bw_info(bw_imc, bw_resc, span);
+
+ fclose(fp);
+
+ return 0;
+}
+
+static int mbm_setup(int num, ...)
+{
+ struct resctrl_val_param *p;
+ static int num_of_runs;
+ va_list param;
+ int ret = 0;
+
+ /* Run NUM_OF_RUNS times */
+ if (num_of_runs++ >= NUM_OF_RUNS)
+ return -1;
+
+ va_start(param, num);
+ p = va_arg(param, struct resctrl_val_param *);
+ va_end(param);
+
+ /* Set up shemata with 100% allocation on the first run. */
+ if (num_of_runs == 0)
+ ret = write_schemata(p->ctrlgrp, "100", p->cpu_no,
+ p->resctrl_val);
+
+ return ret;
+}
+
+void mbm_test_cleanup(void)
+{
+ remove(RESULT_FILE_NAME);
+}
+
+int mbm_bw_change(int span, int core_id, char *bw_report, char **benchmark_cmd)
+{
+ struct resctrl_val_param param = {
+ .resctrl_val = "mbm",
+ .ctrlgrp = "c1",
+ .mongrp = "m1",
+ .span = span,
+ .cpu_no = core_id,
+ .mum_resctrlfs = 1,
+ .filename = RESULT_FILE_NAME,
+ .bw_report = bw_report,
+ .setup = mbm_setup
+ };
+ int ret;
+
+ remove(RESULT_FILE_NAME);
+
+ ret = membw_val(benchmark_cmd, &param);
+ if (ret)
+ return ret;
+
+ ret = check_results(span);
+ if (ret)
+ return ret;
+
+ mbm_test_cleanup();
+
+ return 0;
+}
diff --git a/tools/testing/selftests/resctrl/resctrl.h b/tools/testing/selftests/resctrl/resctrl.h
index 9820af253d4d..231c1eac54e0 100644
--- a/tools/testing/selftests/resctrl/resctrl.h
+++ b/tools/testing/selftests/resctrl/resctrl.h
@@ -70,5 +70,8 @@ int perf_event_open(struct perf_event_attr *hw_event, pid_t pid, int cpu,
int group_fd, unsigned long flags);
int run_fill_buf(int span, int malloc_and_init_memory, int memflush, int op);
int membw_val(char **benchmark_cmd, struct resctrl_val_param *param);
+int mbm_bw_change(int span, int core_id, char *bw_report, char **benchmark_cmd);
+void tests_cleanup(void);
+void mbm_test_cleanup(void);

#endif /* RESCTRL_H */
diff --git a/tools/testing/selftests/resctrl/resctrl_tests.c b/tools/testing/selftests/resctrl/resctrl_tests.c
new file mode 100644
index 000000000000..8a33809ee196
--- /dev/null
+++ b/tools/testing/selftests/resctrl/resctrl_tests.c
@@ -0,0 +1,123 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Resctrl tests
+ *
+ * Copyright (C) 2018 Intel Corporation
+ *
+ * Authors:
+ * Arshiya Hayatkhan Pathan <arshiya.hayatkhan.pathan@xxxxxxxxx>
+ * Sai Praneeth Prakhya <sai.praneeth.prakhya@xxxxxxxxx>,
+ * Fenghua Yu <fenghua.yu@xxxxxxxxx>
+ */
+#include "resctrl.h"
+
+#define BENCHMARK_ARGS 64
+#define BENCHMARK_ARG_SIZE 64
+
+int ben_count;
+
+static void cmd_help(void)
+{
+ printf("usage: resctrl_tests [-h] [-b \"benchmark_cmd [options]\"] [-t test list]\n");
+ printf("\t-b benchmark_cmd [options]: run specified benchmark\n");
+ printf("\t default benchmark is builtin fill_buf\n");
+ printf("\t-t test list: run tests specified in the test list, ");
+ printf("e.g. -t mbm,mba\n");
+ printf("\t-h: help\n");
+}
+
+void tests_cleanup(void)
+{
+ mbm_test_cleanup();
+}
+
+int main(int argc, char **argv)
+{
+ char benchmark_cmd_area[BENCHMARK_ARGS][BENCHMARK_ARG_SIZE];
+ int res, c, core_id = 0, span = 250, argc_new = argc, i;
+ bool has_ben = false, mbm_test = true;
+ char *benchmark_cmd[BENCHMARK_ARGS];
+ char bw_report[64], bm_type[64];
+
+ while ((c = getopt(argc_new, argv, "ht:b:")) != -1) {
+ char *token;
+
+ switch (c) {
+ case 't':
+ token = strtok(optarg, ",");
+
+ mbm_test = false;
+ while (token) {
+ if (!strcmp(token, "mbm")) {
+ mbm_test = true;
+ } else {
+ printf("invalid argument\n");
+
+ return -1;
+ }
+ token = strtok(NULL, ":\t");
+ }
+ break;
+ case 'b':
+ /* Extract benchmark command from command line. */
+ token = strtok(optarg, " ");
+ i = 0;
+ while (token) {
+ benchmark_cmd[i] = benchmark_cmd_area[i];
+ strcpy(benchmark_cmd[i++], token);
+ if (i >= BENCHMARK_ARGS) {
+ printf("Too many benchmark args\n");
+
+ return -1;
+ }
+ token = strtok(NULL, " ");
+ }
+ benchmark_cmd[i] = NULL;
+ has_ben = true;
+ break;
+ case 'h':
+ cmd_help();
+
+ return 0;
+ default:
+ printf("invalid argument\n");
+
+ return -1;
+ }
+ }
+
+ /*
+ * We need root privileges to run because
+ * 1. We write to resctrl FS
+ * 2. We execute perf commands
+ */
+ if (geteuid() != 0) {
+ perror("Please run this program as root\n");
+
+ return errno;
+ }
+
+ if (!has_ben) {
+ /* If no benchmark is given by "-b" argument, use fill_buf. */
+ for (i = 0; i < 5; i++)
+ benchmark_cmd[i] = benchmark_cmd_area[i];
+ strcpy(benchmark_cmd[0], "fill_buf");
+ sprintf(benchmark_cmd[1], "%d", span);
+ strcpy(benchmark_cmd[2], "1");
+ strcpy(benchmark_cmd[3], "1");
+ strcpy(benchmark_cmd[4], "0");
+ benchmark_cmd[5] = NULL;
+ }
+
+ sprintf(bw_report, "reads");
+ sprintf(bm_type, "fill_buf");
+
+ if (mbm_test) {
+ printf("\nMBM BW Change Starting..\n");
+ res = mbm_bw_change(span, core_id, bw_report, benchmark_cmd);
+ if (res)
+ printf("Error in running tests for mbm bw change!\n");
+ }
+
+ return 0;
+}
diff --git a/tools/testing/selftests/resctrl/resctrl_val.c b/tools/testing/selftests/resctrl/resctrl_val.c
index 9a79bf669254..fa77af5a5b57 100644
--- a/tools/testing/selftests/resctrl/resctrl_val.c
+++ b/tools/testing/selftests/resctrl/resctrl_val.c
@@ -436,6 +436,8 @@ pid_t bm_pid, ppid;
static void ctrlc_handler(int signum, siginfo_t *info, void *ptr)
{
kill(bm_pid, SIGKILL);
+ umount_resctrlfs();
+ tests_cleanup();
printf("Ending\n\n");

exit(EXIT_SUCCESS);
--
2.19.1