Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/resctrl: Add MBM test

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.

Co-developed-by: Sai Praneeth Prakhya <sai.praneeth.prakhya@intel.com>
Signed-off-by: Sai Praneeth Prakhya <sai.praneeth.prakhya@intel.com>
Co-developed-by: Babu Moger <babu.moger@amd.com>
Signed-off-by: Babu Moger <babu.moger@amd.com>
Signed-off-by: Fenghua Yu <fenghua.yu@intel.com>
Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>

authored by

Fenghua Yu and committed by
Shuah Khan
ecdbb911 a2561b12

+368 -1
+6 -1
tools/testing/selftests/resctrl/Makefile
··· 3 3 SRCS=$(wildcard *.c) 4 4 OBJS=$(SRCS:.c=.o) 5 5 6 + all: resctrl_tests 7 + 6 8 $(OBJS): $(SRCS) 7 9 $(CC) $(CFLAGS) -c $(SRCS) 10 + 11 + resctrl_tests: $(OBJS) 12 + $(CC) $(CFLAGS) -o $@ $^ 8 13 9 14 .PHONY: clean 10 15 11 16 clean: 12 - $(RM) $(OBJS) 17 + $(RM) $(OBJS) resctrl_tests
+145
tools/testing/selftests/resctrl/mbm_test.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Memory Bandwidth Monitoring (MBM) test 4 + * 5 + * Copyright (C) 2018 Intel Corporation 6 + * 7 + * Authors: 8 + * Sai Praneeth Prakhya <sai.praneeth.prakhya@intel.com>, 9 + * Fenghua Yu <fenghua.yu@intel.com> 10 + */ 11 + #include "resctrl.h" 12 + 13 + #define RESULT_FILE_NAME "result_mbm" 14 + #define MAX_DIFF 300 15 + #define NUM_OF_RUNS 5 16 + 17 + static void 18 + show_bw_info(unsigned long *bw_imc, unsigned long *bw_resc, int span) 19 + { 20 + unsigned long avg_bw_imc = 0, avg_bw_resc = 0; 21 + unsigned long sum_bw_imc = 0, sum_bw_resc = 0; 22 + long avg_diff = 0; 23 + int runs; 24 + 25 + /* 26 + * Discard the first value which is inaccurate due to monitoring setup 27 + * transition phase. 28 + */ 29 + for (runs = 1; runs < NUM_OF_RUNS ; runs++) { 30 + sum_bw_imc += bw_imc[runs]; 31 + sum_bw_resc += bw_resc[runs]; 32 + } 33 + 34 + avg_bw_imc = sum_bw_imc / 4; 35 + avg_bw_resc = sum_bw_resc / 4; 36 + avg_diff = avg_bw_resc - avg_bw_imc; 37 + 38 + printf("%sok MBM: diff within %d%%\n", 39 + labs(avg_diff) > MAX_DIFF ? "not " : "", MAX_DIFF); 40 + tests_run++; 41 + printf("# avg_diff: %lu\n", labs(avg_diff)); 42 + printf("# Span (MB): %d\n", span); 43 + printf("# avg_bw_imc: %lu\n", avg_bw_imc); 44 + printf("# avg_bw_resc: %lu\n", avg_bw_resc); 45 + } 46 + 47 + static int check_results(int span) 48 + { 49 + unsigned long bw_imc[NUM_OF_RUNS], bw_resc[NUM_OF_RUNS]; 50 + char temp[1024], *token_array[8]; 51 + char output[] = RESULT_FILE_NAME; 52 + int runs; 53 + FILE *fp; 54 + 55 + printf("# Checking for pass/fail\n"); 56 + 57 + fp = fopen(output, "r"); 58 + if (!fp) { 59 + perror(output); 60 + 61 + return errno; 62 + } 63 + 64 + runs = 0; 65 + while (fgets(temp, sizeof(temp), fp)) { 66 + char *token = strtok(temp, ":\t"); 67 + int i = 0; 68 + 69 + while (token) { 70 + token_array[i++] = token; 71 + token = strtok(NULL, ":\t"); 72 + } 73 + 74 + bw_resc[runs] = strtoul(token_array[5], NULL, 0); 75 + bw_imc[runs] = strtoul(token_array[3], NULL, 0); 76 + runs++; 77 + } 78 + 79 + show_bw_info(bw_imc, bw_resc, span); 80 + 81 + fclose(fp); 82 + 83 + return 0; 84 + } 85 + 86 + static int mbm_setup(int num, ...) 87 + { 88 + struct resctrl_val_param *p; 89 + static int num_of_runs; 90 + va_list param; 91 + int ret = 0; 92 + 93 + /* Run NUM_OF_RUNS times */ 94 + if (num_of_runs++ >= NUM_OF_RUNS) 95 + return -1; 96 + 97 + va_start(param, num); 98 + p = va_arg(param, struct resctrl_val_param *); 99 + va_end(param); 100 + 101 + /* Set up shemata with 100% allocation on the first run. */ 102 + if (num_of_runs == 0) 103 + ret = write_schemata(p->ctrlgrp, "100", p->cpu_no, 104 + p->resctrl_val); 105 + 106 + return ret; 107 + } 108 + 109 + void mbm_test_cleanup(void) 110 + { 111 + remove(RESULT_FILE_NAME); 112 + } 113 + 114 + int mbm_bw_change(int span, int cpu_no, char *bw_report, char **benchmark_cmd) 115 + { 116 + struct resctrl_val_param param = { 117 + .resctrl_val = "mbm", 118 + .ctrlgrp = "c1", 119 + .mongrp = "m1", 120 + .span = span, 121 + .cpu_no = cpu_no, 122 + .mum_resctrlfs = 1, 123 + .filename = RESULT_FILE_NAME, 124 + .bw_report = bw_report, 125 + .setup = mbm_setup 126 + }; 127 + int ret; 128 + 129 + remove(RESULT_FILE_NAME); 130 + 131 + if (!validate_resctrl_feature_request("mbm")) 132 + return -1; 133 + 134 + ret = resctrl_val(benchmark_cmd, &param); 135 + if (ret) 136 + return ret; 137 + 138 + ret = check_results(span); 139 + if (ret) 140 + return ret; 141 + 142 + mbm_test_cleanup(); 143 + 144 + return 0; 145 + }
+6
tools/testing/selftests/resctrl/resctrl.h
··· 57 57 }; 58 58 59 59 pid_t bm_pid, ppid; 60 + int tests_run; 60 61 62 + bool check_resctrlfs_support(void); 63 + int filter_dmesg(void); 61 64 int remount_resctrlfs(bool mum_resctrlfs); 62 65 int get_resource_id(int cpu_no, int *resource_id); 63 66 int umount_resctrlfs(void); ··· 78 75 int run_fill_buf(unsigned long span, int malloc_and_init_memory, int memflush, 79 76 int op, char *resctrl_va); 80 77 int resctrl_val(char **benchmark_cmd, struct resctrl_val_param *param); 78 + int mbm_bw_change(int span, int cpu_no, char *bw_report, char **benchmark_cmd); 79 + void tests_cleanup(void); 80 + void mbm_test_cleanup(void); 81 81 82 82 #endif /* RESCTRL_H */
+132
tools/testing/selftests/resctrl/resctrl_tests.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Resctrl tests 4 + * 5 + * Copyright (C) 2018 Intel Corporation 6 + * 7 + * Authors: 8 + * Sai Praneeth Prakhya <sai.praneeth.prakhya@intel.com>, 9 + * Fenghua Yu <fenghua.yu@intel.com> 10 + */ 11 + #include "resctrl.h" 12 + 13 + #define BENCHMARK_ARGS 64 14 + #define BENCHMARK_ARG_SIZE 64 15 + 16 + static void cmd_help(void) 17 + { 18 + printf("usage: resctrl_tests [-h] [-b \"benchmark_cmd [options]\"] [-t test list]\n"); 19 + printf("\t-b benchmark_cmd [options]: run specified benchmark\n"); 20 + printf("\t default benchmark is builtin fill_buf\n"); 21 + printf("\t-t test list: run tests specified in the test list, "); 22 + printf("e.g. -t mbm,mba\n"); 23 + printf("\t-h: help\n"); 24 + } 25 + 26 + void tests_cleanup(void) 27 + { 28 + mbm_test_cleanup(); 29 + } 30 + 31 + int main(int argc, char **argv) 32 + { 33 + int res, c, cpu_no = 1, span = 250, argc_new = argc, i, ben_ind; 34 + char *benchmark_cmd[BENCHMARK_ARGS], bw_report[64], bm_type[64]; 35 + char benchmark_cmd_area[BENCHMARK_ARGS][BENCHMARK_ARG_SIZE]; 36 + bool has_ben = false, mbm_test = true; 37 + int ben_count; 38 + 39 + for (i = 0; i < argc; i++) { 40 + if (strcmp(argv[i], "-b") == 0) { 41 + ben_ind = i + 1; 42 + ben_count = argc - ben_ind; 43 + argc_new = ben_ind - 1; 44 + has_ben = true; 45 + break; 46 + } 47 + } 48 + 49 + while ((c = getopt(argc_new, argv, "ht:b:")) != -1) { 50 + char *token; 51 + 52 + switch (c) { 53 + case 't': 54 + token = strtok(optarg, ","); 55 + 56 + mbm_test = false; 57 + while (token) { 58 + if (!strcmp(token, "mbm")) { 59 + mbm_test = true; 60 + } else { 61 + printf("invalid argument\n"); 62 + 63 + return -1; 64 + } 65 + token = strtok(NULL, ":\t"); 66 + } 67 + break; 68 + case 'p': 69 + cpu_no = atoi(optarg); 70 + break; 71 + case 'h': 72 + cmd_help(); 73 + 74 + return 0; 75 + default: 76 + printf("invalid argument\n"); 77 + 78 + return -1; 79 + } 80 + } 81 + 82 + printf("TAP version 13\n"); 83 + 84 + /* 85 + * Typically we need root privileges, because: 86 + * 1. We write to resctrl FS 87 + * 2. We execute perf commands 88 + */ 89 + if (geteuid() != 0) 90 + printf("# WARNING: not running as root, tests may fail.\n"); 91 + 92 + if (has_ben) { 93 + /* Extract benchmark command from command line. */ 94 + for (i = ben_ind; i < argc; i++) { 95 + benchmark_cmd[i - ben_ind] = benchmark_cmd_area[i]; 96 + sprintf(benchmark_cmd[i - ben_ind], "%s", argv[i]); 97 + } 98 + benchmark_cmd[ben_count] = NULL; 99 + } else { 100 + /* If no benchmark is given by "-b" argument, use fill_buf. */ 101 + for (i = 0; i < 6; i++) 102 + benchmark_cmd[i] = benchmark_cmd_area[i]; 103 + 104 + strcpy(benchmark_cmd[0], "fill_buf"); 105 + sprintf(benchmark_cmd[1], "%d", span); 106 + strcpy(benchmark_cmd[2], "1"); 107 + strcpy(benchmark_cmd[3], "1"); 108 + strcpy(benchmark_cmd[4], "0"); 109 + strcpy(benchmark_cmd[5], ""); 110 + benchmark_cmd[6] = NULL; 111 + } 112 + 113 + sprintf(bw_report, "reads"); 114 + sprintf(bm_type, "fill_buf"); 115 + 116 + check_resctrlfs_support(); 117 + filter_dmesg(); 118 + 119 + if (mbm_test) { 120 + printf("# Starting MBM BW change ...\n"); 121 + if (!has_ben) 122 + sprintf(benchmark_cmd[5], "%s", "mba"); 123 + res = mbm_bw_change(span, cpu_no, bw_report, benchmark_cmd); 124 + printf("%sok MBM: bw change\n", res ? "not " : ""); 125 + mbm_test_cleanup(); 126 + tests_run++; 127 + } 128 + 129 + printf("1..%d\n", tests_run); 130 + 131 + return 0; 132 + }
+2
tools/testing/selftests/resctrl/resctrl_val.c
··· 435 435 static void ctrlc_handler(int signum, siginfo_t *info, void *ptr) 436 436 { 437 437 kill(bm_pid, SIGKILL); 438 + umount_resctrlfs(); 439 + tests_cleanup(); 438 440 printf("Ending\n\n"); 439 441 440 442 exit(EXIT_SUCCESS);
+77
tools/testing/selftests/resctrl/resctrlfs.c
··· 388 388 return ret; 389 389 } 390 390 391 + bool check_resctrlfs_support(void) 392 + { 393 + FILE *inf = fopen("/proc/filesystems", "r"); 394 + DIR *dp; 395 + char *res; 396 + bool ret = false; 397 + 398 + if (!inf) 399 + return false; 400 + 401 + res = fgrep(inf, "nodev\tresctrl\n"); 402 + 403 + if (res) { 404 + ret = true; 405 + free(res); 406 + } 407 + 408 + fclose(inf); 409 + 410 + printf("%sok kernel supports resctrl filesystem\n", ret ? "" : "not "); 411 + tests_run++; 412 + 413 + dp = opendir(RESCTRL_PATH); 414 + printf("%sok resctrl mountpoint \"%s\" exists\n", 415 + dp ? "" : "not ", RESCTRL_PATH); 416 + if (dp) 417 + closedir(dp); 418 + tests_run++; 419 + 420 + printf("# resctrl filesystem %s mounted\n", 421 + find_resctrl_mount(NULL) ? "not" : "is"); 422 + 423 + return ret; 424 + } 425 + 391 426 char *fgrep(FILE *inf, const char *str) 392 427 { 393 428 char line[256]; ··· 466 431 fclose(inf); 467 432 468 433 return found; 434 + } 435 + 436 + int filter_dmesg(void) 437 + { 438 + char line[1024]; 439 + FILE *fp; 440 + int pipefds[2]; 441 + pid_t pid; 442 + int ret; 443 + 444 + ret = pipe(pipefds); 445 + if (ret) { 446 + perror("pipe"); 447 + return ret; 448 + } 449 + pid = fork(); 450 + if (pid == 0) { 451 + close(pipefds[0]); 452 + dup2(pipefds[1], STDOUT_FILENO); 453 + execlp("dmesg", "dmesg", NULL); 454 + perror("executing dmesg"); 455 + exit(1); 456 + } 457 + close(pipefds[1]); 458 + fp = fdopen(pipefds[0], "r"); 459 + if (!fp) { 460 + perror("fdopen(pipe)"); 461 + kill(pid, SIGTERM); 462 + 463 + return -1; 464 + } 465 + 466 + while (fgets(line, 1024, fp)) { 467 + if (strstr(line, "intel_rdt:")) 468 + printf("# dmesg: %s", line); 469 + if (strstr(line, "resctrl:")) 470 + printf("# dmesg: %s", line); 471 + } 472 + fclose(fp); 473 + waitpid(pid, NULL, 0); 474 + 475 + return 0; 469 476 } 470 477 471 478 int validate_bw_report_request(char *bw_report)