1 // SPDX-License-Identifier: GPL-2.0 2 3 #include <stdint.h> 4 #include "resctrl.h" 5 6 struct read_format { 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 read_format rf_cqm; 15 static int fd_lm; 16 char llc_occup_path[1024]; 17 18 static void initialize_perf_event_attr(void) 19 { 20 pea_llc_miss.type = PERF_TYPE_HARDWARE; 21 pea_llc_miss.size = sizeof(struct perf_event_attr); 22 pea_llc_miss.read_format = PERF_FORMAT_GROUP; 23 pea_llc_miss.exclude_kernel = 1; 24 pea_llc_miss.exclude_hv = 1; 25 pea_llc_miss.exclude_idle = 1; 26 pea_llc_miss.exclude_callchain_kernel = 1; 27 pea_llc_miss.inherit = 1; 28 pea_llc_miss.exclude_guest = 1; 29 pea_llc_miss.disabled = 1; 30 } 31 32 static void ioctl_perf_event_ioc_reset_enable(void) 33 { 34 ioctl(fd_lm, PERF_EVENT_IOC_RESET, 0); 35 ioctl(fd_lm, PERF_EVENT_IOC_ENABLE, 0); 36 } 37 38 static int perf_event_open_llc_miss(pid_t pid, int cpu_no) 39 { 40 fd_lm = perf_event_open(&pea_llc_miss, pid, cpu_no, -1, 41 PERF_FLAG_FD_CLOEXEC); 42 if (fd_lm == -1) { 43 perror("Error opening leader"); 44 ctrlc_handler(0, NULL, NULL); 45 return -1; 46 } 47 48 return 0; 49 } 50 51 static int initialize_llc_perf(void) 52 { 53 memset(&pea_llc_miss, 0, sizeof(struct perf_event_attr)); 54 memset(&rf_cqm, 0, sizeof(struct read_format)); 55 56 /* Initialize perf_event_attr structures for HW_CACHE_MISSES */ 57 initialize_perf_event_attr(); 58 59 pea_llc_miss.config = PERF_COUNT_HW_CACHE_MISSES; 60 61 rf_cqm.nr = 1; 62 63 return 0; 64 } 65 66 static int reset_enable_llc_perf(pid_t pid, int cpu_no) 67 { 68 int ret = 0; 69 70 ret = perf_event_open_llc_miss(pid, cpu_no); 71 if (ret < 0) 72 return ret; 73 74 /* Start counters to log values */ 75 ioctl_perf_event_ioc_reset_enable(); 76 77 return 0; 78 } 79 80 /* 81 * get_llc_perf: llc cache miss through perf events 82 * @cpu_no: CPU number that the benchmark PID is binded to 83 * 84 * Perf events like HW_CACHE_MISSES could be used to validate number of 85 * cache lines allocated. 86 * 87 * Return: =0 on success. <0 on failure. 88 */ 89 static int get_llc_perf(unsigned long *llc_perf_miss) 90 { 91 __u64 total_misses; 92 93 /* Stop counters after one span to get miss rate */ 94 95 ioctl(fd_lm, PERF_EVENT_IOC_DISABLE, 0); 96 97 if (read(fd_lm, &rf_cqm, sizeof(struct read_format)) == -1) { 98 perror("Could not get llc misses through perf"); 99 100 return -1; 101 } 102 103 total_misses = rf_cqm.values[0].value; 104 105 close(fd_lm); 106 107 *llc_perf_miss = total_misses; 108 109 return 0; 110 } 111 112 /* 113 * Get LLC Occupancy as reported by RESCTRL FS 114 * For CQM, 115 * 1. If con_mon grp and mon grp given, then read from mon grp in 116 * con_mon grp 117 * 2. If only con_mon grp given, then read from con_mon grp 118 * 3. If both not given, then read from root con_mon grp 119 * For CAT, 120 * 1. If con_mon grp given, then read from it 121 * 2. If con_mon grp not given, then read from root con_mon grp 122 * 123 * Return: =0 on success. <0 on failure. 124 */ 125 static int get_llc_occu_resctrl(unsigned long *llc_occupancy) 126 { 127 FILE *fp; 128 129 fp = fopen(llc_occup_path, "r"); 130 if (!fp) { 131 perror("Failed to open results file"); 132 133 return errno; 134 } 135 if (fscanf(fp, "%lu", llc_occupancy) <= 0) { 136 perror("Could not get llc occupancy"); 137 fclose(fp); 138 139 return -1; 140 } 141 fclose(fp); 142 143 return 0; 144 } 145 146 /* 147 * print_results_cache: the cache results are stored in a file 148 * @filename: file that stores the results 149 * @bm_pid: child pid that runs benchmark 150 * @llc_value: perf miss value / 151 * llc occupancy value reported by resctrl FS 152 * 153 * Return: 0 on success. non-zero on failure. 154 */ 155 static int print_results_cache(char *filename, int bm_pid, 156 unsigned long llc_value) 157 { 158 FILE *fp; 159 160 if (strcmp(filename, "stdio") == 0 || strcmp(filename, "stderr") == 0) { 161 printf("Pid: %d \t LLC_value: %lu\n", bm_pid, 162 llc_value); 163 } else { 164 fp = fopen(filename, "a"); 165 if (!fp) { 166 perror("Cannot open results file"); 167 168 return errno; 169 } 170 fprintf(fp, "Pid: %d \t llc_value: %lu\n", bm_pid, llc_value); 171 fclose(fp); 172 } 173 174 return 0; 175 } 176 177 int measure_cache_vals(struct resctrl_val_param *param, int bm_pid) 178 { 179 unsigned long llc_perf_miss = 0, llc_occu_resc = 0, llc_value = 0; 180 int ret; 181 182 /* 183 * Measure cache miss from perf. 184 */ 185 if (!strcmp(param->resctrl_val, "cat")) { 186 ret = get_llc_perf(&llc_perf_miss); 187 if (ret < 0) 188 return ret; 189 llc_value = llc_perf_miss; 190 } 191 192 /* 193 * Measure llc occupancy from resctrl. 194 */ 195 if (!strcmp(param->resctrl_val, "cqm")) { 196 ret = get_llc_occu_resctrl(&llc_occu_resc); 197 if (ret < 0) 198 return ret; 199 llc_value = llc_occu_resc; 200 } 201 ret = print_results_cache(param->filename, bm_pid, llc_value); 202 if (ret) 203 return ret; 204 205 return 0; 206 } 207 208 /* 209 * cache_val: execute benchmark and measure LLC occupancy resctrl 210 * and perf cache miss for the benchmark 211 * @param: parameters passed to cache_val() 212 * 213 * Return: 0 on success. non-zero on failure. 214 */ 215 int cat_val(struct resctrl_val_param *param) 216 { 217 int malloc_and_init_memory = 1, memflush = 1, operation = 0, ret = 0; 218 char *resctrl_val = param->resctrl_val; 219 pid_t bm_pid; 220 221 if (strcmp(param->filename, "") == 0) 222 sprintf(param->filename, "stdio"); 223 224 bm_pid = getpid(); 225 226 /* Taskset benchmark to specified cpu */ 227 ret = taskset_benchmark(bm_pid, param->cpu_no); 228 if (ret) 229 return ret; 230 231 /* Write benchmark to specified con_mon grp, mon_grp in resctrl FS*/ 232 ret = write_bm_pid_to_resctrl(bm_pid, param->ctrlgrp, param->mongrp, 233 resctrl_val); 234 if (ret) 235 return ret; 236 237 if ((strcmp(resctrl_val, "cat") == 0)) { 238 ret = initialize_llc_perf(); 239 if (ret) 240 return ret; 241 } 242 243 /* Test runs until the callback setup() tells the test to stop. */ 244 while (1) { 245 if (strcmp(resctrl_val, "cat") == 0) { 246 ret = param->setup(1, param); 247 if (ret) { 248 ret = 0; 249 break; 250 } 251 ret = reset_enable_llc_perf(bm_pid, param->cpu_no); 252 if (ret) 253 break; 254 255 if (run_fill_buf(param->span, malloc_and_init_memory, 256 memflush, operation, resctrl_val)) { 257 fprintf(stderr, "Error-running fill buffer\n"); 258 ret = -1; 259 break; 260 } 261 262 sleep(1); 263 ret = measure_cache_vals(param, bm_pid); 264 if (ret) 265 break; 266 } else { 267 break; 268 } 269 } 270 271 return ret; 272 } 273