178941183SFenghua Yu // SPDX-License-Identifier: GPL-2.0 278941183SFenghua Yu 378941183SFenghua Yu #include <stdint.h> 478941183SFenghua Yu #include "resctrl.h" 578941183SFenghua Yu 678941183SFenghua Yu struct read_format { 778941183SFenghua Yu __u64 nr; /* The number of events */ 878941183SFenghua Yu struct { 978941183SFenghua Yu __u64 value; /* The value of the event */ 1078941183SFenghua Yu } values[2]; 1178941183SFenghua Yu }; 1278941183SFenghua Yu 13790bf585SFenghua Yu static struct perf_event_attr pea_llc_miss; 14790bf585SFenghua Yu static struct read_format rf_cqm; 15790bf585SFenghua Yu static int fd_lm; 1678941183SFenghua Yu char llc_occup_path[1024]; 1778941183SFenghua Yu 18790bf585SFenghua Yu static void initialize_perf_event_attr(void) 19790bf585SFenghua Yu { 20790bf585SFenghua Yu pea_llc_miss.type = PERF_TYPE_HARDWARE; 21790bf585SFenghua Yu pea_llc_miss.size = sizeof(struct perf_event_attr); 22790bf585SFenghua Yu pea_llc_miss.read_format = PERF_FORMAT_GROUP; 23790bf585SFenghua Yu pea_llc_miss.exclude_kernel = 1; 24790bf585SFenghua Yu pea_llc_miss.exclude_hv = 1; 25790bf585SFenghua Yu pea_llc_miss.exclude_idle = 1; 26790bf585SFenghua Yu pea_llc_miss.exclude_callchain_kernel = 1; 27790bf585SFenghua Yu pea_llc_miss.inherit = 1; 28790bf585SFenghua Yu pea_llc_miss.exclude_guest = 1; 29790bf585SFenghua Yu pea_llc_miss.disabled = 1; 30790bf585SFenghua Yu } 31790bf585SFenghua Yu 32790bf585SFenghua Yu static void ioctl_perf_event_ioc_reset_enable(void) 33790bf585SFenghua Yu { 34790bf585SFenghua Yu ioctl(fd_lm, PERF_EVENT_IOC_RESET, 0); 35790bf585SFenghua Yu ioctl(fd_lm, PERF_EVENT_IOC_ENABLE, 0); 36790bf585SFenghua Yu } 37790bf585SFenghua Yu 38790bf585SFenghua Yu static int perf_event_open_llc_miss(pid_t pid, int cpu_no) 39790bf585SFenghua Yu { 40790bf585SFenghua Yu fd_lm = perf_event_open(&pea_llc_miss, pid, cpu_no, -1, 41790bf585SFenghua Yu PERF_FLAG_FD_CLOEXEC); 42790bf585SFenghua Yu if (fd_lm == -1) { 43790bf585SFenghua Yu perror("Error opening leader"); 44790bf585SFenghua Yu ctrlc_handler(0, NULL, NULL); 45790bf585SFenghua Yu return -1; 46790bf585SFenghua Yu } 47790bf585SFenghua Yu 48790bf585SFenghua Yu return 0; 49790bf585SFenghua Yu } 50790bf585SFenghua Yu 51790bf585SFenghua Yu static int initialize_llc_perf(void) 52790bf585SFenghua Yu { 53790bf585SFenghua Yu memset(&pea_llc_miss, 0, sizeof(struct perf_event_attr)); 54790bf585SFenghua Yu memset(&rf_cqm, 0, sizeof(struct read_format)); 55790bf585SFenghua Yu 56790bf585SFenghua Yu /* Initialize perf_event_attr structures for HW_CACHE_MISSES */ 57790bf585SFenghua Yu initialize_perf_event_attr(); 58790bf585SFenghua Yu 59790bf585SFenghua Yu pea_llc_miss.config = PERF_COUNT_HW_CACHE_MISSES; 60790bf585SFenghua Yu 61790bf585SFenghua Yu rf_cqm.nr = 1; 62790bf585SFenghua Yu 63790bf585SFenghua Yu return 0; 64790bf585SFenghua Yu } 65790bf585SFenghua Yu 66790bf585SFenghua Yu static int reset_enable_llc_perf(pid_t pid, int cpu_no) 67790bf585SFenghua Yu { 68790bf585SFenghua Yu int ret = 0; 69790bf585SFenghua Yu 70790bf585SFenghua Yu ret = perf_event_open_llc_miss(pid, cpu_no); 71790bf585SFenghua Yu if (ret < 0) 72790bf585SFenghua Yu return ret; 73790bf585SFenghua Yu 74790bf585SFenghua Yu /* Start counters to log values */ 75790bf585SFenghua Yu ioctl_perf_event_ioc_reset_enable(); 76790bf585SFenghua Yu 77790bf585SFenghua Yu return 0; 78790bf585SFenghua Yu } 79790bf585SFenghua Yu 80790bf585SFenghua Yu /* 81790bf585SFenghua Yu * get_llc_perf: llc cache miss through perf events 82790bf585SFenghua Yu * @cpu_no: CPU number that the benchmark PID is binded to 83790bf585SFenghua Yu * 84790bf585SFenghua Yu * Perf events like HW_CACHE_MISSES could be used to validate number of 85790bf585SFenghua Yu * cache lines allocated. 86790bf585SFenghua Yu * 87790bf585SFenghua Yu * Return: =0 on success. <0 on failure. 88790bf585SFenghua Yu */ 89790bf585SFenghua Yu static int get_llc_perf(unsigned long *llc_perf_miss) 90790bf585SFenghua Yu { 91790bf585SFenghua Yu __u64 total_misses; 92790bf585SFenghua Yu 93790bf585SFenghua Yu /* Stop counters after one span to get miss rate */ 94790bf585SFenghua Yu 95790bf585SFenghua Yu ioctl(fd_lm, PERF_EVENT_IOC_DISABLE, 0); 96790bf585SFenghua Yu 97790bf585SFenghua Yu if (read(fd_lm, &rf_cqm, sizeof(struct read_format)) == -1) { 98790bf585SFenghua Yu perror("Could not get llc misses through perf"); 99790bf585SFenghua Yu 100790bf585SFenghua Yu return -1; 101790bf585SFenghua Yu } 102790bf585SFenghua Yu 103790bf585SFenghua Yu total_misses = rf_cqm.values[0].value; 104790bf585SFenghua Yu 105790bf585SFenghua Yu close(fd_lm); 106790bf585SFenghua Yu 107790bf585SFenghua Yu *llc_perf_miss = total_misses; 108790bf585SFenghua Yu 109790bf585SFenghua Yu return 0; 110790bf585SFenghua Yu } 111790bf585SFenghua Yu 11278941183SFenghua Yu /* 11378941183SFenghua Yu * Get LLC Occupancy as reported by RESCTRL FS 1142f320911SFenghua Yu * For CMT, 11578941183SFenghua Yu * 1. If con_mon grp and mon grp given, then read from mon grp in 11678941183SFenghua Yu * con_mon grp 11778941183SFenghua Yu * 2. If only con_mon grp given, then read from con_mon grp 11878941183SFenghua Yu * 3. If both not given, then read from root con_mon grp 11978941183SFenghua Yu * For CAT, 12078941183SFenghua Yu * 1. If con_mon grp given, then read from it 12178941183SFenghua Yu * 2. If con_mon grp not given, then read from root con_mon grp 12278941183SFenghua Yu * 12378941183SFenghua Yu * Return: =0 on success. <0 on failure. 12478941183SFenghua Yu */ 12578941183SFenghua Yu static int get_llc_occu_resctrl(unsigned long *llc_occupancy) 12678941183SFenghua Yu { 12778941183SFenghua Yu FILE *fp; 12878941183SFenghua Yu 12978941183SFenghua Yu fp = fopen(llc_occup_path, "r"); 13078941183SFenghua Yu if (!fp) { 13178941183SFenghua Yu perror("Failed to open results file"); 13278941183SFenghua Yu 13378941183SFenghua Yu return errno; 13478941183SFenghua Yu } 13578941183SFenghua Yu if (fscanf(fp, "%lu", llc_occupancy) <= 0) { 13678941183SFenghua Yu perror("Could not get llc occupancy"); 13778941183SFenghua Yu fclose(fp); 13878941183SFenghua Yu 13978941183SFenghua Yu return -1; 14078941183SFenghua Yu } 14178941183SFenghua Yu fclose(fp); 14278941183SFenghua Yu 14378941183SFenghua Yu return 0; 14478941183SFenghua Yu } 14578941183SFenghua Yu 14678941183SFenghua Yu /* 14778941183SFenghua Yu * print_results_cache: the cache results are stored in a file 14878941183SFenghua Yu * @filename: file that stores the results 14978941183SFenghua Yu * @bm_pid: child pid that runs benchmark 15078941183SFenghua Yu * @llc_value: perf miss value / 15178941183SFenghua Yu * llc occupancy value reported by resctrl FS 15278941183SFenghua Yu * 15378941183SFenghua Yu * Return: 0 on success. non-zero on failure. 15478941183SFenghua Yu */ 15578941183SFenghua Yu static int print_results_cache(char *filename, int bm_pid, 15678941183SFenghua Yu unsigned long llc_value) 15778941183SFenghua Yu { 15878941183SFenghua Yu FILE *fp; 15978941183SFenghua Yu 16078941183SFenghua Yu if (strcmp(filename, "stdio") == 0 || strcmp(filename, "stderr") == 0) { 16178941183SFenghua Yu printf("Pid: %d \t LLC_value: %lu\n", bm_pid, 16278941183SFenghua Yu llc_value); 16378941183SFenghua Yu } else { 16478941183SFenghua Yu fp = fopen(filename, "a"); 16578941183SFenghua Yu if (!fp) { 16678941183SFenghua Yu perror("Cannot open results file"); 16778941183SFenghua Yu 16878941183SFenghua Yu return errno; 16978941183SFenghua Yu } 17078941183SFenghua Yu fprintf(fp, "Pid: %d \t llc_value: %lu\n", bm_pid, llc_value); 17178941183SFenghua Yu fclose(fp); 17278941183SFenghua Yu } 17378941183SFenghua Yu 17478941183SFenghua Yu return 0; 17578941183SFenghua Yu } 17678941183SFenghua Yu 17778941183SFenghua Yu int measure_cache_vals(struct resctrl_val_param *param, int bm_pid) 17878941183SFenghua Yu { 179790bf585SFenghua Yu unsigned long llc_perf_miss = 0, llc_occu_resc = 0, llc_value = 0; 18078941183SFenghua Yu int ret; 18178941183SFenghua Yu 18278941183SFenghua Yu /* 183790bf585SFenghua Yu * Measure cache miss from perf. 184790bf585SFenghua Yu */ 18524286736SFenghua Yu if (!strncmp(param->resctrl_val, CAT_STR, sizeof(CAT_STR))) { 186790bf585SFenghua Yu ret = get_llc_perf(&llc_perf_miss); 187790bf585SFenghua Yu if (ret < 0) 188790bf585SFenghua Yu return ret; 189790bf585SFenghua Yu llc_value = llc_perf_miss; 190790bf585SFenghua Yu } 191790bf585SFenghua Yu 192790bf585SFenghua Yu /* 19378941183SFenghua Yu * Measure llc occupancy from resctrl. 19478941183SFenghua Yu */ 1952f320911SFenghua Yu if (!strncmp(param->resctrl_val, CMT_STR, sizeof(CMT_STR))) { 19678941183SFenghua Yu ret = get_llc_occu_resctrl(&llc_occu_resc); 19778941183SFenghua Yu if (ret < 0) 19878941183SFenghua Yu return ret; 19978941183SFenghua Yu llc_value = llc_occu_resc; 20078941183SFenghua Yu } 20178941183SFenghua Yu ret = print_results_cache(param->filename, bm_pid, llc_value); 20278941183SFenghua Yu if (ret) 20378941183SFenghua Yu return ret; 20478941183SFenghua Yu 20578941183SFenghua Yu return 0; 20678941183SFenghua Yu } 207790bf585SFenghua Yu 208790bf585SFenghua Yu /* 209790bf585SFenghua Yu * cache_val: execute benchmark and measure LLC occupancy resctrl 210790bf585SFenghua Yu * and perf cache miss for the benchmark 211790bf585SFenghua Yu * @param: parameters passed to cache_val() 212790bf585SFenghua Yu * 213790bf585SFenghua Yu * Return: 0 on success. non-zero on failure. 214790bf585SFenghua Yu */ 215790bf585SFenghua Yu int cat_val(struct resctrl_val_param *param) 216790bf585SFenghua Yu { 217790bf585SFenghua Yu int malloc_and_init_memory = 1, memflush = 1, operation = 0, ret = 0; 218790bf585SFenghua Yu char *resctrl_val = param->resctrl_val; 219790bf585SFenghua Yu pid_t bm_pid; 220790bf585SFenghua Yu 221790bf585SFenghua Yu if (strcmp(param->filename, "") == 0) 222790bf585SFenghua Yu sprintf(param->filename, "stdio"); 223790bf585SFenghua Yu 224790bf585SFenghua Yu bm_pid = getpid(); 225790bf585SFenghua Yu 226790bf585SFenghua Yu /* Taskset benchmark to specified cpu */ 227790bf585SFenghua Yu ret = taskset_benchmark(bm_pid, param->cpu_no); 228790bf585SFenghua Yu if (ret) 229790bf585SFenghua Yu return ret; 230790bf585SFenghua Yu 231790bf585SFenghua Yu /* Write benchmark to specified con_mon grp, mon_grp in resctrl FS*/ 232790bf585SFenghua Yu ret = write_bm_pid_to_resctrl(bm_pid, param->ctrlgrp, param->mongrp, 233790bf585SFenghua Yu resctrl_val); 234790bf585SFenghua Yu if (ret) 235790bf585SFenghua Yu return ret; 236790bf585SFenghua Yu 23724286736SFenghua Yu if (!strncmp(resctrl_val, CAT_STR, sizeof(CAT_STR))) { 238790bf585SFenghua Yu ret = initialize_llc_perf(); 239790bf585SFenghua Yu if (ret) 240790bf585SFenghua Yu return ret; 241790bf585SFenghua Yu } 242790bf585SFenghua Yu 243790bf585SFenghua Yu /* Test runs until the callback setup() tells the test to stop. */ 244790bf585SFenghua Yu while (1) { 24524286736SFenghua Yu if (!strncmp(resctrl_val, CAT_STR, sizeof(CAT_STR))) { 246790bf585SFenghua Yu ret = param->setup(1, param); 247*fa10366cSIlpo Järvinen if (ret == END_OF_TESTS) { 248790bf585SFenghua Yu ret = 0; 249790bf585SFenghua Yu break; 250790bf585SFenghua Yu } 251*fa10366cSIlpo Järvinen if (ret < 0) 252*fa10366cSIlpo Järvinen break; 253790bf585SFenghua Yu ret = reset_enable_llc_perf(bm_pid, param->cpu_no); 254790bf585SFenghua Yu if (ret) 255790bf585SFenghua Yu break; 256790bf585SFenghua Yu 257790bf585SFenghua Yu if (run_fill_buf(param->span, malloc_and_init_memory, 258790bf585SFenghua Yu memflush, operation, resctrl_val)) { 259790bf585SFenghua Yu fprintf(stderr, "Error-running fill buffer\n"); 260790bf585SFenghua Yu ret = -1; 261790bf585SFenghua Yu break; 262790bf585SFenghua Yu } 263790bf585SFenghua Yu 264790bf585SFenghua Yu sleep(1); 265790bf585SFenghua Yu ret = measure_cache_vals(param, bm_pid); 266790bf585SFenghua Yu if (ret) 267790bf585SFenghua Yu break; 268790bf585SFenghua Yu } else { 269790bf585SFenghua Yu break; 270790bf585SFenghua Yu } 271790bf585SFenghua Yu } 272790bf585SFenghua Yu 273790bf585SFenghua Yu return ret; 274790bf585SFenghua Yu } 27503216ed7SFenghua Yu 27603216ed7SFenghua Yu /* 27703216ed7SFenghua Yu * show_cache_info: show cache test result information 27803216ed7SFenghua Yu * @sum_llc_val: sum of LLC cache result data 27903216ed7SFenghua Yu * @no_of_bits: number of bits 28003216ed7SFenghua Yu * @cache_span: cache span in bytes for CMT or in lines for CAT 28103216ed7SFenghua Yu * @max_diff: max difference 28203216ed7SFenghua Yu * @max_diff_percent: max difference percentage 28303216ed7SFenghua Yu * @num_of_runs: number of runs 28403216ed7SFenghua Yu * @platform: show test information on this platform 28503216ed7SFenghua Yu * @cmt: CMT test or CAT test 28603216ed7SFenghua Yu * 28703216ed7SFenghua Yu * Return: 0 on success. non-zero on failure. 28803216ed7SFenghua Yu */ 28903216ed7SFenghua Yu int show_cache_info(unsigned long sum_llc_val, int no_of_bits, 29003216ed7SFenghua Yu unsigned long cache_span, unsigned long max_diff, 29103216ed7SFenghua Yu unsigned long max_diff_percent, unsigned long num_of_runs, 29203216ed7SFenghua Yu bool platform, bool cmt) 29303216ed7SFenghua Yu { 29403216ed7SFenghua Yu unsigned long avg_llc_val = 0; 29503216ed7SFenghua Yu float diff_percent; 29603216ed7SFenghua Yu long avg_diff = 0; 29703216ed7SFenghua Yu int ret; 29803216ed7SFenghua Yu 29903216ed7SFenghua Yu avg_llc_val = sum_llc_val / (num_of_runs - 1); 30003216ed7SFenghua Yu avg_diff = (long)abs(cache_span - avg_llc_val); 30103216ed7SFenghua Yu diff_percent = ((float)cache_span - avg_llc_val) / cache_span * 100; 30203216ed7SFenghua Yu 30303216ed7SFenghua Yu ret = platform && abs((int)diff_percent) > max_diff_percent && 30403216ed7SFenghua Yu (cmt ? (abs(avg_diff) > max_diff) : true); 30503216ed7SFenghua Yu 306e7507478SFenghua Yu ksft_print_msg("%s Check cache miss rate within %d%%\n", 30703216ed7SFenghua Yu ret ? "Fail:" : "Pass:", max_diff_percent); 30803216ed7SFenghua Yu 30903216ed7SFenghua Yu ksft_print_msg("Percent diff=%d\n", abs((int)diff_percent)); 31003216ed7SFenghua Yu ksft_print_msg("Number of bits: %d\n", no_of_bits); 31103216ed7SFenghua Yu ksft_print_msg("Average LLC val: %lu\n", avg_llc_val); 31203216ed7SFenghua Yu ksft_print_msg("Cache span (%s): %lu\n", cmt ? "bytes" : "lines", 31303216ed7SFenghua Yu cache_span); 31403216ed7SFenghua Yu 31503216ed7SFenghua Yu return ret; 31603216ed7SFenghua Yu } 317