162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci#include <stdint.h> 462306a36Sopenharmony_ci#include "resctrl.h" 562306a36Sopenharmony_ci 662306a36Sopenharmony_cistruct read_format { 762306a36Sopenharmony_ci __u64 nr; /* The number of events */ 862306a36Sopenharmony_ci struct { 962306a36Sopenharmony_ci __u64 value; /* The value of the event */ 1062306a36Sopenharmony_ci } values[2]; 1162306a36Sopenharmony_ci}; 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_cistatic struct perf_event_attr pea_llc_miss; 1462306a36Sopenharmony_cistatic struct read_format rf_cqm; 1562306a36Sopenharmony_cistatic int fd_lm; 1662306a36Sopenharmony_cichar llc_occup_path[1024]; 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistatic void initialize_perf_event_attr(void) 1962306a36Sopenharmony_ci{ 2062306a36Sopenharmony_ci pea_llc_miss.type = PERF_TYPE_HARDWARE; 2162306a36Sopenharmony_ci pea_llc_miss.size = sizeof(struct perf_event_attr); 2262306a36Sopenharmony_ci pea_llc_miss.read_format = PERF_FORMAT_GROUP; 2362306a36Sopenharmony_ci pea_llc_miss.exclude_kernel = 1; 2462306a36Sopenharmony_ci pea_llc_miss.exclude_hv = 1; 2562306a36Sopenharmony_ci pea_llc_miss.exclude_idle = 1; 2662306a36Sopenharmony_ci pea_llc_miss.exclude_callchain_kernel = 1; 2762306a36Sopenharmony_ci pea_llc_miss.inherit = 1; 2862306a36Sopenharmony_ci pea_llc_miss.exclude_guest = 1; 2962306a36Sopenharmony_ci pea_llc_miss.disabled = 1; 3062306a36Sopenharmony_ci} 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic void ioctl_perf_event_ioc_reset_enable(void) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci ioctl(fd_lm, PERF_EVENT_IOC_RESET, 0); 3562306a36Sopenharmony_ci ioctl(fd_lm, PERF_EVENT_IOC_ENABLE, 0); 3662306a36Sopenharmony_ci} 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic int perf_event_open_llc_miss(pid_t pid, int cpu_no) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci fd_lm = perf_event_open(&pea_llc_miss, pid, cpu_no, -1, 4162306a36Sopenharmony_ci PERF_FLAG_FD_CLOEXEC); 4262306a36Sopenharmony_ci if (fd_lm == -1) { 4362306a36Sopenharmony_ci perror("Error opening leader"); 4462306a36Sopenharmony_ci ctrlc_handler(0, NULL, NULL); 4562306a36Sopenharmony_ci return -1; 4662306a36Sopenharmony_ci } 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci return 0; 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic void initialize_llc_perf(void) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci memset(&pea_llc_miss, 0, sizeof(struct perf_event_attr)); 5462306a36Sopenharmony_ci memset(&rf_cqm, 0, sizeof(struct read_format)); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci /* Initialize perf_event_attr structures for HW_CACHE_MISSES */ 5762306a36Sopenharmony_ci initialize_perf_event_attr(); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci pea_llc_miss.config = PERF_COUNT_HW_CACHE_MISSES; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci rf_cqm.nr = 1; 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic int reset_enable_llc_perf(pid_t pid, int cpu_no) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci int ret = 0; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci ret = perf_event_open_llc_miss(pid, cpu_no); 6962306a36Sopenharmony_ci if (ret < 0) 7062306a36Sopenharmony_ci return ret; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci /* Start counters to log values */ 7362306a36Sopenharmony_ci ioctl_perf_event_ioc_reset_enable(); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci return 0; 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci/* 7962306a36Sopenharmony_ci * get_llc_perf: llc cache miss through perf events 8062306a36Sopenharmony_ci * @llc_perf_miss: LLC miss counter that is filled on success 8162306a36Sopenharmony_ci * 8262306a36Sopenharmony_ci * Perf events like HW_CACHE_MISSES could be used to validate number of 8362306a36Sopenharmony_ci * cache lines allocated. 8462306a36Sopenharmony_ci * 8562306a36Sopenharmony_ci * Return: =0 on success. <0 on failure. 8662306a36Sopenharmony_ci */ 8762306a36Sopenharmony_cistatic int get_llc_perf(unsigned long *llc_perf_miss) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci __u64 total_misses; 9062306a36Sopenharmony_ci int ret; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci /* Stop counters after one span to get miss rate */ 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci ioctl(fd_lm, PERF_EVENT_IOC_DISABLE, 0); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci ret = read(fd_lm, &rf_cqm, sizeof(struct read_format)); 9762306a36Sopenharmony_ci if (ret == -1) { 9862306a36Sopenharmony_ci perror("Could not get llc misses through perf"); 9962306a36Sopenharmony_ci return -1; 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci total_misses = rf_cqm.values[0].value; 10362306a36Sopenharmony_ci *llc_perf_miss = total_misses; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci return 0; 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci/* 10962306a36Sopenharmony_ci * Get LLC Occupancy as reported by RESCTRL FS 11062306a36Sopenharmony_ci * For CMT, 11162306a36Sopenharmony_ci * 1. If con_mon grp and mon grp given, then read from mon grp in 11262306a36Sopenharmony_ci * con_mon grp 11362306a36Sopenharmony_ci * 2. If only con_mon grp given, then read from con_mon grp 11462306a36Sopenharmony_ci * 3. If both not given, then read from root con_mon grp 11562306a36Sopenharmony_ci * For CAT, 11662306a36Sopenharmony_ci * 1. If con_mon grp given, then read from it 11762306a36Sopenharmony_ci * 2. If con_mon grp not given, then read from root con_mon grp 11862306a36Sopenharmony_ci * 11962306a36Sopenharmony_ci * Return: =0 on success. <0 on failure. 12062306a36Sopenharmony_ci */ 12162306a36Sopenharmony_cistatic int get_llc_occu_resctrl(unsigned long *llc_occupancy) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci FILE *fp; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci fp = fopen(llc_occup_path, "r"); 12662306a36Sopenharmony_ci if (!fp) { 12762306a36Sopenharmony_ci perror("Failed to open results file"); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci return errno; 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci if (fscanf(fp, "%lu", llc_occupancy) <= 0) { 13262306a36Sopenharmony_ci perror("Could not get llc occupancy"); 13362306a36Sopenharmony_ci fclose(fp); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci return -1; 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci fclose(fp); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci return 0; 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci/* 14362306a36Sopenharmony_ci * print_results_cache: the cache results are stored in a file 14462306a36Sopenharmony_ci * @filename: file that stores the results 14562306a36Sopenharmony_ci * @bm_pid: child pid that runs benchmark 14662306a36Sopenharmony_ci * @llc_value: perf miss value / 14762306a36Sopenharmony_ci * llc occupancy value reported by resctrl FS 14862306a36Sopenharmony_ci * 14962306a36Sopenharmony_ci * Return: 0 on success. non-zero on failure. 15062306a36Sopenharmony_ci */ 15162306a36Sopenharmony_cistatic int print_results_cache(char *filename, int bm_pid, 15262306a36Sopenharmony_ci unsigned long llc_value) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci FILE *fp; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci if (strcmp(filename, "stdio") == 0 || strcmp(filename, "stderr") == 0) { 15762306a36Sopenharmony_ci printf("Pid: %d \t LLC_value: %lu\n", bm_pid, 15862306a36Sopenharmony_ci llc_value); 15962306a36Sopenharmony_ci } else { 16062306a36Sopenharmony_ci fp = fopen(filename, "a"); 16162306a36Sopenharmony_ci if (!fp) { 16262306a36Sopenharmony_ci perror("Cannot open results file"); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci return errno; 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci fprintf(fp, "Pid: %d \t llc_value: %lu\n", bm_pid, llc_value); 16762306a36Sopenharmony_ci fclose(fp); 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci return 0; 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ciint measure_cache_vals(struct resctrl_val_param *param, int bm_pid) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci unsigned long llc_perf_miss = 0, llc_occu_resc = 0, llc_value = 0; 17662306a36Sopenharmony_ci int ret; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci /* 17962306a36Sopenharmony_ci * Measure cache miss from perf. 18062306a36Sopenharmony_ci */ 18162306a36Sopenharmony_ci if (!strncmp(param->resctrl_val, CAT_STR, sizeof(CAT_STR))) { 18262306a36Sopenharmony_ci ret = get_llc_perf(&llc_perf_miss); 18362306a36Sopenharmony_ci if (ret < 0) 18462306a36Sopenharmony_ci return ret; 18562306a36Sopenharmony_ci llc_value = llc_perf_miss; 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci /* 18962306a36Sopenharmony_ci * Measure llc occupancy from resctrl. 19062306a36Sopenharmony_ci */ 19162306a36Sopenharmony_ci if (!strncmp(param->resctrl_val, CMT_STR, sizeof(CMT_STR))) { 19262306a36Sopenharmony_ci ret = get_llc_occu_resctrl(&llc_occu_resc); 19362306a36Sopenharmony_ci if (ret < 0) 19462306a36Sopenharmony_ci return ret; 19562306a36Sopenharmony_ci llc_value = llc_occu_resc; 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci ret = print_results_cache(param->filename, bm_pid, llc_value); 19862306a36Sopenharmony_ci if (ret) 19962306a36Sopenharmony_ci return ret; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci return 0; 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci/* 20562306a36Sopenharmony_ci * cache_val: execute benchmark and measure LLC occupancy resctrl 20662306a36Sopenharmony_ci * and perf cache miss for the benchmark 20762306a36Sopenharmony_ci * @param: parameters passed to cache_val() 20862306a36Sopenharmony_ci * @span: buffer size for the benchmark 20962306a36Sopenharmony_ci * 21062306a36Sopenharmony_ci * Return: 0 on success. non-zero on failure. 21162306a36Sopenharmony_ci */ 21262306a36Sopenharmony_ciint cat_val(struct resctrl_val_param *param, size_t span) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci int memflush = 1, operation = 0, ret = 0; 21562306a36Sopenharmony_ci char *resctrl_val = param->resctrl_val; 21662306a36Sopenharmony_ci pid_t bm_pid; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci if (strcmp(param->filename, "") == 0) 21962306a36Sopenharmony_ci sprintf(param->filename, "stdio"); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci bm_pid = getpid(); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci /* Taskset benchmark to specified cpu */ 22462306a36Sopenharmony_ci ret = taskset_benchmark(bm_pid, param->cpu_no); 22562306a36Sopenharmony_ci if (ret) 22662306a36Sopenharmony_ci return ret; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci /* Write benchmark to specified con_mon grp, mon_grp in resctrl FS*/ 22962306a36Sopenharmony_ci ret = write_bm_pid_to_resctrl(bm_pid, param->ctrlgrp, param->mongrp, 23062306a36Sopenharmony_ci resctrl_val); 23162306a36Sopenharmony_ci if (ret) 23262306a36Sopenharmony_ci return ret; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci initialize_llc_perf(); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci /* Test runs until the callback setup() tells the test to stop. */ 23762306a36Sopenharmony_ci while (1) { 23862306a36Sopenharmony_ci ret = param->setup(param); 23962306a36Sopenharmony_ci if (ret == END_OF_TESTS) { 24062306a36Sopenharmony_ci ret = 0; 24162306a36Sopenharmony_ci break; 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci if (ret < 0) 24462306a36Sopenharmony_ci break; 24562306a36Sopenharmony_ci ret = reset_enable_llc_perf(bm_pid, param->cpu_no); 24662306a36Sopenharmony_ci if (ret) 24762306a36Sopenharmony_ci break; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci if (run_fill_buf(span, memflush, operation, true)) { 25062306a36Sopenharmony_ci fprintf(stderr, "Error-running fill buffer\n"); 25162306a36Sopenharmony_ci ret = -1; 25262306a36Sopenharmony_ci goto pe_close; 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci sleep(1); 25662306a36Sopenharmony_ci ret = measure_cache_vals(param, bm_pid); 25762306a36Sopenharmony_ci if (ret) 25862306a36Sopenharmony_ci goto pe_close; 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci return ret; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_cipe_close: 26462306a36Sopenharmony_ci close(fd_lm); 26562306a36Sopenharmony_ci return ret; 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci/* 26962306a36Sopenharmony_ci * show_cache_info: show cache test result information 27062306a36Sopenharmony_ci * @sum_llc_val: sum of LLC cache result data 27162306a36Sopenharmony_ci * @no_of_bits: number of bits 27262306a36Sopenharmony_ci * @cache_span: cache span in bytes for CMT or in lines for CAT 27362306a36Sopenharmony_ci * @max_diff: max difference 27462306a36Sopenharmony_ci * @max_diff_percent: max difference percentage 27562306a36Sopenharmony_ci * @num_of_runs: number of runs 27662306a36Sopenharmony_ci * @platform: show test information on this platform 27762306a36Sopenharmony_ci * @cmt: CMT test or CAT test 27862306a36Sopenharmony_ci * 27962306a36Sopenharmony_ci * Return: 0 on success. non-zero on failure. 28062306a36Sopenharmony_ci */ 28162306a36Sopenharmony_ciint show_cache_info(unsigned long sum_llc_val, int no_of_bits, 28262306a36Sopenharmony_ci size_t cache_span, unsigned long max_diff, 28362306a36Sopenharmony_ci unsigned long max_diff_percent, unsigned long num_of_runs, 28462306a36Sopenharmony_ci bool platform, bool cmt) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci unsigned long avg_llc_val = 0; 28762306a36Sopenharmony_ci float diff_percent; 28862306a36Sopenharmony_ci long avg_diff = 0; 28962306a36Sopenharmony_ci int ret; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci avg_llc_val = sum_llc_val / num_of_runs; 29262306a36Sopenharmony_ci avg_diff = (long)abs(cache_span - avg_llc_val); 29362306a36Sopenharmony_ci diff_percent = ((float)cache_span - avg_llc_val) / cache_span * 100; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci ret = platform && abs((int)diff_percent) > max_diff_percent && 29662306a36Sopenharmony_ci (cmt ? (abs(avg_diff) > max_diff) : true); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci ksft_print_msg("%s Check cache miss rate within %d%%\n", 29962306a36Sopenharmony_ci ret ? "Fail:" : "Pass:", max_diff_percent); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci ksft_print_msg("Percent diff=%d\n", abs((int)diff_percent)); 30262306a36Sopenharmony_ci ksft_print_msg("Number of bits: %d\n", no_of_bits); 30362306a36Sopenharmony_ci ksft_print_msg("Average LLC val: %lu\n", avg_llc_val); 30462306a36Sopenharmony_ci ksft_print_msg("Cache span (%s): %zu\n", cmt ? "bytes" : "lines", 30562306a36Sopenharmony_ci cache_span); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci return ret; 30862306a36Sopenharmony_ci} 309