Olivier Deprez | 157378f | 2022-04-04 15:47:50 +0200 | [diff] [blame^] | 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 (!strncmp(param->resctrl_val, CAT_STR, sizeof(CAT_STR))) { |
| 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 (!strncmp(param->resctrl_val, CQM_STR, sizeof(CQM_STR))) { |
| 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 (!strncmp(resctrl_val, CAT_STR, sizeof(CAT_STR))) { |
| 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 (!strncmp(resctrl_val, CAT_STR, sizeof(CAT_STR))) { |
| 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 | } |