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 14*205de6ddSIlpo Järvinen #define RESULT_FILE_NAME "result_cat" 15790bf585SFenghua Yu #define NUM_OF_RUNS 5 16790bf585SFenghua Yu 17790bf585SFenghua Yu /* 18*205de6ddSIlpo Järvinen * Minimum difference in LLC misses between a test with n+1 bits CBM to the 19*205de6ddSIlpo Järvinen * test with n bits is MIN_DIFF_PERCENT_PER_BIT * (n - 1). With e.g. 5 vs 4 20*205de6ddSIlpo Järvinen * bits in the CBM mask, the minimum difference must be at least 21*205de6ddSIlpo Järvinen * MIN_DIFF_PERCENT_PER_BIT * (4 - 1) = 3 percent. 22*205de6ddSIlpo Järvinen * 23*205de6ddSIlpo Järvinen * The relationship between number of used CBM bits and difference in LLC 24*205de6ddSIlpo Järvinen * misses is not expected to be linear. With a small number of bits, the 25*205de6ddSIlpo Järvinen * margin is smaller than with larger number of bits. For selftest purposes, 26*205de6ddSIlpo Järvinen * however, linear approach is enough because ultimately only pass/fail 27*205de6ddSIlpo Järvinen * decision has to be made and distinction between strong and stronger 28*205de6ddSIlpo Järvinen * signal is irrelevant. 29790bf585SFenghua Yu */ 30*205de6ddSIlpo Järvinen #define MIN_DIFF_PERCENT_PER_BIT 1UL 31790bf585SFenghua Yu 3233403bc7SIlpo Järvinen static int show_results_info(__u64 sum_llc_val, int no_of_bits, 33*205de6ddSIlpo Järvinen unsigned long cache_span, 34*205de6ddSIlpo Järvinen unsigned long min_diff_percent, 35*205de6ddSIlpo Järvinen unsigned long num_of_runs, bool platform, 36*205de6ddSIlpo Järvinen __s64 *prev_avg_llc_val) 375caf1b64SIlpo Järvinen { 3833403bc7SIlpo Järvinen __u64 avg_llc_val = 0; 39*205de6ddSIlpo Järvinen float avg_diff; 40*205de6ddSIlpo Järvinen int ret = 0; 415caf1b64SIlpo Järvinen 425caf1b64SIlpo Järvinen avg_llc_val = sum_llc_val / num_of_runs; 43*205de6ddSIlpo Järvinen if (*prev_avg_llc_val) { 44*205de6ddSIlpo Järvinen float delta = (__s64)(avg_llc_val - *prev_avg_llc_val); 455caf1b64SIlpo Järvinen 46*205de6ddSIlpo Järvinen avg_diff = delta / *prev_avg_llc_val; 47*205de6ddSIlpo Järvinen ret = platform && (avg_diff * 100) < (float)min_diff_percent; 485caf1b64SIlpo Järvinen 49*205de6ddSIlpo Järvinen ksft_print_msg("%s Check cache miss rate changed more than %.1f%%\n", 50*205de6ddSIlpo Järvinen ret ? "Fail:" : "Pass:", (float)min_diff_percent); 515caf1b64SIlpo Järvinen 52*205de6ddSIlpo Järvinen ksft_print_msg("Percent diff=%.1f\n", avg_diff * 100); 53*205de6ddSIlpo Järvinen } 54*205de6ddSIlpo Järvinen *prev_avg_llc_val = avg_llc_val; 555caf1b64SIlpo Järvinen 565caf1b64SIlpo Järvinen show_cache_info(no_of_bits, avg_llc_val, cache_span, true); 575caf1b64SIlpo Järvinen 585caf1b64SIlpo Järvinen return ret; 595caf1b64SIlpo Järvinen } 605caf1b64SIlpo Järvinen 61*205de6ddSIlpo Järvinen /* Remove the highest bit from CBM */ 62*205de6ddSIlpo Järvinen static unsigned long next_mask(unsigned long current_mask) 63*205de6ddSIlpo Järvinen { 64*205de6ddSIlpo Järvinen return current_mask & (current_mask >> 1); 65*205de6ddSIlpo Järvinen } 66*205de6ddSIlpo Järvinen 67*205de6ddSIlpo Järvinen static int check_results(struct resctrl_val_param *param, const char *cache_type, 68*205de6ddSIlpo Järvinen unsigned long cache_total_size, unsigned long full_cache_mask, 69*205de6ddSIlpo Järvinen unsigned long current_mask) 70790bf585SFenghua Yu { 71790bf585SFenghua Yu char *token_array[8], temp[512]; 7233403bc7SIlpo Järvinen __u64 sum_llc_perf_miss = 0; 73*205de6ddSIlpo Järvinen __s64 prev_avg_llc_val = 0; 74*205de6ddSIlpo Järvinen unsigned long alloc_size; 75*205de6ddSIlpo Järvinen int runs = 0; 76*205de6ddSIlpo Järvinen int fail = 0; 77*205de6ddSIlpo Järvinen int ret; 78790bf585SFenghua Yu FILE *fp; 79790bf585SFenghua Yu 80ca2f4214SFenghua Yu ksft_print_msg("Checking for pass/fail\n"); 81790bf585SFenghua Yu fp = fopen(param->filename, "r"); 82790bf585SFenghua Yu if (!fp) { 83cc8ff7f5SIlpo Järvinen ksft_perror("Cannot open file"); 84790bf585SFenghua Yu 85c90fba60SIlpo Järvinen return -1; 86790bf585SFenghua Yu } 87790bf585SFenghua Yu 88790bf585SFenghua Yu while (fgets(temp, sizeof(temp), fp)) { 89790bf585SFenghua Yu char *token = strtok(temp, ":\t"); 90790bf585SFenghua Yu int fields = 0; 91*205de6ddSIlpo Järvinen int bits; 92790bf585SFenghua Yu 93790bf585SFenghua Yu while (token) { 94790bf585SFenghua Yu token_array[fields++] = token; 95790bf585SFenghua Yu token = strtok(NULL, ":\t"); 96790bf585SFenghua Yu } 97*205de6ddSIlpo Järvinen 9833403bc7SIlpo Järvinen sum_llc_perf_miss += strtoull(token_array[3], NULL, 0); 99790bf585SFenghua Yu runs++; 100*205de6ddSIlpo Järvinen 101*205de6ddSIlpo Järvinen if (runs < NUM_OF_RUNS) 102*205de6ddSIlpo Järvinen continue; 103*205de6ddSIlpo Järvinen 104*205de6ddSIlpo Järvinen if (!current_mask) { 105*205de6ddSIlpo Järvinen ksft_print_msg("Unexpected empty cache mask\n"); 106*205de6ddSIlpo Järvinen break; 107*205de6ddSIlpo Järvinen } 108*205de6ddSIlpo Järvinen 109*205de6ddSIlpo Järvinen alloc_size = cache_portion_size(cache_total_size, current_mask, full_cache_mask); 110*205de6ddSIlpo Järvinen 111*205de6ddSIlpo Järvinen bits = count_bits(current_mask); 112*205de6ddSIlpo Järvinen 113*205de6ddSIlpo Järvinen ret = show_results_info(sum_llc_perf_miss, bits, 114*205de6ddSIlpo Järvinen alloc_size / 64, 115*205de6ddSIlpo Järvinen MIN_DIFF_PERCENT_PER_BIT * (bits - 1), 116*205de6ddSIlpo Järvinen runs, get_vendor() == ARCH_INTEL, 117*205de6ddSIlpo Järvinen &prev_avg_llc_val); 118*205de6ddSIlpo Järvinen if (ret) 119*205de6ddSIlpo Järvinen fail = 1; 120*205de6ddSIlpo Järvinen 121*205de6ddSIlpo Järvinen runs = 0; 122*205de6ddSIlpo Järvinen sum_llc_perf_miss = 0; 123*205de6ddSIlpo Järvinen current_mask = next_mask(current_mask); 124790bf585SFenghua Yu } 125790bf585SFenghua Yu 126790bf585SFenghua Yu fclose(fp); 127790bf585SFenghua Yu 128*205de6ddSIlpo Järvinen return fail; 129790bf585SFenghua Yu } 130790bf585SFenghua Yu 131790bf585SFenghua Yu void cat_test_cleanup(void) 132790bf585SFenghua Yu { 133*205de6ddSIlpo Järvinen remove(RESULT_FILE_NAME); 134790bf585SFenghua Yu } 135790bf585SFenghua Yu 136433f437bSIlpo Järvinen /* 137433f437bSIlpo Järvinen * cat_test - Execute CAT benchmark and measure cache misses 138433f437bSIlpo Järvinen * @param: Parameters passed to cat_test() 139433f437bSIlpo Järvinen * @span: Buffer size for the benchmark 140*205de6ddSIlpo Järvinen * @current_mask Start mask for the first iteration 141*205de6ddSIlpo Järvinen * 142*205de6ddSIlpo Järvinen * Run CAT selftest by varying the allocated cache portion and comparing the 143*205de6ddSIlpo Järvinen * impact on cache misses (the result analysis is done in check_results() 144*205de6ddSIlpo Järvinen * and show_results_info(), not in this function). 145*205de6ddSIlpo Järvinen * 146*205de6ddSIlpo Järvinen * One bit is removed from the CAT allocation bit mask (in current_mask) for 147*205de6ddSIlpo Järvinen * each subsequent test which keeps reducing the size of the allocated cache 148*205de6ddSIlpo Järvinen * portion. A single test flushes the buffer, reads it to warm up the cache, 149*205de6ddSIlpo Järvinen * and reads the buffer again. The cache misses are measured during the last 150*205de6ddSIlpo Järvinen * read pass. 151433f437bSIlpo Järvinen * 152433f437bSIlpo Järvinen * Return: 0 when the test was run, < 0 on error. 153433f437bSIlpo Järvinen */ 154*205de6ddSIlpo Järvinen static int cat_test(struct resctrl_val_param *param, size_t span, unsigned long current_mask) 155433f437bSIlpo Järvinen { 156433f437bSIlpo Järvinen char *resctrl_val = param->resctrl_val; 157433f437bSIlpo Järvinen struct perf_event_read pe_read; 158433f437bSIlpo Järvinen struct perf_event_attr pea; 159*205de6ddSIlpo Järvinen unsigned char *buf; 160*205de6ddSIlpo Järvinen char schemata[64]; 161*205de6ddSIlpo Järvinen int ret, i, pe_fd; 162433f437bSIlpo Järvinen pid_t bm_pid; 163433f437bSIlpo Järvinen 164433f437bSIlpo Järvinen if (strcmp(param->filename, "") == 0) 165433f437bSIlpo Järvinen sprintf(param->filename, "stdio"); 166433f437bSIlpo Järvinen 167433f437bSIlpo Järvinen bm_pid = getpid(); 168433f437bSIlpo Järvinen 169433f437bSIlpo Järvinen /* Taskset benchmark to specified cpu */ 170433f437bSIlpo Järvinen ret = taskset_benchmark(bm_pid, param->cpu_no); 171433f437bSIlpo Järvinen if (ret) 172433f437bSIlpo Järvinen return ret; 173433f437bSIlpo Järvinen 174433f437bSIlpo Järvinen /* Write benchmark to specified con_mon grp, mon_grp in resctrl FS*/ 175433f437bSIlpo Järvinen ret = write_bm_pid_to_resctrl(bm_pid, param->ctrlgrp, param->mongrp, 176433f437bSIlpo Järvinen resctrl_val); 177433f437bSIlpo Järvinen if (ret) 178433f437bSIlpo Järvinen return ret; 179433f437bSIlpo Järvinen 180433f437bSIlpo Järvinen perf_event_attr_initialize(&pea, PERF_COUNT_HW_CACHE_MISSES); 181433f437bSIlpo Järvinen perf_event_initialize_read_format(&pe_read); 1822892731eSIlpo Järvinen pe_fd = perf_open(&pea, bm_pid, param->cpu_no); 1832892731eSIlpo Järvinen if (pe_fd < 0) 1842892731eSIlpo Järvinen return pe_fd; 185433f437bSIlpo Järvinen 186*205de6ddSIlpo Järvinen buf = alloc_buffer(span, 1); 187*205de6ddSIlpo Järvinen if (!buf) { 188433f437bSIlpo Järvinen ret = -1; 189433f437bSIlpo Järvinen goto pe_close; 190433f437bSIlpo Järvinen } 191433f437bSIlpo Järvinen 192*205de6ddSIlpo Järvinen while (current_mask) { 193*205de6ddSIlpo Järvinen snprintf(schemata, sizeof(schemata), "%lx", param->mask & ~current_mask); 194*205de6ddSIlpo Järvinen ret = write_schemata("", schemata, param->cpu_no, param->resctrl_val); 195*205de6ddSIlpo Järvinen if (ret) 196*205de6ddSIlpo Järvinen goto free_buf; 197*205de6ddSIlpo Järvinen snprintf(schemata, sizeof(schemata), "%lx", current_mask); 198*205de6ddSIlpo Järvinen ret = write_schemata(param->ctrlgrp, schemata, param->cpu_no, param->resctrl_val); 199*205de6ddSIlpo Järvinen if (ret) 200*205de6ddSIlpo Järvinen goto free_buf; 201*205de6ddSIlpo Järvinen 202*205de6ddSIlpo Järvinen for (i = 0; i < NUM_OF_RUNS; i++) { 203*205de6ddSIlpo Järvinen mem_flush(buf, span); 204*205de6ddSIlpo Järvinen fill_cache_read(buf, span, true); 205*205de6ddSIlpo Järvinen 206*205de6ddSIlpo Järvinen ret = perf_event_reset_enable(pe_fd); 207*205de6ddSIlpo Järvinen if (ret) 208*205de6ddSIlpo Järvinen goto free_buf; 209*205de6ddSIlpo Järvinen 210*205de6ddSIlpo Järvinen fill_cache_read(buf, span, true); 211*205de6ddSIlpo Järvinen 212433f437bSIlpo Järvinen ret = perf_event_measure(pe_fd, &pe_read, param->filename, bm_pid); 213433f437bSIlpo Järvinen if (ret) 214*205de6ddSIlpo Järvinen goto free_buf; 215*205de6ddSIlpo Järvinen } 216*205de6ddSIlpo Järvinen current_mask = next_mask(current_mask); 217433f437bSIlpo Järvinen } 218433f437bSIlpo Järvinen 219*205de6ddSIlpo Järvinen free_buf: 220*205de6ddSIlpo Järvinen free(buf); 221433f437bSIlpo Järvinen pe_close: 222433f437bSIlpo Järvinen close(pe_fd); 223*205de6ddSIlpo Järvinen 224433f437bSIlpo Järvinen return ret; 225433f437bSIlpo Järvinen } 226433f437bSIlpo Järvinen 227790bf585SFenghua Yu int cat_perf_miss_val(int cpu_no, int n, char *cache_type) 228790bf585SFenghua Yu { 229*205de6ddSIlpo Järvinen unsigned long long_mask, start_mask, full_cache_mask; 23019e94a23SIlpo Järvinen unsigned long cache_total_size = 0; 231*205de6ddSIlpo Järvinen unsigned int start; 23285b73447SIlpo Järvinen int count_of_bits; 233b1a901e0SIlpo Järvinen size_t span; 234*205de6ddSIlpo Järvinen int ret; 235790bf585SFenghua Yu 236b6dfac94SIlpo Järvinen ret = get_full_cbm(cache_type, &full_cache_mask); 237b6dfac94SIlpo Järvinen if (ret) 238b6dfac94SIlpo Järvinen return ret; 239b6dfac94SIlpo Järvinen /* Get the largest contiguous exclusive portion of the cache */ 240b6dfac94SIlpo Järvinen ret = get_mask_no_shareable(cache_type, &long_mask); 241790bf585SFenghua Yu if (ret) 242790bf585SFenghua Yu return ret; 243790bf585SFenghua Yu 244790bf585SFenghua Yu /* Get L3/L2 cache size */ 24519e94a23SIlpo Järvinen ret = get_cache_size(cpu_no, cache_type, &cache_total_size); 246790bf585SFenghua Yu if (ret) 247790bf585SFenghua Yu return ret; 24819e94a23SIlpo Järvinen ksft_print_msg("Cache size :%lu\n", cache_total_size); 249790bf585SFenghua Yu 250*205de6ddSIlpo Järvinen count_of_bits = count_contiguous_bits(long_mask, &start); 251790bf585SFenghua Yu 25209a67934SFenghua Yu if (!n) 25309a67934SFenghua Yu n = count_of_bits / 2; 25409a67934SFenghua Yu 25509a67934SFenghua Yu if (n > count_of_bits - 1) { 256ca2f4214SFenghua Yu ksft_print_msg("Invalid input value for no_of_bits n!\n"); 257ca2f4214SFenghua Yu ksft_print_msg("Please enter value in range 1 to %d\n", 258790bf585SFenghua Yu count_of_bits - 1); 259790bf585SFenghua Yu return -1; 260790bf585SFenghua Yu } 261*205de6ddSIlpo Järvinen start_mask = create_bit_mask(start, n); 262790bf585SFenghua Yu 263790bf585SFenghua Yu struct resctrl_val_param param = { 26424286736SFenghua Yu .resctrl_val = CAT_STR, 265790bf585SFenghua Yu .cpu_no = cpu_no, 266*205de6ddSIlpo Järvinen .ctrlgrp = "c1", 267*205de6ddSIlpo Järvinen .filename = RESULT_FILE_NAME, 268*205de6ddSIlpo Järvinen .num_of_runs = 0, 269790bf585SFenghua Yu }; 270*205de6ddSIlpo Järvinen param.mask = long_mask; 271*205de6ddSIlpo Järvinen span = cache_portion_size(cache_total_size, start_mask, full_cache_mask); 272790bf585SFenghua Yu 273790bf585SFenghua Yu remove(param.filename); 274790bf585SFenghua Yu 275*205de6ddSIlpo Järvinen ret = cat_test(¶m, span, start_mask); 276*205de6ddSIlpo Järvinen if (ret) 277*205de6ddSIlpo Järvinen goto out; 278790bf585SFenghua Yu 279*205de6ddSIlpo Järvinen ret = check_results(¶m, cache_type, cache_total_size, full_cache_mask, start_mask); 280*205de6ddSIlpo Järvinen out: 281790bf585SFenghua Yu cat_test_cleanup(); 282790bf585SFenghua Yu 28339e34ddcSShaopeng Tan return ret; 284790bf585SFenghua Yu } 285