1ecdbb911SFenghua Yu // SPDX-License-Identifier: GPL-2.0
2ecdbb911SFenghua Yu /*
3ecdbb911SFenghua Yu * Resctrl tests
4ecdbb911SFenghua Yu *
5ecdbb911SFenghua Yu * Copyright (C) 2018 Intel Corporation
6ecdbb911SFenghua Yu *
7ecdbb911SFenghua Yu * Authors:
8ecdbb911SFenghua Yu * Sai Praneeth Prakhya <[email protected]>,
9ecdbb911SFenghua Yu * Fenghua Yu <[email protected]>
10ecdbb911SFenghua Yu */
11ecdbb911SFenghua Yu #include "resctrl.h"
12ecdbb911SFenghua Yu
1390a009dbSIlpo Järvinen /* Volatile memory sink to prevent compiler optimizations */
1490a009dbSIlpo Järvinen static volatile int sink_target;
1590a009dbSIlpo Järvinen volatile int *value_sink = &sink_target;
1690a009dbSIlpo Järvinen
17c603ff5bSIlpo Järvinen static struct resctrl_test *resctrl_tests[] = {
18c603ff5bSIlpo Järvinen &mbm_test,
19c603ff5bSIlpo Järvinen &mba_test,
20c603ff5bSIlpo Järvinen &cmt_test,
21c603ff5bSIlpo Järvinen &l3_cat_test,
22ae638551SMaciej Wieczor-Retman &l3_noncont_cat_test,
23ae638551SMaciej Wieczor-Retman &l2_noncont_cat_test,
24c603ff5bSIlpo Järvinen };
25c603ff5bSIlpo Järvinen
detect_vendor(void)266220f69eSShaopeng Tan static int detect_vendor(void)
2753f74fbeSBabu Moger {
2853f74fbeSBabu Moger FILE *inf = fopen("/proc/cpuinfo", "r");
296220f69eSShaopeng Tan int vendor_id = 0;
306220f69eSShaopeng Tan char *s = NULL;
3153f74fbeSBabu Moger char *res;
3253f74fbeSBabu Moger
3353f74fbeSBabu Moger if (!inf)
346220f69eSShaopeng Tan return vendor_id;
3553f74fbeSBabu Moger
3653f74fbeSBabu Moger res = fgrep(inf, "vendor_id");
3753f74fbeSBabu Moger
386220f69eSShaopeng Tan if (res)
396220f69eSShaopeng Tan s = strchr(res, ':');
4053f74fbeSBabu Moger
416220f69eSShaopeng Tan if (s && !strcmp(s, ": GenuineIntel\n"))
426220f69eSShaopeng Tan vendor_id = ARCH_INTEL;
436220f69eSShaopeng Tan else if (s && !strcmp(s, ": AuthenticAMD\n"))
446220f69eSShaopeng Tan vendor_id = ARCH_AMD;
456220f69eSShaopeng Tan
4653f74fbeSBabu Moger fclose(inf);
476220f69eSShaopeng Tan free(res);
486220f69eSShaopeng Tan return vendor_id;
496220f69eSShaopeng Tan }
506220f69eSShaopeng Tan
get_vendor(void)516220f69eSShaopeng Tan int get_vendor(void)
526220f69eSShaopeng Tan {
536220f69eSShaopeng Tan static int vendor = -1;
546220f69eSShaopeng Tan
556220f69eSShaopeng Tan if (vendor == -1)
566220f69eSShaopeng Tan vendor = detect_vendor();
576220f69eSShaopeng Tan if (vendor == 0)
586220f69eSShaopeng Tan ksft_print_msg("Can not get vendor info...\n");
596220f69eSShaopeng Tan
606220f69eSShaopeng Tan return vendor;
6153f74fbeSBabu Moger }
6253f74fbeSBabu Moger
cmd_help(void)63ecdbb911SFenghua Yu static void cmd_help(void)
64ecdbb911SFenghua Yu {
65c603ff5bSIlpo Järvinen int i;
66c603ff5bSIlpo Järvinen
675eb6360eSIlpo Järvinen printf("usage: resctrl_tests [-h] [-t test list] [-n no_of_bits] [-b benchmark_cmd [option]...]\n");
685eb6360eSIlpo Järvinen printf("\t-b benchmark_cmd [option]...: run specified benchmark for MBM, MBA and CMT\n");
69ecdbb911SFenghua Yu printf("\t default benchmark is builtin fill_buf\n");
705339792bSIlpo Järvinen printf("\t-t test list: run tests/groups specified by the list, ");
712f320911SFenghua Yu printf("e.g. -t mbm,mba,cmt,cat\n");
725339792bSIlpo Järvinen printf("\t\tSupported tests (group):\n");
735339792bSIlpo Järvinen for (i = 0; i < ARRAY_SIZE(resctrl_tests); i++) {
745339792bSIlpo Järvinen if (resctrl_tests[i]->group)
755339792bSIlpo Järvinen printf("\t\t\t%s (%s)\n", resctrl_tests[i]->name, resctrl_tests[i]->group);
765339792bSIlpo Järvinen else
77c603ff5bSIlpo Järvinen printf("\t\t\t%s\n", resctrl_tests[i]->name);
785339792bSIlpo Järvinen }
7978941183SFenghua Yu printf("\t-n no_of_bits: run cache tests using specified no of bits in cache bit mask\n");
8078941183SFenghua Yu printf("\t-p cpu_no: specify CPU number to run the test. 1 is default\n");
81ecdbb911SFenghua Yu printf("\t-h: help\n");
82ecdbb911SFenghua Yu }
83ecdbb911SFenghua Yu
test_prepare(const struct resctrl_test * test)84e6487230SMaciej Wieczor-Retman static int test_prepare(const struct resctrl_test *test)
853aff5146SIlpo Järvinen {
863aff5146SIlpo Järvinen int res;
873aff5146SIlpo Järvinen
88e6487230SMaciej Wieczor-Retman res = signal_handler_register(test);
893aff5146SIlpo Järvinen if (res) {
903aff5146SIlpo Järvinen ksft_print_msg("Failed to register signal handler\n");
913aff5146SIlpo Järvinen return res;
923aff5146SIlpo Järvinen }
933aff5146SIlpo Järvinen
943aff5146SIlpo Järvinen res = mount_resctrlfs();
953aff5146SIlpo Järvinen if (res) {
963aff5146SIlpo Järvinen signal_handler_unregister();
973aff5146SIlpo Järvinen ksft_print_msg("Failed to mount resctrl FS\n");
983aff5146SIlpo Järvinen return res;
993aff5146SIlpo Järvinen }
1003aff5146SIlpo Järvinen return 0;
1013aff5146SIlpo Järvinen }
1023aff5146SIlpo Järvinen
test_cleanup(const struct resctrl_test * test)1036cd36898SMaciej Wieczor-Retman static void test_cleanup(const struct resctrl_test *test)
1043aff5146SIlpo Järvinen {
1056cd36898SMaciej Wieczor-Retman if (test->cleanup)
1066cd36898SMaciej Wieczor-Retman test->cleanup();
1073aff5146SIlpo Järvinen umount_resctrlfs();
1083aff5146SIlpo Järvinen signal_handler_unregister();
1093aff5146SIlpo Järvinen }
1103aff5146SIlpo Järvinen
test_vendor_specific_check(const struct resctrl_test * test)111c603ff5bSIlpo Järvinen static bool test_vendor_specific_check(const struct resctrl_test *test)
112c9fb4e7cSFenghua Yu {
113c603ff5bSIlpo Järvinen if (!test->vendor_specific)
114c603ff5bSIlpo Järvinen return true;
115c9fb4e7cSFenghua Yu
116c603ff5bSIlpo Järvinen return get_vendor() & test->vendor_specific;
117c603ff5bSIlpo Järvinen }
118c603ff5bSIlpo Järvinen
run_single_test(const struct resctrl_test * test,const struct user_params * uparams)119c603ff5bSIlpo Järvinen static void run_single_test(const struct resctrl_test *test, const struct user_params *uparams)
120c603ff5bSIlpo Järvinen {
121*a1cd99e7SMaciej Wieczor-Retman int ret, snc_mode;
122c603ff5bSIlpo Järvinen
123c603ff5bSIlpo Järvinen if (test->disabled)
124c603ff5bSIlpo Järvinen return;
125c603ff5bSIlpo Järvinen
126c603ff5bSIlpo Järvinen if (!test_vendor_specific_check(test)) {
127c603ff5bSIlpo Järvinen ksft_test_result_skip("Hardware does not support %s\n", test->name);
128c603ff5bSIlpo Järvinen return;
129c603ff5bSIlpo Järvinen }
130c603ff5bSIlpo Järvinen
131*a1cd99e7SMaciej Wieczor-Retman snc_mode = snc_nodes_per_l3_cache();
132*a1cd99e7SMaciej Wieczor-Retman
133c603ff5bSIlpo Järvinen ksft_print_msg("Starting %s test ...\n", test->name);
134f1dd7198SFenghua Yu
135*a1cd99e7SMaciej Wieczor-Retman if (snc_mode == 1 && snc_unreliable && get_vendor() == ARCH_INTEL) {
136*a1cd99e7SMaciej Wieczor-Retman ksft_test_result_skip("SNC detection unreliable due to offline CPUs. Test results may not be accurate if SNC enabled.\n");
137*a1cd99e7SMaciej Wieczor-Retman return;
138*a1cd99e7SMaciej Wieczor-Retman }
139*a1cd99e7SMaciej Wieczor-Retman
140e6487230SMaciej Wieczor-Retman if (test_prepare(test)) {
1413aff5146SIlpo Järvinen ksft_exit_fail_msg("Abnormal failure when preparing for the test\n");
142caddc0fbSIlpo Järvinen return;
143caddc0fbSIlpo Järvinen }
144caddc0fbSIlpo Järvinen
145c603ff5bSIlpo Järvinen if (!test->feature_check(test)) {
146c603ff5bSIlpo Järvinen ksft_test_result_skip("Hardware does not support %s or %s is disabled\n",
147c603ff5bSIlpo Järvinen test->name, test->name);
1483aff5146SIlpo Järvinen goto cleanup;
149f1dd7198SFenghua Yu }
150f1dd7198SFenghua Yu
151c603ff5bSIlpo Järvinen ret = test->run_test(test, uparams);
152c603ff5bSIlpo Järvinen ksft_test_result(!ret, "%s: test\n", test->name);
153caddc0fbSIlpo Järvinen
1543aff5146SIlpo Järvinen cleanup:
1556cd36898SMaciej Wieczor-Retman test_cleanup(test);
156c9fb4e7cSFenghua Yu }
157c9fb4e7cSFenghua Yu
158e958c21eSReinette Chatre /*
159e958c21eSReinette Chatre * Allocate and initialize a struct fill_buf_param with user provided
160e958c21eSReinette Chatre * (via "-b fill_buf <fill_buf parameters>") parameters.
161e958c21eSReinette Chatre *
162e958c21eSReinette Chatre * Use defaults (that may not be appropriate for all tests) for any
163e958c21eSReinette Chatre * fill_buf parameters omitted by the user.
164e958c21eSReinette Chatre *
165e958c21eSReinette Chatre * Historically it may have been possible for user space to provide
166e958c21eSReinette Chatre * additional parameters, "operation" ("read" vs "write") in
167e958c21eSReinette Chatre * benchmark_cmd[3] and "once" (run "once" or until terminated) in
168e958c21eSReinette Chatre * benchmark_cmd[4]. Changing these parameters have never been
169e958c21eSReinette Chatre * supported with the default of "read" operation and running until
170e958c21eSReinette Chatre * terminated built into the tests. Any unsupported values for
171e958c21eSReinette Chatre * (original) "fill_buf" parameters are treated as failure.
172e958c21eSReinette Chatre *
173e958c21eSReinette Chatre * Return: On failure, forcibly exits the test on any parsing failure,
174e958c21eSReinette Chatre * returns NULL if no parsing needed (user did not actually provide
175e958c21eSReinette Chatre * "-b fill_buf").
176e958c21eSReinette Chatre * On success, returns pointer to newly allocated and fully
177e958c21eSReinette Chatre * initialized struct fill_buf_param that caller must free.
178e958c21eSReinette Chatre */
alloc_fill_buf_param(struct user_params * uparams)179e958c21eSReinette Chatre static struct fill_buf_param *alloc_fill_buf_param(struct user_params *uparams)
180e958c21eSReinette Chatre {
181e958c21eSReinette Chatre struct fill_buf_param *fill_param = NULL;
182e958c21eSReinette Chatre char *endptr = NULL;
183e958c21eSReinette Chatre
184e958c21eSReinette Chatre if (!uparams->benchmark_cmd[0] || strcmp(uparams->benchmark_cmd[0], "fill_buf"))
185e958c21eSReinette Chatre return NULL;
186e958c21eSReinette Chatre
187e958c21eSReinette Chatre fill_param = malloc(sizeof(*fill_param));
188e958c21eSReinette Chatre if (!fill_param)
189e958c21eSReinette Chatre ksft_exit_skip("Unable to allocate memory for fill_buf parameters.\n");
190e958c21eSReinette Chatre
191e958c21eSReinette Chatre if (uparams->benchmark_cmd[1] && *uparams->benchmark_cmd[1] != '\0') {
192e958c21eSReinette Chatre errno = 0;
193e958c21eSReinette Chatre fill_param->buf_size = strtoul(uparams->benchmark_cmd[1], &endptr, 10);
194e958c21eSReinette Chatre if (errno || *endptr != '\0') {
195e958c21eSReinette Chatre free(fill_param);
196e958c21eSReinette Chatre ksft_exit_skip("Unable to parse benchmark buffer size.\n");
197e958c21eSReinette Chatre }
198e958c21eSReinette Chatre } else {
199f77b9672SReinette Chatre fill_param->buf_size = MINIMUM_SPAN;
200e958c21eSReinette Chatre }
201e958c21eSReinette Chatre
202e958c21eSReinette Chatre if (uparams->benchmark_cmd[2] && *uparams->benchmark_cmd[2] != '\0') {
203e958c21eSReinette Chatre errno = 0;
204e958c21eSReinette Chatre fill_param->memflush = strtol(uparams->benchmark_cmd[2], &endptr, 10) != 0;
205e958c21eSReinette Chatre if (errno || *endptr != '\0') {
206e958c21eSReinette Chatre free(fill_param);
207e958c21eSReinette Chatre ksft_exit_skip("Unable to parse benchmark memflush parameter.\n");
208e958c21eSReinette Chatre }
209e958c21eSReinette Chatre } else {
210e958c21eSReinette Chatre fill_param->memflush = true;
211e958c21eSReinette Chatre }
212e958c21eSReinette Chatre
213e958c21eSReinette Chatre if (uparams->benchmark_cmd[3] && *uparams->benchmark_cmd[3] != '\0') {
214e958c21eSReinette Chatre if (strcmp(uparams->benchmark_cmd[3], "0")) {
215e958c21eSReinette Chatre free(fill_param);
216e958c21eSReinette Chatre ksft_exit_skip("Only read operations supported.\n");
217e958c21eSReinette Chatre }
218e958c21eSReinette Chatre }
219e958c21eSReinette Chatre
220e958c21eSReinette Chatre if (uparams->benchmark_cmd[4] && *uparams->benchmark_cmd[4] != '\0') {
221e958c21eSReinette Chatre if (strcmp(uparams->benchmark_cmd[4], "false")) {
222e958c21eSReinette Chatre free(fill_param);
223e958c21eSReinette Chatre ksft_exit_skip("fill_buf is required to run until termination.\n");
224e958c21eSReinette Chatre }
225e958c21eSReinette Chatre }
226e958c21eSReinette Chatre
227e958c21eSReinette Chatre return fill_param;
228e958c21eSReinette Chatre }
229e958c21eSReinette Chatre
init_user_params(struct user_params * uparams)23015f29882SIlpo Järvinen static void init_user_params(struct user_params *uparams)
23115f29882SIlpo Järvinen {
23215f29882SIlpo Järvinen memset(uparams, 0, sizeof(*uparams));
23315f29882SIlpo Järvinen
23415f29882SIlpo Järvinen uparams->cpu = 1;
23515f29882SIlpo Järvinen uparams->bits = 0;
23615f29882SIlpo Järvinen }
23715f29882SIlpo Järvinen
main(int argc,char ** argv)238ecdbb911SFenghua Yu int main(int argc, char **argv)
239ecdbb911SFenghua Yu {
240e958c21eSReinette Chatre struct fill_buf_param *fill_param = NULL;
241c603ff5bSIlpo Järvinen int tests = ARRAY_SIZE(resctrl_tests);
242c603ff5bSIlpo Järvinen bool test_param_seen = false;
24315f29882SIlpo Järvinen struct user_params uparams;
244e958c21eSReinette Chatre int c, i;
24515f29882SIlpo Järvinen
24615f29882SIlpo Järvinen init_user_params(&uparams);
247ecdbb911SFenghua Yu
248f23c7925SIlpo Järvinen while ((c = getopt(argc, argv, "ht:b:n:p:")) != -1) {
249ecdbb911SFenghua Yu char *token;
250ecdbb911SFenghua Yu
251ecdbb911SFenghua Yu switch (c) {
252f23c7925SIlpo Järvinen case 'b':
253f23c7925SIlpo Järvinen /*
254f23c7925SIlpo Järvinen * First move optind back to the (first) optarg and
255f23c7925SIlpo Järvinen * then build the benchmark command using the
256f23c7925SIlpo Järvinen * remaining arguments.
257f23c7925SIlpo Järvinen */
258f23c7925SIlpo Järvinen optind--;
259f23c7925SIlpo Järvinen if (argc - optind >= BENCHMARK_ARGS)
260f23c7925SIlpo Järvinen ksft_exit_fail_msg("Too long benchmark command");
261f23c7925SIlpo Järvinen
262f23c7925SIlpo Järvinen /* Extract benchmark command from command line. */
263f23c7925SIlpo Järvinen for (i = 0; i < argc - optind; i++)
26415f29882SIlpo Järvinen uparams.benchmark_cmd[i] = argv[i + optind];
26515f29882SIlpo Järvinen uparams.benchmark_cmd[i] = NULL;
266f23c7925SIlpo Järvinen
267f23c7925SIlpo Järvinen goto last_arg;
268ecdbb911SFenghua Yu case 't':
269ecdbb911SFenghua Yu token = strtok(optarg, ",");
270ecdbb911SFenghua Yu
271c603ff5bSIlpo Järvinen if (!test_param_seen) {
272c603ff5bSIlpo Järvinen for (i = 0; i < ARRAY_SIZE(resctrl_tests); i++)
273c603ff5bSIlpo Järvinen resctrl_tests[i]->disabled = true;
274c603ff5bSIlpo Järvinen tests = 0;
275c603ff5bSIlpo Järvinen test_param_seen = true;
276c603ff5bSIlpo Järvinen }
277ecdbb911SFenghua Yu while (token) {
278c603ff5bSIlpo Järvinen bool found = false;
279c603ff5bSIlpo Järvinen
280c603ff5bSIlpo Järvinen for (i = 0; i < ARRAY_SIZE(resctrl_tests); i++) {
2815339792bSIlpo Järvinen if (!strcasecmp(token, resctrl_tests[i]->name) ||
2825339792bSIlpo Järvinen (resctrl_tests[i]->group &&
2835339792bSIlpo Järvinen !strcasecmp(token, resctrl_tests[i]->group))) {
284c603ff5bSIlpo Järvinen if (resctrl_tests[i]->disabled)
285ca2f4214SFenghua Yu tests++;
286c603ff5bSIlpo Järvinen resctrl_tests[i]->disabled = false;
287c603ff5bSIlpo Järvinen found = true;
288c603ff5bSIlpo Järvinen }
289c603ff5bSIlpo Järvinen }
290c603ff5bSIlpo Järvinen
291c603ff5bSIlpo Järvinen if (!found) {
292c603ff5bSIlpo Järvinen printf("invalid test: %s\n", token);
293ecdbb911SFenghua Yu
294ecdbb911SFenghua Yu return -1;
295ecdbb911SFenghua Yu }
2961421ec68SXiaochen Shen token = strtok(NULL, ",");
297ecdbb911SFenghua Yu }
298ecdbb911SFenghua Yu break;
299ecdbb911SFenghua Yu case 'p':
30015f29882SIlpo Järvinen uparams.cpu = atoi(optarg);
301ecdbb911SFenghua Yu break;
30278941183SFenghua Yu case 'n':
30315f29882SIlpo Järvinen uparams.bits = atoi(optarg);
30415f29882SIlpo Järvinen if (uparams.bits <= 0) {
30509a67934SFenghua Yu printf("Bail out! invalid argument for no_of_bits\n");
30609a67934SFenghua Yu return -1;
30709a67934SFenghua Yu }
30878941183SFenghua Yu break;
309ecdbb911SFenghua Yu case 'h':
310ecdbb911SFenghua Yu cmd_help();
311ecdbb911SFenghua Yu
312ecdbb911SFenghua Yu return 0;
313ecdbb911SFenghua Yu default:
314ecdbb911SFenghua Yu printf("invalid argument\n");
315ecdbb911SFenghua Yu
316ecdbb911SFenghua Yu return -1;
317ecdbb911SFenghua Yu }
318ecdbb911SFenghua Yu }
319f23c7925SIlpo Järvinen last_arg:
320ecdbb911SFenghua Yu
321e958c21eSReinette Chatre fill_param = alloc_fill_buf_param(&uparams);
322e958c21eSReinette Chatre if (fill_param)
323e958c21eSReinette Chatre uparams.fill_buf = fill_param;
324e958c21eSReinette Chatre
325ca2f4214SFenghua Yu ksft_print_header();
326ecdbb911SFenghua Yu
327ecdbb911SFenghua Yu /*
328ecdbb911SFenghua Yu * Typically we need root privileges, because:
329ecdbb911SFenghua Yu * 1. We write to resctrl FS
330ecdbb911SFenghua Yu * 2. We execute perf commands
331ecdbb911SFenghua Yu */
332ecdbb911SFenghua Yu if (geteuid() != 0)
33347b59f36SNathan Chancellor ksft_exit_skip("Not running as root. Skipping...\n");
334ecdbb911SFenghua Yu
33547809eb7SIlpo Järvinen if (!check_resctrlfs_support())
33647b59f36SNathan Chancellor ksft_exit_skip("resctrl FS does not exist. Enable X86_CPU_RESCTRL config option.\n");
33747809eb7SIlpo Järvinen
33847809eb7SIlpo Järvinen if (umount_resctrlfs())
33947b59f36SNathan Chancellor ksft_exit_skip("resctrl FS unmount failed.\n");
34047809eb7SIlpo Järvinen
34147809eb7SIlpo Järvinen filter_dmesg();
34247809eb7SIlpo Järvinen
343c603ff5bSIlpo Järvinen ksft_set_plan(tests);
344ca2f4214SFenghua Yu
345c603ff5bSIlpo Järvinen for (i = 0; i < ARRAY_SIZE(resctrl_tests); i++)
346c603ff5bSIlpo Järvinen run_single_test(resctrl_tests[i], &uparams);
347790bf585SFenghua Yu
348e958c21eSReinette Chatre free(fill_param);
349c2b17907SPeter Newman ksft_finished();
350ecdbb911SFenghua Yu }
351