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 519ce29d23SIlpo Järvinen static void 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 64790bf585SFenghua Yu static int reset_enable_llc_perf(pid_t pid, int cpu_no) 65790bf585SFenghua Yu { 66790bf585SFenghua Yu int ret = 0; 67790bf585SFenghua Yu 68790bf585SFenghua Yu ret = perf_event_open_llc_miss(pid, cpu_no); 69790bf585SFenghua Yu if (ret < 0) 70790bf585SFenghua Yu return ret; 71790bf585SFenghua Yu 72790bf585SFenghua Yu /* Start counters to log values */ 73790bf585SFenghua Yu ioctl_perf_event_ioc_reset_enable(); 74790bf585SFenghua Yu 75790bf585SFenghua Yu return 0; 76790bf585SFenghua Yu } 77790bf585SFenghua Yu 78790bf585SFenghua Yu /* 79790bf585SFenghua Yu * get_llc_perf: llc cache miss through perf events 805874a6a1SIlpo Järvinen * @llc_perf_miss: LLC miss counter that is filled on success 81790bf585SFenghua Yu * 82790bf585SFenghua Yu * Perf events like HW_CACHE_MISSES could be used to validate number of 83790bf585SFenghua Yu * cache lines allocated. 84790bf585SFenghua Yu * 85790bf585SFenghua Yu * Return: =0 on success. <0 on failure. 86790bf585SFenghua Yu */ 87790bf585SFenghua Yu static int get_llc_perf(unsigned long *llc_perf_miss) 88790bf585SFenghua Yu { 89790bf585SFenghua Yu __u64 total_misses; 9051a0c3b7SIlpo Järvinen int ret; 91790bf585SFenghua Yu 92790bf585SFenghua Yu /* Stop counters after one span to get miss rate */ 93790bf585SFenghua Yu 94790bf585SFenghua Yu ioctl(fd_lm, PERF_EVENT_IOC_DISABLE, 0); 95790bf585SFenghua Yu 9651a0c3b7SIlpo Järvinen ret = read(fd_lm, &rf_cqm, sizeof(struct read_format)); 9751a0c3b7SIlpo Järvinen if (ret == -1) { 98790bf585SFenghua Yu perror("Could not get llc misses through perf"); 99790bf585SFenghua Yu return -1; 100790bf585SFenghua Yu } 101790bf585SFenghua Yu 102790bf585SFenghua Yu total_misses = rf_cqm.values[0].value; 103790bf585SFenghua Yu *llc_perf_miss = total_misses; 104790bf585SFenghua Yu 105790bf585SFenghua Yu return 0; 106790bf585SFenghua Yu } 107790bf585SFenghua Yu 10878941183SFenghua Yu /* 10978941183SFenghua Yu * Get LLC Occupancy as reported by RESCTRL FS 1102f320911SFenghua Yu * For CMT, 11178941183SFenghua Yu * 1. If con_mon grp and mon grp given, then read from mon grp in 11278941183SFenghua Yu * con_mon grp 11378941183SFenghua Yu * 2. If only con_mon grp given, then read from con_mon grp 11478941183SFenghua Yu * 3. If both not given, then read from root con_mon grp 11578941183SFenghua Yu * For CAT, 11678941183SFenghua Yu * 1. If con_mon grp given, then read from it 11778941183SFenghua Yu * 2. If con_mon grp not given, then read from root con_mon grp 11878941183SFenghua Yu * 11978941183SFenghua Yu * Return: =0 on success. <0 on failure. 12078941183SFenghua Yu */ 12178941183SFenghua Yu static int get_llc_occu_resctrl(unsigned long *llc_occupancy) 12278941183SFenghua Yu { 12378941183SFenghua Yu FILE *fp; 12478941183SFenghua Yu 12578941183SFenghua Yu fp = fopen(llc_occup_path, "r"); 12678941183SFenghua Yu if (!fp) { 12778941183SFenghua Yu perror("Failed to open results file"); 12878941183SFenghua Yu 12978941183SFenghua Yu return errno; 13078941183SFenghua Yu } 13178941183SFenghua Yu if (fscanf(fp, "%lu", llc_occupancy) <= 0) { 13278941183SFenghua Yu perror("Could not get llc occupancy"); 13378941183SFenghua Yu fclose(fp); 13478941183SFenghua Yu 13578941183SFenghua Yu return -1; 13678941183SFenghua Yu } 13778941183SFenghua Yu fclose(fp); 13878941183SFenghua Yu 13978941183SFenghua Yu return 0; 14078941183SFenghua Yu } 14178941183SFenghua Yu 14278941183SFenghua Yu /* 14378941183SFenghua Yu * print_results_cache: the cache results are stored in a file 14478941183SFenghua Yu * @filename: file that stores the results 14578941183SFenghua Yu * @bm_pid: child pid that runs benchmark 14678941183SFenghua Yu * @llc_value: perf miss value / 14778941183SFenghua Yu * llc occupancy value reported by resctrl FS 14878941183SFenghua Yu * 14978941183SFenghua Yu * Return: 0 on success. non-zero on failure. 15078941183SFenghua Yu */ 15178941183SFenghua Yu static int print_results_cache(char *filename, int bm_pid, 15278941183SFenghua Yu unsigned long llc_value) 15378941183SFenghua Yu { 15478941183SFenghua Yu FILE *fp; 15578941183SFenghua Yu 15678941183SFenghua Yu if (strcmp(filename, "stdio") == 0 || strcmp(filename, "stderr") == 0) { 15778941183SFenghua Yu printf("Pid: %d \t LLC_value: %lu\n", bm_pid, 15878941183SFenghua Yu llc_value); 15978941183SFenghua Yu } else { 16078941183SFenghua Yu fp = fopen(filename, "a"); 16178941183SFenghua Yu if (!fp) { 16278941183SFenghua Yu perror("Cannot open results file"); 16378941183SFenghua Yu 16478941183SFenghua Yu return errno; 16578941183SFenghua Yu } 16678941183SFenghua Yu fprintf(fp, "Pid: %d \t llc_value: %lu\n", bm_pid, llc_value); 16778941183SFenghua Yu fclose(fp); 16878941183SFenghua Yu } 16978941183SFenghua Yu 17078941183SFenghua Yu return 0; 17178941183SFenghua Yu } 17278941183SFenghua Yu 17378941183SFenghua Yu int measure_cache_vals(struct resctrl_val_param *param, int bm_pid) 17478941183SFenghua Yu { 175790bf585SFenghua Yu unsigned long llc_perf_miss = 0, llc_occu_resc = 0, llc_value = 0; 17678941183SFenghua Yu int ret; 17778941183SFenghua Yu 17878941183SFenghua Yu /* 179790bf585SFenghua Yu * Measure cache miss from perf. 180790bf585SFenghua Yu */ 18124286736SFenghua Yu if (!strncmp(param->resctrl_val, CAT_STR, sizeof(CAT_STR))) { 182790bf585SFenghua Yu ret = get_llc_perf(&llc_perf_miss); 183790bf585SFenghua Yu if (ret < 0) 184790bf585SFenghua Yu return ret; 185790bf585SFenghua Yu llc_value = llc_perf_miss; 186790bf585SFenghua Yu } 187790bf585SFenghua Yu 188790bf585SFenghua Yu /* 18978941183SFenghua Yu * Measure llc occupancy from resctrl. 19078941183SFenghua Yu */ 1912f320911SFenghua Yu if (!strncmp(param->resctrl_val, CMT_STR, sizeof(CMT_STR))) { 19278941183SFenghua Yu ret = get_llc_occu_resctrl(&llc_occu_resc); 19378941183SFenghua Yu if (ret < 0) 19478941183SFenghua Yu return ret; 19578941183SFenghua Yu llc_value = llc_occu_resc; 19678941183SFenghua Yu } 19778941183SFenghua Yu ret = print_results_cache(param->filename, bm_pid, llc_value); 19878941183SFenghua Yu if (ret) 19978941183SFenghua Yu return ret; 20078941183SFenghua Yu 20178941183SFenghua Yu return 0; 20278941183SFenghua Yu } 203790bf585SFenghua Yu 204790bf585SFenghua Yu /* 205790bf585SFenghua Yu * cache_val: execute benchmark and measure LLC occupancy resctrl 206790bf585SFenghua Yu * and perf cache miss for the benchmark 207790bf585SFenghua Yu * @param: parameters passed to cache_val() 208790bf585SFenghua Yu * 209790bf585SFenghua Yu * Return: 0 on success. non-zero on failure. 210790bf585SFenghua Yu */ 211790bf585SFenghua Yu int cat_val(struct resctrl_val_param *param) 212790bf585SFenghua Yu { 213326baed2SIlpo Järvinen int memflush = 1, operation = 0, ret = 0; 214790bf585SFenghua Yu char *resctrl_val = param->resctrl_val; 215790bf585SFenghua Yu pid_t bm_pid; 216790bf585SFenghua Yu 217790bf585SFenghua Yu if (strcmp(param->filename, "") == 0) 218790bf585SFenghua Yu sprintf(param->filename, "stdio"); 219790bf585SFenghua Yu 220790bf585SFenghua Yu bm_pid = getpid(); 221790bf585SFenghua Yu 222790bf585SFenghua Yu /* Taskset benchmark to specified cpu */ 223790bf585SFenghua Yu ret = taskset_benchmark(bm_pid, param->cpu_no); 224790bf585SFenghua Yu if (ret) 225790bf585SFenghua Yu return ret; 226790bf585SFenghua Yu 227790bf585SFenghua Yu /* Write benchmark to specified con_mon grp, mon_grp in resctrl FS*/ 228790bf585SFenghua Yu ret = write_bm_pid_to_resctrl(bm_pid, param->ctrlgrp, param->mongrp, 229790bf585SFenghua Yu resctrl_val); 230790bf585SFenghua Yu if (ret) 231790bf585SFenghua Yu return ret; 232790bf585SFenghua Yu 2339ce29d23SIlpo Järvinen if (!strncmp(resctrl_val, CAT_STR, sizeof(CAT_STR))) 2349ce29d23SIlpo Järvinen initialize_llc_perf(); 235790bf585SFenghua Yu 236790bf585SFenghua Yu /* Test runs until the callback setup() tells the test to stop. */ 237790bf585SFenghua Yu while (1) { 23824286736SFenghua Yu if (!strncmp(resctrl_val, CAT_STR, sizeof(CAT_STR))) { 239790bf585SFenghua Yu ret = param->setup(1, param); 240fa10366cSIlpo Järvinen if (ret == END_OF_TESTS) { 241790bf585SFenghua Yu ret = 0; 242790bf585SFenghua Yu break; 243790bf585SFenghua Yu } 244fa10366cSIlpo Järvinen if (ret < 0) 245fa10366cSIlpo Järvinen break; 246790bf585SFenghua Yu ret = reset_enable_llc_perf(bm_pid, param->cpu_no); 247790bf585SFenghua Yu if (ret) 248790bf585SFenghua Yu break; 249790bf585SFenghua Yu 250*7f3c980cSIlpo Järvinen if (run_fill_buf(param->span, memflush, operation, true)) { 251790bf585SFenghua Yu fprintf(stderr, "Error-running fill buffer\n"); 252790bf585SFenghua Yu ret = -1; 25351a0c3b7SIlpo Järvinen goto pe_close; 254790bf585SFenghua Yu } 255790bf585SFenghua Yu 256790bf585SFenghua Yu sleep(1); 257790bf585SFenghua Yu ret = measure_cache_vals(param, bm_pid); 258790bf585SFenghua Yu if (ret) 25951a0c3b7SIlpo Järvinen goto pe_close; 26051a0c3b7SIlpo Järvinen 26151a0c3b7SIlpo Järvinen close(fd_lm); 262790bf585SFenghua Yu } else { 263790bf585SFenghua Yu break; 264790bf585SFenghua Yu } 265790bf585SFenghua Yu } 266790bf585SFenghua Yu 267790bf585SFenghua Yu return ret; 26851a0c3b7SIlpo Järvinen 26951a0c3b7SIlpo Järvinen pe_close: 27051a0c3b7SIlpo Järvinen close(fd_lm); 27151a0c3b7SIlpo Järvinen return ret; 272790bf585SFenghua Yu } 27303216ed7SFenghua Yu 27403216ed7SFenghua Yu /* 27503216ed7SFenghua Yu * show_cache_info: show cache test result information 27603216ed7SFenghua Yu * @sum_llc_val: sum of LLC cache result data 27703216ed7SFenghua Yu * @no_of_bits: number of bits 27803216ed7SFenghua Yu * @cache_span: cache span in bytes for CMT or in lines for CAT 27903216ed7SFenghua Yu * @max_diff: max difference 28003216ed7SFenghua Yu * @max_diff_percent: max difference percentage 28103216ed7SFenghua Yu * @num_of_runs: number of runs 28203216ed7SFenghua Yu * @platform: show test information on this platform 28303216ed7SFenghua Yu * @cmt: CMT test or CAT test 28403216ed7SFenghua Yu * 28503216ed7SFenghua Yu * Return: 0 on success. non-zero on failure. 28603216ed7SFenghua Yu */ 28703216ed7SFenghua Yu int show_cache_info(unsigned long sum_llc_val, int no_of_bits, 28867a86643SIlpo Järvinen size_t cache_span, unsigned long max_diff, 28903216ed7SFenghua Yu unsigned long max_diff_percent, unsigned long num_of_runs, 29003216ed7SFenghua Yu bool platform, bool cmt) 29103216ed7SFenghua Yu { 29203216ed7SFenghua Yu unsigned long avg_llc_val = 0; 29303216ed7SFenghua Yu float diff_percent; 29403216ed7SFenghua Yu long avg_diff = 0; 29503216ed7SFenghua Yu int ret; 29603216ed7SFenghua Yu 29703216ed7SFenghua Yu avg_llc_val = sum_llc_val / (num_of_runs - 1); 29803216ed7SFenghua Yu avg_diff = (long)abs(cache_span - avg_llc_val); 29903216ed7SFenghua Yu diff_percent = ((float)cache_span - avg_llc_val) / cache_span * 100; 30003216ed7SFenghua Yu 30103216ed7SFenghua Yu ret = platform && abs((int)diff_percent) > max_diff_percent && 30203216ed7SFenghua Yu (cmt ? (abs(avg_diff) > max_diff) : true); 30303216ed7SFenghua Yu 304e7507478SFenghua Yu ksft_print_msg("%s Check cache miss rate within %d%%\n", 30503216ed7SFenghua Yu ret ? "Fail:" : "Pass:", max_diff_percent); 30603216ed7SFenghua Yu 30703216ed7SFenghua Yu ksft_print_msg("Percent diff=%d\n", abs((int)diff_percent)); 30803216ed7SFenghua Yu ksft_print_msg("Number of bits: %d\n", no_of_bits); 30903216ed7SFenghua Yu ksft_print_msg("Average LLC val: %lu\n", avg_llc_val); 31067a86643SIlpo Järvinen ksft_print_msg("Cache span (%s): %zu\n", cmt ? "bytes" : "lines", 31103216ed7SFenghua Yu cache_span); 31203216ed7SFenghua Yu 31303216ed7SFenghua Yu return ret; 31403216ed7SFenghua Yu } 315