1790bf585SFenghua Yu // SPDX-License-Identifier: GPL-2.0 2790bf585SFenghua Yu /* 3790bf585SFenghua Yu * Cache Allocation Technology (CAT) test 4790bf585SFenghua Yu * 5790bf585SFenghua Yu * Copyright (C) 2018 Intel Corporation 6790bf585SFenghua Yu * 7790bf585SFenghua Yu * Authors: 8790bf585SFenghua Yu * Sai Praneeth Prakhya <[email protected]>, 9790bf585SFenghua Yu * Fenghua Yu <[email protected]> 10790bf585SFenghua Yu */ 11790bf585SFenghua Yu #include "resctrl.h" 12790bf585SFenghua Yu #include <unistd.h> 13790bf585SFenghua Yu 14790bf585SFenghua Yu #define RESULT_FILE_NAME1 "result_cat1" 15790bf585SFenghua Yu #define RESULT_FILE_NAME2 "result_cat2" 16790bf585SFenghua Yu #define NUM_OF_RUNS 5 17790bf585SFenghua Yu #define MAX_DIFF_PERCENT 4 18790bf585SFenghua Yu #define MAX_DIFF 1000000 19790bf585SFenghua Yu 20790bf585SFenghua Yu /* 21790bf585SFenghua Yu * Change schemata. Write schemata to specified 22790bf585SFenghua Yu * con_mon grp, mon_grp in resctrl FS. 23790bf585SFenghua Yu * Run 5 times in order to get average values. 24790bf585SFenghua Yu */ 258ee592a6SIlpo Järvinen static int cat_setup(struct resctrl_val_param *p) 26790bf585SFenghua Yu { 27790bf585SFenghua Yu char schemata[64]; 28790bf585SFenghua Yu int ret = 0; 29790bf585SFenghua Yu 30790bf585SFenghua Yu /* Run NUM_OF_RUNS times */ 31790bf585SFenghua Yu if (p->num_of_runs >= NUM_OF_RUNS) 32fa10366cSIlpo Järvinen return END_OF_TESTS; 33790bf585SFenghua Yu 34790bf585SFenghua Yu if (p->num_of_runs == 0) { 35790bf585SFenghua Yu sprintf(schemata, "%lx", p->mask); 36790bf585SFenghua Yu ret = write_schemata(p->ctrlgrp, schemata, p->cpu_no, 37790bf585SFenghua Yu p->resctrl_val); 38790bf585SFenghua Yu } 39790bf585SFenghua Yu p->num_of_runs++; 40790bf585SFenghua Yu 41790bf585SFenghua Yu return ret; 42790bf585SFenghua Yu } 43790bf585SFenghua Yu 4433403bc7SIlpo Järvinen static int show_results_info(__u64 sum_llc_val, int no_of_bits, 455caf1b64SIlpo Järvinen unsigned long cache_span, unsigned long max_diff, 465caf1b64SIlpo Järvinen unsigned long max_diff_percent, unsigned long num_of_runs, 475caf1b64SIlpo Järvinen bool platform) 485caf1b64SIlpo Järvinen { 4933403bc7SIlpo Järvinen __u64 avg_llc_val = 0; 505caf1b64SIlpo Järvinen float diff_percent; 515caf1b64SIlpo Järvinen int ret; 525caf1b64SIlpo Järvinen 535caf1b64SIlpo Järvinen avg_llc_val = sum_llc_val / num_of_runs; 545caf1b64SIlpo Järvinen diff_percent = ((float)cache_span - avg_llc_val) / cache_span * 100; 555caf1b64SIlpo Järvinen 565caf1b64SIlpo Järvinen ret = platform && abs((int)diff_percent) > max_diff_percent; 575caf1b64SIlpo Järvinen 585caf1b64SIlpo Järvinen ksft_print_msg("%s Check cache miss rate within %lu%%\n", 595caf1b64SIlpo Järvinen ret ? "Fail:" : "Pass:", max_diff_percent); 605caf1b64SIlpo Järvinen 615caf1b64SIlpo Järvinen ksft_print_msg("Percent diff=%d\n", abs((int)diff_percent)); 625caf1b64SIlpo Järvinen 635caf1b64SIlpo Järvinen show_cache_info(no_of_bits, avg_llc_val, cache_span, true); 645caf1b64SIlpo Järvinen 655caf1b64SIlpo Järvinen return ret; 665caf1b64SIlpo Järvinen } 675caf1b64SIlpo Järvinen 68b1a901e0SIlpo Järvinen static int check_results(struct resctrl_val_param *param, size_t span) 69790bf585SFenghua Yu { 70790bf585SFenghua Yu char *token_array[8], temp[512]; 71790bf585SFenghua Yu int runs = 0, no_of_bits = 0; 7233403bc7SIlpo Järvinen __u64 sum_llc_perf_miss = 0; 73790bf585SFenghua Yu FILE *fp; 74790bf585SFenghua Yu 75ca2f4214SFenghua Yu ksft_print_msg("Checking for pass/fail\n"); 76790bf585SFenghua Yu fp = fopen(param->filename, "r"); 77790bf585SFenghua Yu if (!fp) { 78cc8ff7f5SIlpo Järvinen ksft_perror("Cannot open file"); 79790bf585SFenghua Yu 80c90fba60SIlpo Järvinen return -1; 81790bf585SFenghua Yu } 82790bf585SFenghua Yu 83790bf585SFenghua Yu while (fgets(temp, sizeof(temp), fp)) { 84790bf585SFenghua Yu char *token = strtok(temp, ":\t"); 85790bf585SFenghua Yu int fields = 0; 86790bf585SFenghua Yu 87790bf585SFenghua Yu while (token) { 88790bf585SFenghua Yu token_array[fields++] = token; 89790bf585SFenghua Yu token = strtok(NULL, ":\t"); 90790bf585SFenghua Yu } 91790bf585SFenghua Yu /* 92790bf585SFenghua Yu * Discard the first value which is inaccurate due to monitoring 93790bf585SFenghua Yu * setup transition phase. 94790bf585SFenghua Yu */ 95790bf585SFenghua Yu if (runs > 0) 9633403bc7SIlpo Järvinen sum_llc_perf_miss += strtoull(token_array[3], NULL, 0); 97790bf585SFenghua Yu runs++; 98790bf585SFenghua Yu } 99790bf585SFenghua Yu 100790bf585SFenghua Yu fclose(fp); 101790bf585SFenghua Yu no_of_bits = count_bits(param->mask); 102790bf585SFenghua Yu 1035caf1b64SIlpo Järvinen return show_results_info(sum_llc_perf_miss, no_of_bits, span / 64, 1043dad011bSIlpo Järvinen MAX_DIFF, MAX_DIFF_PERCENT, runs - 1, 1055caf1b64SIlpo Järvinen get_vendor() == ARCH_INTEL); 106790bf585SFenghua Yu } 107790bf585SFenghua Yu 108790bf585SFenghua Yu void cat_test_cleanup(void) 109790bf585SFenghua Yu { 110790bf585SFenghua Yu remove(RESULT_FILE_NAME1); 111790bf585SFenghua Yu remove(RESULT_FILE_NAME2); 112790bf585SFenghua Yu } 113790bf585SFenghua Yu 114*433f437bSIlpo Järvinen /* 115*433f437bSIlpo Järvinen * cat_test - Execute CAT benchmark and measure cache misses 116*433f437bSIlpo Järvinen * @param: Parameters passed to cat_test() 117*433f437bSIlpo Järvinen * @span: Buffer size for the benchmark 118*433f437bSIlpo Järvinen * 119*433f437bSIlpo Järvinen * Return: 0 when the test was run, < 0 on error. 120*433f437bSIlpo Järvinen */ 121*433f437bSIlpo Järvinen static int cat_test(struct resctrl_val_param *param, size_t span) 122*433f437bSIlpo Järvinen { 123*433f437bSIlpo Järvinen int memflush = 1, operation = 0, ret = 0; 124*433f437bSIlpo Järvinen char *resctrl_val = param->resctrl_val; 125*433f437bSIlpo Järvinen struct perf_event_read pe_read; 126*433f437bSIlpo Järvinen struct perf_event_attr pea; 127*433f437bSIlpo Järvinen pid_t bm_pid; 128*433f437bSIlpo Järvinen int pe_fd; 129*433f437bSIlpo Järvinen 130*433f437bSIlpo Järvinen if (strcmp(param->filename, "") == 0) 131*433f437bSIlpo Järvinen sprintf(param->filename, "stdio"); 132*433f437bSIlpo Järvinen 133*433f437bSIlpo Järvinen bm_pid = getpid(); 134*433f437bSIlpo Järvinen 135*433f437bSIlpo Järvinen /* Taskset benchmark to specified cpu */ 136*433f437bSIlpo Järvinen ret = taskset_benchmark(bm_pid, param->cpu_no); 137*433f437bSIlpo Järvinen if (ret) 138*433f437bSIlpo Järvinen return ret; 139*433f437bSIlpo Järvinen 140*433f437bSIlpo Järvinen /* Write benchmark to specified con_mon grp, mon_grp in resctrl FS*/ 141*433f437bSIlpo Järvinen ret = write_bm_pid_to_resctrl(bm_pid, param->ctrlgrp, param->mongrp, 142*433f437bSIlpo Järvinen resctrl_val); 143*433f437bSIlpo Järvinen if (ret) 144*433f437bSIlpo Järvinen return ret; 145*433f437bSIlpo Järvinen 146*433f437bSIlpo Järvinen perf_event_attr_initialize(&pea, PERF_COUNT_HW_CACHE_MISSES); 147*433f437bSIlpo Järvinen perf_event_initialize_read_format(&pe_read); 148*433f437bSIlpo Järvinen 149*433f437bSIlpo Järvinen /* Test runs until the callback setup() tells the test to stop. */ 150*433f437bSIlpo Järvinen while (1) { 151*433f437bSIlpo Järvinen ret = param->setup(param); 152*433f437bSIlpo Järvinen if (ret == END_OF_TESTS) { 153*433f437bSIlpo Järvinen ret = 0; 154*433f437bSIlpo Järvinen break; 155*433f437bSIlpo Järvinen } 156*433f437bSIlpo Järvinen if (ret < 0) 157*433f437bSIlpo Järvinen break; 158*433f437bSIlpo Järvinen pe_fd = perf_open(&pea, bm_pid, param->cpu_no); 159*433f437bSIlpo Järvinen if (pe_fd < 0) { 160*433f437bSIlpo Järvinen ret = -1; 161*433f437bSIlpo Järvinen break; 162*433f437bSIlpo Järvinen } 163*433f437bSIlpo Järvinen 164*433f437bSIlpo Järvinen if (run_fill_buf(span, memflush, operation, true)) { 165*433f437bSIlpo Järvinen fprintf(stderr, "Error-running fill buffer\n"); 166*433f437bSIlpo Järvinen ret = -1; 167*433f437bSIlpo Järvinen goto pe_close; 168*433f437bSIlpo Järvinen } 169*433f437bSIlpo Järvinen 170*433f437bSIlpo Järvinen sleep(1); 171*433f437bSIlpo Järvinen ret = perf_event_measure(pe_fd, &pe_read, param->filename, bm_pid); 172*433f437bSIlpo Järvinen if (ret) 173*433f437bSIlpo Järvinen goto pe_close; 174*433f437bSIlpo Järvinen 175*433f437bSIlpo Järvinen close(pe_fd); 176*433f437bSIlpo Järvinen } 177*433f437bSIlpo Järvinen 178*433f437bSIlpo Järvinen return ret; 179*433f437bSIlpo Järvinen 180*433f437bSIlpo Järvinen pe_close: 181*433f437bSIlpo Järvinen close(pe_fd); 182*433f437bSIlpo Järvinen return ret; 183*433f437bSIlpo Järvinen } 184*433f437bSIlpo Järvinen 185790bf585SFenghua Yu int cat_perf_miss_val(int cpu_no, int n, char *cache_type) 186790bf585SFenghua Yu { 187b6dfac94SIlpo Järvinen unsigned long full_cache_mask, long_mask; 188790bf585SFenghua Yu unsigned long l_mask, l_mask_1; 189790bf585SFenghua Yu int ret, pipefd[2], sibling_cpu_no; 19019e94a23SIlpo Järvinen unsigned long cache_total_size = 0; 19185b73447SIlpo Järvinen int count_of_bits; 192790bf585SFenghua Yu char pipe_message; 193b1a901e0SIlpo Järvinen size_t span; 194790bf585SFenghua Yu 195b6dfac94SIlpo Järvinen ret = get_full_cbm(cache_type, &full_cache_mask); 196b6dfac94SIlpo Järvinen if (ret) 197b6dfac94SIlpo Järvinen return ret; 198b6dfac94SIlpo Järvinen /* Get the largest contiguous exclusive portion of the cache */ 199b6dfac94SIlpo Järvinen ret = get_mask_no_shareable(cache_type, &long_mask); 200790bf585SFenghua Yu if (ret) 201790bf585SFenghua Yu return ret; 202790bf585SFenghua Yu 203790bf585SFenghua Yu /* Get L3/L2 cache size */ 20419e94a23SIlpo Järvinen ret = get_cache_size(cpu_no, cache_type, &cache_total_size); 205790bf585SFenghua Yu if (ret) 206790bf585SFenghua Yu return ret; 20719e94a23SIlpo Järvinen ksft_print_msg("Cache size :%lu\n", cache_total_size); 208790bf585SFenghua Yu 209790bf585SFenghua Yu count_of_bits = count_bits(long_mask); 210790bf585SFenghua Yu 21109a67934SFenghua Yu if (!n) 21209a67934SFenghua Yu n = count_of_bits / 2; 21309a67934SFenghua Yu 21409a67934SFenghua Yu if (n > count_of_bits - 1) { 215ca2f4214SFenghua Yu ksft_print_msg("Invalid input value for no_of_bits n!\n"); 216ca2f4214SFenghua Yu ksft_print_msg("Please enter value in range 1 to %d\n", 217790bf585SFenghua Yu count_of_bits - 1); 218790bf585SFenghua Yu return -1; 219790bf585SFenghua Yu } 220790bf585SFenghua Yu 221790bf585SFenghua Yu /* Get core id from same socket for running another thread */ 222790bf585SFenghua Yu sibling_cpu_no = get_core_sibling(cpu_no); 223790bf585SFenghua Yu if (sibling_cpu_no < 0) 224790bf585SFenghua Yu return -1; 225790bf585SFenghua Yu 226790bf585SFenghua Yu struct resctrl_val_param param = { 22724286736SFenghua Yu .resctrl_val = CAT_STR, 228790bf585SFenghua Yu .cpu_no = cpu_no, 229790bf585SFenghua Yu .setup = cat_setup, 230790bf585SFenghua Yu }; 231790bf585SFenghua Yu 232790bf585SFenghua Yu l_mask = long_mask >> n; 233790bf585SFenghua Yu l_mask_1 = ~l_mask & long_mask; 234790bf585SFenghua Yu 235790bf585SFenghua Yu /* Set param values for parent thread which will be allocated bitmask 236790bf585SFenghua Yu * with (max_bits - n) bits 237790bf585SFenghua Yu */ 238b6dfac94SIlpo Järvinen span = cache_portion_size(cache_total_size, l_mask, full_cache_mask); 239790bf585SFenghua Yu strcpy(param.ctrlgrp, "c2"); 240790bf585SFenghua Yu strcpy(param.mongrp, "m2"); 241790bf585SFenghua Yu strcpy(param.filename, RESULT_FILE_NAME2); 242790bf585SFenghua Yu param.mask = l_mask; 243790bf585SFenghua Yu param.num_of_runs = 0; 244790bf585SFenghua Yu 245790bf585SFenghua Yu if (pipe(pipefd)) { 246cc8ff7f5SIlpo Järvinen ksft_perror("Unable to create pipe"); 247c90fba60SIlpo Järvinen return -1; 248790bf585SFenghua Yu } 249790bf585SFenghua Yu 250a080b6e7SShaopeng Tan fflush(stdout); 251790bf585SFenghua Yu bm_pid = fork(); 252790bf585SFenghua Yu 253790bf585SFenghua Yu /* Set param values for child thread which will be allocated bitmask 254790bf585SFenghua Yu * with n bits 255790bf585SFenghua Yu */ 256790bf585SFenghua Yu if (bm_pid == 0) { 257790bf585SFenghua Yu param.mask = l_mask_1; 258790bf585SFenghua Yu strcpy(param.ctrlgrp, "c1"); 259790bf585SFenghua Yu strcpy(param.mongrp, "m1"); 260b6dfac94SIlpo Järvinen span = cache_portion_size(cache_total_size, l_mask_1, full_cache_mask); 261790bf585SFenghua Yu strcpy(param.filename, RESULT_FILE_NAME1); 262790bf585SFenghua Yu param.num_of_runs = 0; 263790bf585SFenghua Yu param.cpu_no = sibling_cpu_no; 264790bf585SFenghua Yu } 265790bf585SFenghua Yu 266790bf585SFenghua Yu remove(param.filename); 267790bf585SFenghua Yu 268*433f437bSIlpo Järvinen ret = cat_test(¶m, span); 26939e34ddcSShaopeng Tan if (ret == 0) 270b1a901e0SIlpo Järvinen ret = check_results(¶m, span); 271790bf585SFenghua Yu 272790bf585SFenghua Yu if (bm_pid == 0) { 273790bf585SFenghua Yu /* Tell parent that child is ready */ 274790bf585SFenghua Yu close(pipefd[0]); 275790bf585SFenghua Yu pipe_message = 1; 276790bf585SFenghua Yu if (write(pipefd[1], &pipe_message, sizeof(pipe_message)) < 27739e34ddcSShaopeng Tan sizeof(pipe_message)) 27839e34ddcSShaopeng Tan /* 27939e34ddcSShaopeng Tan * Just print the error message. 28039e34ddcSShaopeng Tan * Let while(1) run and wait for itself to be killed. 28139e34ddcSShaopeng Tan */ 282cc8ff7f5SIlpo Järvinen ksft_perror("Failed signaling parent process"); 283790bf585SFenghua Yu 284790bf585SFenghua Yu close(pipefd[1]); 285790bf585SFenghua Yu while (1) 286790bf585SFenghua Yu ; 287790bf585SFenghua Yu } else { 288790bf585SFenghua Yu /* Parent waits for child to be ready. */ 289790bf585SFenghua Yu close(pipefd[1]); 290790bf585SFenghua Yu pipe_message = 0; 291790bf585SFenghua Yu while (pipe_message != 1) { 292790bf585SFenghua Yu if (read(pipefd[0], &pipe_message, 293790bf585SFenghua Yu sizeof(pipe_message)) < sizeof(pipe_message)) { 294cc8ff7f5SIlpo Järvinen ksft_perror("Failed reading from child process"); 295790bf585SFenghua Yu break; 296790bf585SFenghua Yu } 297790bf585SFenghua Yu } 298790bf585SFenghua Yu close(pipefd[0]); 299790bf585SFenghua Yu kill(bm_pid, SIGKILL); 300790bf585SFenghua Yu } 301790bf585SFenghua Yu 302790bf585SFenghua Yu cat_test_cleanup(); 303790bf585SFenghua Yu 30439e34ddcSShaopeng Tan return ret; 305790bf585SFenghua Yu } 306