178941183SFenghua Yu // SPDX-License-Identifier: GPL-2.0
278941183SFenghua Yu 
378941183SFenghua Yu #include <stdint.h>
478941183SFenghua Yu #include "resctrl.h"
578941183SFenghua Yu 
678941183SFenghua Yu struct read_format {
778941183SFenghua Yu 	__u64 nr;			/* The number of events */
878941183SFenghua Yu 	struct {
978941183SFenghua Yu 		__u64 value;		/* The value of the event */
1078941183SFenghua Yu 	} values[2];
1178941183SFenghua Yu };
1278941183SFenghua Yu 
13790bf585SFenghua Yu static struct perf_event_attr pea_llc_miss;
14790bf585SFenghua Yu static struct read_format rf_cqm;
15790bf585SFenghua Yu static int fd_lm;
1678941183SFenghua Yu char llc_occup_path[1024];
1778941183SFenghua Yu 
18790bf585SFenghua Yu static void initialize_perf_event_attr(void)
19790bf585SFenghua Yu {
20790bf585SFenghua Yu 	pea_llc_miss.type = PERF_TYPE_HARDWARE;
21790bf585SFenghua Yu 	pea_llc_miss.size = sizeof(struct perf_event_attr);
22790bf585SFenghua Yu 	pea_llc_miss.read_format = PERF_FORMAT_GROUP;
23790bf585SFenghua Yu 	pea_llc_miss.exclude_kernel = 1;
24790bf585SFenghua Yu 	pea_llc_miss.exclude_hv = 1;
25790bf585SFenghua Yu 	pea_llc_miss.exclude_idle = 1;
26790bf585SFenghua Yu 	pea_llc_miss.exclude_callchain_kernel = 1;
27790bf585SFenghua Yu 	pea_llc_miss.inherit = 1;
28790bf585SFenghua Yu 	pea_llc_miss.exclude_guest = 1;
29790bf585SFenghua Yu 	pea_llc_miss.disabled = 1;
30790bf585SFenghua Yu }
31790bf585SFenghua Yu 
32790bf585SFenghua Yu static void ioctl_perf_event_ioc_reset_enable(void)
33790bf585SFenghua Yu {
34790bf585SFenghua Yu 	ioctl(fd_lm, PERF_EVENT_IOC_RESET, 0);
35790bf585SFenghua Yu 	ioctl(fd_lm, PERF_EVENT_IOC_ENABLE, 0);
36790bf585SFenghua Yu }
37790bf585SFenghua Yu 
38790bf585SFenghua Yu static int perf_event_open_llc_miss(pid_t pid, int cpu_no)
39790bf585SFenghua Yu {
40790bf585SFenghua Yu 	fd_lm = perf_event_open(&pea_llc_miss, pid, cpu_no, -1,
41790bf585SFenghua Yu 				PERF_FLAG_FD_CLOEXEC);
42790bf585SFenghua Yu 	if (fd_lm == -1) {
43790bf585SFenghua Yu 		perror("Error opening leader");
44790bf585SFenghua Yu 		ctrlc_handler(0, NULL, NULL);
45790bf585SFenghua Yu 		return -1;
46790bf585SFenghua Yu 	}
47790bf585SFenghua Yu 
48790bf585SFenghua Yu 	return 0;
49790bf585SFenghua Yu }
50790bf585SFenghua Yu 
51790bf585SFenghua Yu static int initialize_llc_perf(void)
52790bf585SFenghua Yu {
53790bf585SFenghua Yu 	memset(&pea_llc_miss, 0, sizeof(struct perf_event_attr));
54790bf585SFenghua Yu 	memset(&rf_cqm, 0, sizeof(struct read_format));
55790bf585SFenghua Yu 
56790bf585SFenghua Yu 	/* Initialize perf_event_attr structures for HW_CACHE_MISSES */
57790bf585SFenghua Yu 	initialize_perf_event_attr();
58790bf585SFenghua Yu 
59790bf585SFenghua Yu 	pea_llc_miss.config = PERF_COUNT_HW_CACHE_MISSES;
60790bf585SFenghua Yu 
61790bf585SFenghua Yu 	rf_cqm.nr = 1;
62790bf585SFenghua Yu 
63790bf585SFenghua Yu 	return 0;
64790bf585SFenghua Yu }
65790bf585SFenghua Yu 
66790bf585SFenghua Yu static int reset_enable_llc_perf(pid_t pid, int cpu_no)
67790bf585SFenghua Yu {
68790bf585SFenghua Yu 	int ret = 0;
69790bf585SFenghua Yu 
70790bf585SFenghua Yu 	ret = perf_event_open_llc_miss(pid, cpu_no);
71790bf585SFenghua Yu 	if (ret < 0)
72790bf585SFenghua Yu 		return ret;
73790bf585SFenghua Yu 
74790bf585SFenghua Yu 	/* Start counters to log values */
75790bf585SFenghua Yu 	ioctl_perf_event_ioc_reset_enable();
76790bf585SFenghua Yu 
77790bf585SFenghua Yu 	return 0;
78790bf585SFenghua Yu }
79790bf585SFenghua Yu 
80790bf585SFenghua Yu /*
81790bf585SFenghua Yu  * get_llc_perf:	llc cache miss through perf events
82790bf585SFenghua Yu  * @cpu_no:		CPU number that the benchmark PID is binded to
83790bf585SFenghua Yu  *
84790bf585SFenghua Yu  * Perf events like HW_CACHE_MISSES could be used to validate number of
85790bf585SFenghua Yu  * cache lines allocated.
86790bf585SFenghua Yu  *
87790bf585SFenghua Yu  * Return: =0 on success.  <0 on failure.
88790bf585SFenghua Yu  */
89790bf585SFenghua Yu static int get_llc_perf(unsigned long *llc_perf_miss)
90790bf585SFenghua Yu {
91790bf585SFenghua Yu 	__u64 total_misses;
92790bf585SFenghua Yu 
93790bf585SFenghua Yu 	/* Stop counters after one span to get miss rate */
94790bf585SFenghua Yu 
95790bf585SFenghua Yu 	ioctl(fd_lm, PERF_EVENT_IOC_DISABLE, 0);
96790bf585SFenghua Yu 
97790bf585SFenghua Yu 	if (read(fd_lm, &rf_cqm, sizeof(struct read_format)) == -1) {
98790bf585SFenghua Yu 		perror("Could not get llc misses through perf");
99790bf585SFenghua Yu 
100790bf585SFenghua Yu 		return -1;
101790bf585SFenghua Yu 	}
102790bf585SFenghua Yu 
103790bf585SFenghua Yu 	total_misses = rf_cqm.values[0].value;
104790bf585SFenghua Yu 
105790bf585SFenghua Yu 	close(fd_lm);
106790bf585SFenghua Yu 
107790bf585SFenghua Yu 	*llc_perf_miss = total_misses;
108790bf585SFenghua Yu 
109790bf585SFenghua Yu 	return 0;
110790bf585SFenghua Yu }
111790bf585SFenghua Yu 
11278941183SFenghua Yu /*
11378941183SFenghua Yu  * Get LLC Occupancy as reported by RESCTRL FS
1142f320911SFenghua Yu  * For CMT,
11578941183SFenghua Yu  * 1. If con_mon grp and mon grp given, then read from mon grp in
11678941183SFenghua Yu  * con_mon grp
11778941183SFenghua Yu  * 2. If only con_mon grp given, then read from con_mon grp
11878941183SFenghua Yu  * 3. If both not given, then read from root con_mon grp
11978941183SFenghua Yu  * For CAT,
12078941183SFenghua Yu  * 1. If con_mon grp given, then read from it
12178941183SFenghua Yu  * 2. If con_mon grp not given, then read from root con_mon grp
12278941183SFenghua Yu  *
12378941183SFenghua Yu  * Return: =0 on success.  <0 on failure.
12478941183SFenghua Yu  */
12578941183SFenghua Yu static int get_llc_occu_resctrl(unsigned long *llc_occupancy)
12678941183SFenghua Yu {
12778941183SFenghua Yu 	FILE *fp;
12878941183SFenghua Yu 
12978941183SFenghua Yu 	fp = fopen(llc_occup_path, "r");
13078941183SFenghua Yu 	if (!fp) {
13178941183SFenghua Yu 		perror("Failed to open results file");
13278941183SFenghua Yu 
13378941183SFenghua Yu 		return errno;
13478941183SFenghua Yu 	}
13578941183SFenghua Yu 	if (fscanf(fp, "%lu", llc_occupancy) <= 0) {
13678941183SFenghua Yu 		perror("Could not get llc occupancy");
13778941183SFenghua Yu 		fclose(fp);
13878941183SFenghua Yu 
13978941183SFenghua Yu 		return -1;
14078941183SFenghua Yu 	}
14178941183SFenghua Yu 	fclose(fp);
14278941183SFenghua Yu 
14378941183SFenghua Yu 	return 0;
14478941183SFenghua Yu }
14578941183SFenghua Yu 
14678941183SFenghua Yu /*
14778941183SFenghua Yu  * print_results_cache:	the cache results are stored in a file
14878941183SFenghua Yu  * @filename:		file that stores the results
14978941183SFenghua Yu  * @bm_pid:		child pid that runs benchmark
15078941183SFenghua Yu  * @llc_value:		perf miss value /
15178941183SFenghua Yu  *			llc occupancy value reported by resctrl FS
15278941183SFenghua Yu  *
15378941183SFenghua Yu  * Return:		0 on success. non-zero on failure.
15478941183SFenghua Yu  */
15578941183SFenghua Yu static int print_results_cache(char *filename, int bm_pid,
15678941183SFenghua Yu 			       unsigned long llc_value)
15778941183SFenghua Yu {
15878941183SFenghua Yu 	FILE *fp;
15978941183SFenghua Yu 
16078941183SFenghua Yu 	if (strcmp(filename, "stdio") == 0 || strcmp(filename, "stderr") == 0) {
16178941183SFenghua Yu 		printf("Pid: %d \t LLC_value: %lu\n", bm_pid,
16278941183SFenghua Yu 		       llc_value);
16378941183SFenghua Yu 	} else {
16478941183SFenghua Yu 		fp = fopen(filename, "a");
16578941183SFenghua Yu 		if (!fp) {
16678941183SFenghua Yu 			perror("Cannot open results file");
16778941183SFenghua Yu 
16878941183SFenghua Yu 			return errno;
16978941183SFenghua Yu 		}
17078941183SFenghua Yu 		fprintf(fp, "Pid: %d \t llc_value: %lu\n", bm_pid, llc_value);
17178941183SFenghua Yu 		fclose(fp);
17278941183SFenghua Yu 	}
17378941183SFenghua Yu 
17478941183SFenghua Yu 	return 0;
17578941183SFenghua Yu }
17678941183SFenghua Yu 
17778941183SFenghua Yu int measure_cache_vals(struct resctrl_val_param *param, int bm_pid)
17878941183SFenghua Yu {
179790bf585SFenghua Yu 	unsigned long llc_perf_miss = 0, llc_occu_resc = 0, llc_value = 0;
18078941183SFenghua Yu 	int ret;
18178941183SFenghua Yu 
18278941183SFenghua Yu 	/*
183790bf585SFenghua Yu 	 * Measure cache miss from perf.
184790bf585SFenghua Yu 	 */
18524286736SFenghua Yu 	if (!strncmp(param->resctrl_val, CAT_STR, sizeof(CAT_STR))) {
186790bf585SFenghua Yu 		ret = get_llc_perf(&llc_perf_miss);
187790bf585SFenghua Yu 		if (ret < 0)
188790bf585SFenghua Yu 			return ret;
189790bf585SFenghua Yu 		llc_value = llc_perf_miss;
190790bf585SFenghua Yu 	}
191790bf585SFenghua Yu 
192790bf585SFenghua Yu 	/*
19378941183SFenghua Yu 	 * Measure llc occupancy from resctrl.
19478941183SFenghua Yu 	 */
1952f320911SFenghua Yu 	if (!strncmp(param->resctrl_val, CMT_STR, sizeof(CMT_STR))) {
19678941183SFenghua Yu 		ret = get_llc_occu_resctrl(&llc_occu_resc);
19778941183SFenghua Yu 		if (ret < 0)
19878941183SFenghua Yu 			return ret;
19978941183SFenghua Yu 		llc_value = llc_occu_resc;
20078941183SFenghua Yu 	}
20178941183SFenghua Yu 	ret = print_results_cache(param->filename, bm_pid, llc_value);
20278941183SFenghua Yu 	if (ret)
20378941183SFenghua Yu 		return ret;
20478941183SFenghua Yu 
20578941183SFenghua Yu 	return 0;
20678941183SFenghua Yu }
207790bf585SFenghua Yu 
208790bf585SFenghua Yu /*
209790bf585SFenghua Yu  * cache_val:		execute benchmark and measure LLC occupancy resctrl
210790bf585SFenghua Yu  * and perf cache miss for the benchmark
211790bf585SFenghua Yu  * @param:		parameters passed to cache_val()
212790bf585SFenghua Yu  *
213790bf585SFenghua Yu  * Return:		0 on success. non-zero on failure.
214790bf585SFenghua Yu  */
215790bf585SFenghua Yu int cat_val(struct resctrl_val_param *param)
216790bf585SFenghua Yu {
217790bf585SFenghua Yu 	int malloc_and_init_memory = 1, memflush = 1, operation = 0, ret = 0;
218790bf585SFenghua Yu 	char *resctrl_val = param->resctrl_val;
219790bf585SFenghua Yu 	pid_t bm_pid;
220790bf585SFenghua Yu 
221790bf585SFenghua Yu 	if (strcmp(param->filename, "") == 0)
222790bf585SFenghua Yu 		sprintf(param->filename, "stdio");
223790bf585SFenghua Yu 
224790bf585SFenghua Yu 	bm_pid = getpid();
225790bf585SFenghua Yu 
226790bf585SFenghua Yu 	/* Taskset benchmark to specified cpu */
227790bf585SFenghua Yu 	ret = taskset_benchmark(bm_pid, param->cpu_no);
228790bf585SFenghua Yu 	if (ret)
229790bf585SFenghua Yu 		return ret;
230790bf585SFenghua Yu 
231790bf585SFenghua Yu 	/* Write benchmark to specified con_mon grp, mon_grp in resctrl FS*/
232790bf585SFenghua Yu 	ret = write_bm_pid_to_resctrl(bm_pid, param->ctrlgrp, param->mongrp,
233790bf585SFenghua Yu 				      resctrl_val);
234790bf585SFenghua Yu 	if (ret)
235790bf585SFenghua Yu 		return ret;
236790bf585SFenghua Yu 
23724286736SFenghua Yu 	if (!strncmp(resctrl_val, CAT_STR, sizeof(CAT_STR))) {
238790bf585SFenghua Yu 		ret = initialize_llc_perf();
239790bf585SFenghua Yu 		if (ret)
240790bf585SFenghua Yu 			return ret;
241790bf585SFenghua Yu 	}
242790bf585SFenghua Yu 
243790bf585SFenghua Yu 	/* Test runs until the callback setup() tells the test to stop. */
244790bf585SFenghua Yu 	while (1) {
24524286736SFenghua Yu 		if (!strncmp(resctrl_val, CAT_STR, sizeof(CAT_STR))) {
246790bf585SFenghua Yu 			ret = param->setup(1, param);
247790bf585SFenghua Yu 			if (ret) {
248790bf585SFenghua Yu 				ret = 0;
249790bf585SFenghua Yu 				break;
250790bf585SFenghua Yu 			}
251790bf585SFenghua Yu 			ret = reset_enable_llc_perf(bm_pid, param->cpu_no);
252790bf585SFenghua Yu 			if (ret)
253790bf585SFenghua Yu 				break;
254790bf585SFenghua Yu 
255790bf585SFenghua Yu 			if (run_fill_buf(param->span, malloc_and_init_memory,
256790bf585SFenghua Yu 					 memflush, operation, resctrl_val)) {
257790bf585SFenghua Yu 				fprintf(stderr, "Error-running fill buffer\n");
258790bf585SFenghua Yu 				ret = -1;
259790bf585SFenghua Yu 				break;
260790bf585SFenghua Yu 			}
261790bf585SFenghua Yu 
262790bf585SFenghua Yu 			sleep(1);
263790bf585SFenghua Yu 			ret = measure_cache_vals(param, bm_pid);
264790bf585SFenghua Yu 			if (ret)
265790bf585SFenghua Yu 				break;
266790bf585SFenghua Yu 		} else {
267790bf585SFenghua Yu 			break;
268790bf585SFenghua Yu 		}
269790bf585SFenghua Yu 	}
270790bf585SFenghua Yu 
271790bf585SFenghua Yu 	return ret;
272790bf585SFenghua Yu }
273*03216ed7SFenghua Yu 
274*03216ed7SFenghua Yu /*
275*03216ed7SFenghua Yu  * show_cache_info:	show cache test result information
276*03216ed7SFenghua Yu  * @sum_llc_val:	sum of LLC cache result data
277*03216ed7SFenghua Yu  * @no_of_bits:		number of bits
278*03216ed7SFenghua Yu  * @cache_span:		cache span in bytes for CMT or in lines for CAT
279*03216ed7SFenghua Yu  * @max_diff:		max difference
280*03216ed7SFenghua Yu  * @max_diff_percent:	max difference percentage
281*03216ed7SFenghua Yu  * @num_of_runs:	number of runs
282*03216ed7SFenghua Yu  * @platform:		show test information on this platform
283*03216ed7SFenghua Yu  * @cmt:		CMT test or CAT test
284*03216ed7SFenghua Yu  *
285*03216ed7SFenghua Yu  * Return:		0 on success. non-zero on failure.
286*03216ed7SFenghua Yu  */
287*03216ed7SFenghua Yu int show_cache_info(unsigned long sum_llc_val, int no_of_bits,
288*03216ed7SFenghua Yu 		    unsigned long cache_span, unsigned long max_diff,
289*03216ed7SFenghua Yu 		    unsigned long max_diff_percent, unsigned long num_of_runs,
290*03216ed7SFenghua Yu 		    bool platform, bool cmt)
291*03216ed7SFenghua Yu {
292*03216ed7SFenghua Yu 	unsigned long avg_llc_val = 0;
293*03216ed7SFenghua Yu 	float diff_percent;
294*03216ed7SFenghua Yu 	long avg_diff = 0;
295*03216ed7SFenghua Yu 	int ret;
296*03216ed7SFenghua Yu 
297*03216ed7SFenghua Yu 	avg_llc_val = sum_llc_val / (num_of_runs - 1);
298*03216ed7SFenghua Yu 	avg_diff = (long)abs(cache_span - avg_llc_val);
299*03216ed7SFenghua Yu 	diff_percent = ((float)cache_span - avg_llc_val) / cache_span * 100;
300*03216ed7SFenghua Yu 
301*03216ed7SFenghua Yu 	ret = platform && abs((int)diff_percent) > max_diff_percent &&
302*03216ed7SFenghua Yu 	      (cmt ? (abs(avg_diff) > max_diff) : true);
303*03216ed7SFenghua Yu 
304*03216ed7SFenghua Yu 	ksft_print_msg("%s cache miss rate within %d%%\n",
305*03216ed7SFenghua Yu 		       ret ? "Fail:" : "Pass:", max_diff_percent);
306*03216ed7SFenghua Yu 
307*03216ed7SFenghua Yu 	ksft_print_msg("Percent diff=%d\n", abs((int)diff_percent));
308*03216ed7SFenghua Yu 	ksft_print_msg("Number of bits: %d\n", no_of_bits);
309*03216ed7SFenghua Yu 	ksft_print_msg("Average LLC val: %lu\n", avg_llc_val);
310*03216ed7SFenghua Yu 	ksft_print_msg("Cache span (%s): %lu\n", cmt ? "bytes" : "lines",
311*03216ed7SFenghua Yu 		       cache_span);
312*03216ed7SFenghua Yu 
313*03216ed7SFenghua Yu 	return ret;
314*03216ed7SFenghua Yu }
315