1 // SPDX-License-Identifier: GPL-2.0
2 
3 #include <stdint.h>
4 #include "resctrl.h"
5 
6 struct perf_event_read {
7 	__u64 nr;			/* The number of events */
8 	struct {
9 		__u64 value;		/* The value of the event */
10 	} values[2];
11 };
12 
13 static struct perf_event_attr pea_llc_miss;
14 static struct perf_event_read pe_read;
15 static int pe_fd;
16 char llc_occup_path[1024];
17 
18 static void perf_event_attr_initialize(__u64 config)
19 {
20 	memset(&pea_llc_miss, 0, sizeof(struct perf_event_attr));
21 	pea_llc_miss.type = PERF_TYPE_HARDWARE;
22 	pea_llc_miss.size = sizeof(struct perf_event_attr);
23 	pea_llc_miss.read_format = PERF_FORMAT_GROUP;
24 	pea_llc_miss.exclude_kernel = 1;
25 	pea_llc_miss.exclude_hv = 1;
26 	pea_llc_miss.exclude_idle = 1;
27 	pea_llc_miss.exclude_callchain_kernel = 1;
28 	pea_llc_miss.inherit = 1;
29 	pea_llc_miss.exclude_guest = 1;
30 	pea_llc_miss.disabled = 1;
31 	pea_llc_miss.config = config;
32 }
33 
34 /* Start counters to log values */
35 static void perf_event_reset_enable(void)
36 {
37 	ioctl(pe_fd, PERF_EVENT_IOC_RESET, 0);
38 	ioctl(pe_fd, PERF_EVENT_IOC_ENABLE, 0);
39 }
40 
41 static void perf_event_initialize_read_format(void)
42 {
43 	memset(&pe_read, 0, sizeof(struct perf_event_read));
44 	pe_read.nr = 1;
45 }
46 
47 static int perf_open(pid_t pid, int cpu_no)
48 {
49 	pe_fd = perf_event_open(&pea_llc_miss, pid, cpu_no, -1, PERF_FLAG_FD_CLOEXEC);
50 	if (pe_fd == -1) {
51 		ksft_perror("Error opening leader");
52 		return -1;
53 	}
54 
55 	perf_event_reset_enable();
56 
57 	return 0;
58 }
59 
60 /*
61  * Get LLC Occupancy as reported by RESCTRL FS
62  * For CMT,
63  * 1. If con_mon grp and mon grp given, then read from mon grp in
64  * con_mon grp
65  * 2. If only con_mon grp given, then read from con_mon grp
66  * 3. If both not given, then read from root con_mon grp
67  * For CAT,
68  * 1. If con_mon grp given, then read from it
69  * 2. If con_mon grp not given, then read from root con_mon grp
70  *
71  * Return: =0 on success.  <0 on failure.
72  */
73 static int get_llc_occu_resctrl(unsigned long *llc_occupancy)
74 {
75 	FILE *fp;
76 
77 	fp = fopen(llc_occup_path, "r");
78 	if (!fp) {
79 		ksft_perror("Failed to open results file");
80 
81 		return -1;
82 	}
83 	if (fscanf(fp, "%lu", llc_occupancy) <= 0) {
84 		ksft_perror("Could not get llc occupancy");
85 		fclose(fp);
86 
87 		return -1;
88 	}
89 	fclose(fp);
90 
91 	return 0;
92 }
93 
94 /*
95  * print_results_cache:	the cache results are stored in a file
96  * @filename:		file that stores the results
97  * @bm_pid:		child pid that runs benchmark
98  * @llc_value:		perf miss value /
99  *			llc occupancy value reported by resctrl FS
100  *
101  * Return:		0 on success, < 0 on error.
102  */
103 static int print_results_cache(const char *filename, int bm_pid, __u64 llc_value)
104 {
105 	FILE *fp;
106 
107 	if (strcmp(filename, "stdio") == 0 || strcmp(filename, "stderr") == 0) {
108 		printf("Pid: %d \t LLC_value: %llu\n", bm_pid, llc_value);
109 	} else {
110 		fp = fopen(filename, "a");
111 		if (!fp) {
112 			ksft_perror("Cannot open results file");
113 
114 			return -1;
115 		}
116 		fprintf(fp, "Pid: %d \t llc_value: %llu\n", bm_pid, llc_value);
117 		fclose(fp);
118 	}
119 
120 	return 0;
121 }
122 
123 /*
124  * perf_event_measure - Measure perf events
125  * @filename:	Filename for writing the results
126  * @bm_pid:	PID that runs the benchmark
127  *
128  * Measures perf events (e.g., cache misses) and writes the results into
129  * @filename. @bm_pid is written to the results file along with the measured
130  * value.
131  *
132  * Return: =0 on success. <0 on failure.
133  */
134 static int perf_event_measure(const char *filename, int bm_pid)
135 {
136 	int ret;
137 
138 	/* Stop counters after one span to get miss rate */
139 	ioctl(pe_fd, PERF_EVENT_IOC_DISABLE, 0);
140 
141 	ret = read(pe_fd, &pe_read, sizeof(struct perf_event_read));
142 	if (ret == -1) {
143 		ksft_perror("Could not get perf value");
144 		return -1;
145 	}
146 
147 	return print_results_cache(filename, bm_pid, pe_read.values[0].value);
148 }
149 
150 /*
151  * measure_llc_resctrl - Measure resctrl LLC value from resctrl
152  * @filename:	Filename for writing the results
153  * @bm_pid:	PID that runs the benchmark
154  *
155  * Measures LLC occupancy from resctrl and writes the results into @filename.
156  * @bm_pid is written to the results file along with the measured value.
157  *
158  * Return: =0 on success. <0 on failure.
159  */
160 int measure_llc_resctrl(const char *filename, int bm_pid)
161 {
162 	unsigned long llc_occu_resc = 0;
163 	int ret;
164 
165 	ret = get_llc_occu_resctrl(&llc_occu_resc);
166 	if (ret < 0)
167 		return ret;
168 
169 	return print_results_cache(filename, bm_pid, llc_occu_resc);
170 }
171 
172 /*
173  * cache_val:		execute benchmark and measure LLC occupancy resctrl
174  * and perf cache miss for the benchmark
175  * @param:		parameters passed to cache_val()
176  * @span:		buffer size for the benchmark
177  *
178  * Return:		0 when the test was run, < 0 on error.
179  */
180 int cat_val(struct resctrl_val_param *param, size_t span)
181 {
182 	int memflush = 1, operation = 0, ret = 0;
183 	char *resctrl_val = param->resctrl_val;
184 	pid_t bm_pid;
185 
186 	if (strcmp(param->filename, "") == 0)
187 		sprintf(param->filename, "stdio");
188 
189 	bm_pid = getpid();
190 
191 	/* Taskset benchmark to specified cpu */
192 	ret = taskset_benchmark(bm_pid, param->cpu_no);
193 	if (ret)
194 		return ret;
195 
196 	/* Write benchmark to specified con_mon grp, mon_grp in resctrl FS*/
197 	ret = write_bm_pid_to_resctrl(bm_pid, param->ctrlgrp, param->mongrp,
198 				      resctrl_val);
199 	if (ret)
200 		return ret;
201 
202 	perf_event_attr_initialize(PERF_COUNT_HW_CACHE_MISSES);
203 	perf_event_initialize_read_format();
204 
205 	/* Test runs until the callback setup() tells the test to stop. */
206 	while (1) {
207 		ret = param->setup(param);
208 		if (ret == END_OF_TESTS) {
209 			ret = 0;
210 			break;
211 		}
212 		if (ret < 0)
213 			break;
214 		ret = perf_open(bm_pid, param->cpu_no);
215 		if (ret)
216 			break;
217 
218 		if (run_fill_buf(span, memflush, operation, true)) {
219 			fprintf(stderr, "Error-running fill buffer\n");
220 			ret = -1;
221 			goto pe_close;
222 		}
223 
224 		sleep(1);
225 		ret = perf_event_measure(param->filename, bm_pid);
226 		if (ret)
227 			goto pe_close;
228 	}
229 
230 	return ret;
231 
232 pe_close:
233 	close(pe_fd);
234 	return ret;
235 }
236 
237 /*
238  * show_cache_info - Show generic cache test information
239  * @no_of_bits:		Number of bits
240  * @avg_llc_val:	Average of LLC cache result data
241  * @cache_span:		Cache span
242  * @lines:		@cache_span in lines or bytes
243  */
244 void show_cache_info(int no_of_bits, __u64 avg_llc_val, size_t cache_span, bool lines)
245 {
246 	ksft_print_msg("Number of bits: %d\n", no_of_bits);
247 	ksft_print_msg("Average LLC val: %llu\n", avg_llc_val);
248 	ksft_print_msg("Cache span (%s): %zu\n", lines ? "lines" : "bytes",
249 		       cache_span);
250 }
251