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