1// SPDX-License-Identifier: GPL-2.0 2 3#include <stdint.h> 4#include "resctrl.h" 5 6struct read_format { 7 __u64 nr; /* The number of events */ 8 struct { 9 __u64 value; /* The value of the event */ 10 } values[2]; 11}; 12 13static struct perf_event_attr pea_llc_miss; 14static struct read_format rf_cqm; 15static int fd_lm; 16char llc_occup_path[1024]; 17 18static 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 32static 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 38static 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 51static 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 66static 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 */ 89static int get_llc_perf(unsigned long *llc_perf_miss) 90{ 91 __u64 total_misses; 92 int ret; 93 94 /* Stop counters after one span to get miss rate */ 95 96 ioctl(fd_lm, PERF_EVENT_IOC_DISABLE, 0); 97 98 ret = read(fd_lm, &rf_cqm, sizeof(struct read_format)); 99 if (ret == -1) { 100 perror("Could not get llc misses through perf"); 101 return -1; 102 } 103 104 total_misses = rf_cqm.values[0].value; 105 *llc_perf_miss = total_misses; 106 107 return 0; 108} 109 110/* 111 * Get LLC Occupancy as reported by RESCTRL FS 112 * For CQM, 113 * 1. If con_mon grp and mon grp given, then read from mon grp in 114 * con_mon grp 115 * 2. If only con_mon grp given, then read from con_mon grp 116 * 3. If both not given, then read from root con_mon grp 117 * For CAT, 118 * 1. If con_mon grp given, then read from it 119 * 2. If con_mon grp not given, then read from root con_mon grp 120 * 121 * Return: =0 on success. <0 on failure. 122 */ 123static int get_llc_occu_resctrl(unsigned long *llc_occupancy) 124{ 125 FILE *fp; 126 127 fp = fopen(llc_occup_path, "r"); 128 if (!fp) { 129 perror("Failed to open results file"); 130 131 return errno; 132 } 133 if (fscanf(fp, "%lu", llc_occupancy) <= 0) { 134 perror("Could not get llc occupancy"); 135 fclose(fp); 136 137 return -1; 138 } 139 fclose(fp); 140 141 return 0; 142} 143 144/* 145 * print_results_cache: the cache results are stored in a file 146 * @filename: file that stores the results 147 * @bm_pid: child pid that runs benchmark 148 * @llc_value: perf miss value / 149 * llc occupancy value reported by resctrl FS 150 * 151 * Return: 0 on success. non-zero on failure. 152 */ 153static int print_results_cache(char *filename, int bm_pid, 154 unsigned long llc_value) 155{ 156 FILE *fp; 157 158 if (strcmp(filename, "stdio") == 0 || strcmp(filename, "stderr") == 0) { 159 printf("Pid: %d \t LLC_value: %lu\n", bm_pid, 160 llc_value); 161 } else { 162 fp = fopen(filename, "a"); 163 if (!fp) { 164 perror("Cannot open results file"); 165 166 return errno; 167 } 168 fprintf(fp, "Pid: %d \t llc_value: %lu\n", bm_pid, llc_value); 169 fclose(fp); 170 } 171 172 return 0; 173} 174 175int measure_cache_vals(struct resctrl_val_param *param, int bm_pid) 176{ 177 unsigned long llc_perf_miss = 0, llc_occu_resc = 0, llc_value = 0; 178 int ret; 179 180 /* 181 * Measure cache miss from perf. 182 */ 183 if (!strncmp(param->resctrl_val, CAT_STR, sizeof(CAT_STR))) { 184 ret = get_llc_perf(&llc_perf_miss); 185 if (ret < 0) 186 return ret; 187 llc_value = llc_perf_miss; 188 } 189 190 /* 191 * Measure llc occupancy from resctrl. 192 */ 193 if (!strncmp(param->resctrl_val, CQM_STR, sizeof(CQM_STR))) { 194 ret = get_llc_occu_resctrl(&llc_occu_resc); 195 if (ret < 0) 196 return ret; 197 llc_value = llc_occu_resc; 198 } 199 ret = print_results_cache(param->filename, bm_pid, llc_value); 200 if (ret) 201 return ret; 202 203 return 0; 204} 205 206/* 207 * cache_val: execute benchmark and measure LLC occupancy resctrl 208 * and perf cache miss for the benchmark 209 * @param: parameters passed to cache_val() 210 * 211 * Return: 0 on success. non-zero on failure. 212 */ 213int cat_val(struct resctrl_val_param *param) 214{ 215 int malloc_and_init_memory = 1, memflush = 1, operation = 0, ret = 0; 216 char *resctrl_val = param->resctrl_val; 217 pid_t bm_pid; 218 219 if (strcmp(param->filename, "") == 0) 220 sprintf(param->filename, "stdio"); 221 222 bm_pid = getpid(); 223 224 /* Taskset benchmark to specified cpu */ 225 ret = taskset_benchmark(bm_pid, param->cpu_no); 226 if (ret) 227 return ret; 228 229 /* Write benchmark to specified con_mon grp, mon_grp in resctrl FS*/ 230 ret = write_bm_pid_to_resctrl(bm_pid, param->ctrlgrp, param->mongrp, 231 resctrl_val); 232 if (ret) 233 return ret; 234 235 if (!strncmp(resctrl_val, CAT_STR, sizeof(CAT_STR))) { 236 ret = initialize_llc_perf(); 237 if (ret) 238 return ret; 239 } 240 241 /* Test runs until the callback setup() tells the test to stop. */ 242 while (1) { 243 if (!strncmp(resctrl_val, CAT_STR, sizeof(CAT_STR))) { 244 ret = param->setup(1, param); 245 if (ret) { 246 ret = 0; 247 break; 248 } 249 ret = reset_enable_llc_perf(bm_pid, param->cpu_no); 250 if (ret) 251 break; 252 253 if (run_fill_buf(param->span, malloc_and_init_memory, 254 memflush, operation, resctrl_val)) { 255 fprintf(stderr, "Error-running fill buffer\n"); 256 ret = -1; 257 goto pe_close; 258 } 259 260 sleep(1); 261 ret = measure_cache_vals(param, bm_pid); 262 if (ret) 263 goto pe_close; 264 265 close(fd_lm); 266 } else { 267 break; 268 } 269 } 270 271 return ret; 272 273pe_close: 274 close(fd_lm); 275 return ret; 276} 277