xref: /linux/tools/testing/selftests/resctrl/resctrl_val.c (revision c066a681ac2f331cc2e493cb0b4072d86c5291b5)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Memory bandwidth monitoring and allocation library
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 UNCORE_IMC		"uncore_imc"
14 #define READ_FILE_NAME		"events/cas_count_read"
15 #define DYN_PMU_PATH		"/sys/bus/event_source/devices"
16 #define SCALE			0.00006103515625
17 #define MAX_IMCS		20
18 #define MAX_TOKENS		5
19 
20 #define CON_MBM_LOCAL_BYTES_PATH		\
21 	"%s/%s/mon_data/mon_L3_%02d/mbm_local_bytes"
22 
23 struct membw_read_format {
24 	__u64 value;         /* The value of the event */
25 	__u64 time_enabled;  /* if PERF_FORMAT_TOTAL_TIME_ENABLED */
26 	__u64 time_running;  /* if PERF_FORMAT_TOTAL_TIME_RUNNING */
27 	__u64 id;            /* if PERF_FORMAT_ID */
28 };
29 
30 struct imc_counter_config {
31 	__u32 type;
32 	__u64 event;
33 	__u64 umask;
34 	struct perf_event_attr pe;
35 	int fd;
36 };
37 
38 static char mbm_total_path[1024];
39 static int imcs;
40 static struct imc_counter_config imc_counters_config[MAX_IMCS];
41 static const struct resctrl_test *current_test;
42 
43 static void read_mem_bw_initialize_perf_event_attr(int i)
44 {
45 	memset(&imc_counters_config[i].pe, 0,
46 	       sizeof(struct perf_event_attr));
47 	imc_counters_config[i].pe.type = imc_counters_config[i].type;
48 	imc_counters_config[i].pe.size = sizeof(struct perf_event_attr);
49 	imc_counters_config[i].pe.disabled = 1;
50 	imc_counters_config[i].pe.inherit = 1;
51 	imc_counters_config[i].pe.exclude_guest = 0;
52 	imc_counters_config[i].pe.config =
53 		imc_counters_config[i].umask << 8 |
54 		imc_counters_config[i].event;
55 	imc_counters_config[i].pe.sample_type = PERF_SAMPLE_IDENTIFIER;
56 	imc_counters_config[i].pe.read_format =
57 		PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING;
58 }
59 
60 static void read_mem_bw_ioctl_perf_event_ioc_reset_enable(int i)
61 {
62 	ioctl(imc_counters_config[i].fd, PERF_EVENT_IOC_RESET, 0);
63 	ioctl(imc_counters_config[i].fd, PERF_EVENT_IOC_ENABLE, 0);
64 }
65 
66 static void read_mem_bw_ioctl_perf_event_ioc_disable(int i)
67 {
68 	ioctl(imc_counters_config[i].fd, PERF_EVENT_IOC_DISABLE, 0);
69 }
70 
71 /*
72  * get_read_event_and_umask:	Parse config into event and umask
73  * @cas_count_cfg:	Config
74  * @count:		iMC number
75  */
76 static void get_read_event_and_umask(char *cas_count_cfg, int count)
77 {
78 	char *token[MAX_TOKENS];
79 	int i = 0;
80 
81 	token[0] = strtok(cas_count_cfg, "=,");
82 
83 	for (i = 1; i < MAX_TOKENS; i++)
84 		token[i] = strtok(NULL, "=,");
85 
86 	for (i = 0; i < MAX_TOKENS - 1; i++) {
87 		if (!token[i])
88 			break;
89 		if (strcmp(token[i], "event") == 0)
90 			imc_counters_config[count].event = strtol(token[i + 1], NULL, 16);
91 		if (strcmp(token[i], "umask") == 0)
92 			imc_counters_config[count].umask = strtol(token[i + 1], NULL, 16);
93 	}
94 }
95 
96 static int open_perf_read_event(int i, int cpu_no)
97 {
98 	imc_counters_config[i].fd =
99 		perf_event_open(&imc_counters_config[i].pe, -1, cpu_no, -1,
100 				PERF_FLAG_FD_CLOEXEC);
101 
102 	if (imc_counters_config[i].fd == -1) {
103 		fprintf(stderr, "Error opening leader %llx\n",
104 			imc_counters_config[i].pe.config);
105 
106 		return -1;
107 	}
108 
109 	return 0;
110 }
111 
112 /* Get type and config of an iMC counter's read event. */
113 static int read_from_imc_dir(char *imc_dir, int count)
114 {
115 	char cas_count_cfg[1024], imc_counter_cfg[1024], imc_counter_type[1024];
116 	FILE *fp;
117 
118 	/* Get type of iMC counter */
119 	sprintf(imc_counter_type, "%s%s", imc_dir, "type");
120 	fp = fopen(imc_counter_type, "r");
121 	if (!fp) {
122 		ksft_perror("Failed to open iMC counter type file");
123 
124 		return -1;
125 	}
126 	if (fscanf(fp, "%u", &imc_counters_config[count].type) <= 0) {
127 		ksft_perror("Could not get iMC type");
128 		fclose(fp);
129 
130 		return -1;
131 	}
132 	fclose(fp);
133 
134 	/* Get read config */
135 	sprintf(imc_counter_cfg, "%s%s", imc_dir, READ_FILE_NAME);
136 	fp = fopen(imc_counter_cfg, "r");
137 	if (!fp) {
138 		ksft_perror("Failed to open iMC config file");
139 
140 		return -1;
141 	}
142 	if (fscanf(fp, "%1023s", cas_count_cfg) <= 0) {
143 		ksft_perror("Could not get iMC cas count read");
144 		fclose(fp);
145 
146 		return -1;
147 	}
148 	fclose(fp);
149 
150 	get_read_event_and_umask(cas_count_cfg, count);
151 
152 	return 0;
153 }
154 
155 /*
156  * A system can have 'n' number of iMC (Integrated Memory Controller)
157  * counters, get that 'n'. Discover the properties of the available
158  * counters in support of needed performance measurement via perf.
159  * For each iMC counter get it's type and config. Also obtain each
160  * counter's event and umask for the memory read events that will be
161  * measured.
162  *
163  * Enumerate all these details into an array of structures.
164  *
165  * Return: >= 0 on success. < 0 on failure.
166  */
167 static int num_of_imcs(void)
168 {
169 	char imc_dir[512], *temp;
170 	unsigned int count = 0;
171 	struct dirent *ep;
172 	int ret;
173 	DIR *dp;
174 
175 	dp = opendir(DYN_PMU_PATH);
176 	if (dp) {
177 		while ((ep = readdir(dp))) {
178 			temp = strstr(ep->d_name, UNCORE_IMC);
179 			if (!temp)
180 				continue;
181 
182 			/*
183 			 * imc counters are named as "uncore_imc_<n>", hence
184 			 * increment the pointer to point to <n>. Note that
185 			 * sizeof(UNCORE_IMC) would count for null character as
186 			 * well and hence the last underscore character in
187 			 * uncore_imc'_' need not be counted.
188 			 */
189 			temp = temp + sizeof(UNCORE_IMC);
190 
191 			/*
192 			 * Some directories under "DYN_PMU_PATH" could have
193 			 * names like "uncore_imc_free_running", hence, check if
194 			 * first character is a numerical digit or not.
195 			 */
196 			if (temp[0] >= '0' && temp[0] <= '9') {
197 				sprintf(imc_dir, "%s/%s/", DYN_PMU_PATH,
198 					ep->d_name);
199 				ret = read_from_imc_dir(imc_dir, count);
200 				if (ret) {
201 					closedir(dp);
202 
203 					return ret;
204 				}
205 				count++;
206 			}
207 		}
208 		closedir(dp);
209 		if (count == 0) {
210 			ksft_print_msg("Unable to find iMC counters\n");
211 
212 			return -1;
213 		}
214 	} else {
215 		ksft_perror("Unable to open PMU directory");
216 
217 		return -1;
218 	}
219 
220 	return count;
221 }
222 
223 int initialize_read_mem_bw_imc(void)
224 {
225 	int imc;
226 
227 	imcs = num_of_imcs();
228 	if (imcs <= 0)
229 		return imcs;
230 
231 	/* Initialize perf_event_attr structures for all iMC's */
232 	for (imc = 0; imc < imcs; imc++)
233 		read_mem_bw_initialize_perf_event_attr(imc);
234 
235 	return 0;
236 }
237 
238 static void perf_close_imc_read_mem_bw(void)
239 {
240 	int mc;
241 
242 	for (mc = 0; mc < imcs; mc++) {
243 		if (imc_counters_config[mc].fd != -1)
244 			close(imc_counters_config[mc].fd);
245 	}
246 }
247 
248 /*
249  * perf_open_imc_read_mem_bw - Open perf fds for IMCs
250  * @cpu_no: CPU number that the benchmark PID is bound to
251  *
252  * Return: = 0 on success. < 0 on failure.
253  */
254 static int perf_open_imc_read_mem_bw(int cpu_no)
255 {
256 	int imc, ret;
257 
258 	for (imc = 0; imc < imcs; imc++)
259 		imc_counters_config[imc].fd = -1;
260 
261 	for (imc = 0; imc < imcs; imc++) {
262 		ret = open_perf_read_event(imc, cpu_no);
263 		if (ret)
264 			goto close_fds;
265 	}
266 
267 	return 0;
268 
269 close_fds:
270 	perf_close_imc_read_mem_bw();
271 	return -1;
272 }
273 
274 /*
275  * do_imc_read_mem_bw_test - Perform memory bandwidth test
276  *
277  * Runs memory bandwidth test over one second period. Also, handles starting
278  * and stopping of the IMC perf counters around the test.
279  */
280 static void do_imc_read_mem_bw_test(void)
281 {
282 	int imc;
283 
284 	for (imc = 0; imc < imcs; imc++)
285 		read_mem_bw_ioctl_perf_event_ioc_reset_enable(imc);
286 
287 	sleep(1);
288 
289 	/* Stop counters after a second to get results. */
290 	for (imc = 0; imc < imcs; imc++)
291 		read_mem_bw_ioctl_perf_event_ioc_disable(imc);
292 }
293 
294 /*
295  * get_read_mem_bw_imc - Memory read bandwidth as reported by iMC counters
296  *
297  * Memory read bandwidth utilized by a process on a socket can be calculated
298  * using iMC counters' read events. Perf events are used to read these
299  * counters.
300  *
301  * Return: = 0 on success. < 0 on failure.
302  */
303 static int get_read_mem_bw_imc(float *bw_imc)
304 {
305 	float reads = 0, of_mul_read = 1;
306 	int imc;
307 
308 	/*
309 	 * Log read event values from all iMC counters into
310 	 * struct imc_counter_config.
311 	 * Take overflow into consideration before calculating total bandwidth.
312 	 */
313 	for (imc = 0; imc < imcs; imc++) {
314 		struct membw_read_format measurement;
315 		struct imc_counter_config *r =
316 			&imc_counters_config[imc];
317 
318 		if (read(r->fd, &measurement, sizeof(measurement)) == -1) {
319 			ksft_perror("Couldn't get read bandwidth through iMC");
320 			return -1;
321 		}
322 
323 		__u64 r_time_enabled = measurement.time_enabled;
324 		__u64 r_time_running = measurement.time_running;
325 
326 		if (r_time_enabled != r_time_running)
327 			of_mul_read = (float)r_time_enabled /
328 					(float)r_time_running;
329 
330 		reads += measurement.value * of_mul_read * SCALE;
331 	}
332 
333 	*bw_imc = reads;
334 	return 0;
335 }
336 
337 /*
338  * initialize_mem_bw_resctrl:	Appropriately populate "mbm_total_path"
339  * @param:	Parameters passed to resctrl_val()
340  * @domain_id:	Domain ID (cache ID; for MB, L3 cache ID)
341  */
342 void initialize_mem_bw_resctrl(const struct resctrl_val_param *param,
343 			       int domain_id)
344 {
345 	sprintf(mbm_total_path, CON_MBM_LOCAL_BYTES_PATH, RESCTRL_PATH,
346 		param->ctrlgrp, domain_id);
347 }
348 
349 /*
350  * Open file to read MBM local bytes from resctrl FS
351  */
352 static FILE *open_mem_bw_resctrl(const char *mbm_bw_file)
353 {
354 	FILE *fp;
355 
356 	fp = fopen(mbm_bw_file, "r");
357 	if (!fp)
358 		ksft_perror("Failed to open total memory bandwidth file");
359 
360 	return fp;
361 }
362 
363 /*
364  * Get MBM Local bytes as reported by resctrl FS
365  */
366 static int get_mem_bw_resctrl(FILE *fp, unsigned long *mbm_total)
367 {
368 	if (fscanf(fp, "%lu\n", mbm_total) <= 0) {
369 		ksft_perror("Could not get MBM local bytes");
370 		return -1;
371 	}
372 	return 0;
373 }
374 
375 static pid_t bm_pid;
376 
377 void ctrlc_handler(int signum, siginfo_t *info, void *ptr)
378 {
379 	/* Only kill child after bm_pid is set after fork() */
380 	if (bm_pid)
381 		kill(bm_pid, SIGKILL);
382 	umount_resctrlfs();
383 	if (current_test && current_test->cleanup)
384 		current_test->cleanup();
385 	ksft_print_msg("Ending\n\n");
386 
387 	exit(EXIT_SUCCESS);
388 }
389 
390 /*
391  * Register CTRL-C handler for parent, as it has to kill
392  * child process before exiting.
393  */
394 int signal_handler_register(const struct resctrl_test *test)
395 {
396 	struct sigaction sigact = {};
397 	int ret = 0;
398 
399 	bm_pid = 0;
400 
401 	current_test = test;
402 	sigact.sa_sigaction = ctrlc_handler;
403 	sigemptyset(&sigact.sa_mask);
404 	sigact.sa_flags = SA_SIGINFO;
405 	if (sigaction(SIGINT, &sigact, NULL) ||
406 	    sigaction(SIGTERM, &sigact, NULL) ||
407 	    sigaction(SIGHUP, &sigact, NULL)) {
408 		ksft_perror("sigaction");
409 		ret = -1;
410 	}
411 	return ret;
412 }
413 
414 /*
415  * Reset signal handler to SIG_DFL.
416  * Non-Value return because the caller should keep
417  * the error code of other path even if sigaction fails.
418  */
419 void signal_handler_unregister(void)
420 {
421 	struct sigaction sigact = {};
422 
423 	current_test = NULL;
424 	sigact.sa_handler = SIG_DFL;
425 	sigemptyset(&sigact.sa_mask);
426 	if (sigaction(SIGINT, &sigact, NULL) ||
427 	    sigaction(SIGTERM, &sigact, NULL) ||
428 	    sigaction(SIGHUP, &sigact, NULL)) {
429 		ksft_perror("sigaction");
430 	}
431 }
432 
433 /*
434  * print_results_bw:	the memory bandwidth results are stored in a file
435  * @filename:		file that stores the results
436  * @bm_pid:		child pid that runs benchmark
437  * @bw_imc:		perf imc counter value
438  * @bw_resc:		memory bandwidth value
439  *
440  * Return:		0 on success, < 0 on error.
441  */
442 static int print_results_bw(char *filename, pid_t bm_pid, float bw_imc,
443 			    unsigned long bw_resc)
444 {
445 	unsigned long diff = fabs(bw_imc - bw_resc);
446 	FILE *fp;
447 
448 	if (strcmp(filename, "stdio") == 0 || strcmp(filename, "stderr") == 0) {
449 		printf("Pid: %d \t Mem_BW_iMC: %f \t ", (int)bm_pid, bw_imc);
450 		printf("Mem_BW_resc: %lu \t Difference: %lu\n", bw_resc, diff);
451 	} else {
452 		fp = fopen(filename, "a");
453 		if (!fp) {
454 			ksft_perror("Cannot open results file");
455 
456 			return -1;
457 		}
458 		if (fprintf(fp, "Pid: %d \t Mem_BW_iMC: %f \t Mem_BW_resc: %lu \t Difference: %lu\n",
459 			    (int)bm_pid, bw_imc, bw_resc, diff) <= 0) {
460 			ksft_print_msg("Could not log results\n");
461 			fclose(fp);
462 
463 			return -1;
464 		}
465 		fclose(fp);
466 	}
467 
468 	return 0;
469 }
470 
471 /*
472  * measure_read_mem_bw - Measures read memory bandwidth numbers while benchmark runs
473  * @uparams:		User supplied parameters
474  * @param:		Parameters passed to resctrl_val()
475  * @bm_pid:		PID that runs the benchmark
476  *
477  * Measure memory bandwidth from resctrl and from another source which is
478  * perf imc value or could be something else if perf imc event is not
479  * available. Compare the two values to validate resctrl value. It takes
480  * 1 sec to measure the data.
481  * resctrl does not distinguish between read and write operations so
482  * its data includes all memory operations.
483  */
484 int measure_read_mem_bw(const struct user_params *uparams,
485 			struct resctrl_val_param *param, pid_t bm_pid)
486 {
487 	unsigned long bw_resc, bw_resc_start, bw_resc_end;
488 	FILE *mem_bw_fp;
489 	float bw_imc;
490 	int ret;
491 
492 	mem_bw_fp = open_mem_bw_resctrl(mbm_total_path);
493 	if (!mem_bw_fp)
494 		return -1;
495 
496 	ret = perf_open_imc_read_mem_bw(uparams->cpu);
497 	if (ret < 0)
498 		goto close_fp;
499 
500 	ret = get_mem_bw_resctrl(mem_bw_fp, &bw_resc_start);
501 	if (ret < 0)
502 		goto close_imc;
503 
504 	rewind(mem_bw_fp);
505 
506 	do_imc_read_mem_bw_test();
507 
508 	ret = get_mem_bw_resctrl(mem_bw_fp, &bw_resc_end);
509 	if (ret < 0)
510 		goto close_imc;
511 
512 	ret = get_read_mem_bw_imc(&bw_imc);
513 	if (ret < 0)
514 		goto close_imc;
515 
516 	perf_close_imc_read_mem_bw();
517 	fclose(mem_bw_fp);
518 
519 	bw_resc = (bw_resc_end - bw_resc_start) / MB;
520 
521 	return print_results_bw(param->filename, bm_pid, bw_imc, bw_resc);
522 
523 close_imc:
524 	perf_close_imc_read_mem_bw();
525 close_fp:
526 	fclose(mem_bw_fp);
527 	return ret;
528 }
529 
530 /*
531  * resctrl_val:	execute benchmark and measure memory bandwidth on
532  *			the benchmark
533  * @test:		test information structure
534  * @uparams:		user supplied parameters
535  * @param:		parameters passed to resctrl_val()
536  *
537  * Return:		0 when the test was run, < 0 on error.
538  */
539 int resctrl_val(const struct resctrl_test *test,
540 		const struct user_params *uparams,
541 		struct resctrl_val_param *param)
542 {
543 	unsigned char *buf = NULL;
544 	cpu_set_t old_affinity;
545 	int domain_id;
546 	int ret = 0;
547 	pid_t ppid;
548 
549 	if (strcmp(param->filename, "") == 0)
550 		sprintf(param->filename, "stdio");
551 
552 	ret = get_domain_id(test->resource, uparams->cpu, &domain_id);
553 	if (ret < 0) {
554 		ksft_print_msg("Could not get domain ID\n");
555 		return ret;
556 	}
557 
558 	ppid = getpid();
559 
560 	/* Taskset test to specified CPU. */
561 	ret = taskset_benchmark(ppid, uparams->cpu, &old_affinity);
562 	if (ret)
563 		return ret;
564 
565 	/* Write test to specified control & monitoring group in resctrl FS. */
566 	ret = write_bm_pid_to_resctrl(ppid, param->ctrlgrp, param->mongrp);
567 	if (ret)
568 		goto reset_affinity;
569 
570 	if (param->init) {
571 		ret = param->init(test, uparams, param, domain_id);
572 		if (ret)
573 			goto reset_affinity;
574 	}
575 
576 	/*
577 	 * If not running user provided benchmark, run the default
578 	 * "fill_buf". First phase of "fill_buf" is to prepare the
579 	 * buffer that the benchmark will operate on. No measurements
580 	 * are needed during this phase and prepared memory will be
581 	 * passed to next part of benchmark via copy-on-write thus
582 	 * no impact on the benchmark that relies on reading from
583 	 * memory only.
584 	 */
585 	if (param->fill_buf) {
586 		buf = alloc_buffer(param->fill_buf->buf_size,
587 				   param->fill_buf->memflush);
588 		if (!buf) {
589 			ret = -ENOMEM;
590 			goto reset_affinity;
591 		}
592 	}
593 
594 	fflush(stdout);
595 	bm_pid = fork();
596 	if (bm_pid == -1) {
597 		ret = -errno;
598 		ksft_perror("Unable to fork");
599 		goto free_buf;
600 	}
601 
602 	/*
603 	 * What needs to be measured runs in separate process until
604 	 * terminated.
605 	 */
606 	if (bm_pid == 0) {
607 		if (param->fill_buf)
608 			fill_cache_read(buf, param->fill_buf->buf_size, false);
609 		else if (uparams->benchmark_cmd[0])
610 			execvp(uparams->benchmark_cmd[0], (char **)uparams->benchmark_cmd);
611 		exit(EXIT_SUCCESS);
612 	}
613 
614 	ksft_print_msg("Benchmark PID: %d\n", (int)bm_pid);
615 
616 	/* Give benchmark enough time to fully run. */
617 	sleep(1);
618 
619 	/* Test runs until the callback setup() tells the test to stop. */
620 	while (1) {
621 		ret = param->setup(test, uparams, param);
622 		if (ret == END_OF_TESTS) {
623 			ret = 0;
624 			break;
625 		}
626 		if (ret < 0)
627 			break;
628 
629 		ret = param->measure(uparams, param, bm_pid);
630 		if (ret)
631 			break;
632 	}
633 
634 	kill(bm_pid, SIGKILL);
635 free_buf:
636 	free(buf);
637 reset_affinity:
638 	taskset_restore(ppid, &old_affinity);
639 	return ret;
640 }
641