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