1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Cache Allocation Technology (CAT) test 4 * 5 * Copyright (C) 2018 Intel Corporation 6 * 7 * Authors: 8 * Sai Praneeth Prakhya <sai.praneeth.prakhya@intel.com>, 9 * Fenghua Yu <fenghua.yu@intel.com> 10 */ 11 #include "resctrl.h" 12 #include <unistd.h> 13 14 #define RESULT_FILE_NAME1 "result_cat1" 15 #define RESULT_FILE_NAME2 "result_cat2" 16 #define NUM_OF_RUNS 5 17 #define MAX_DIFF_PERCENT 4 18 #define MAX_DIFF 1000000 19 20 int count_of_bits; 21 char cbm_mask[256]; 22 unsigned long long_mask; 23 unsigned long cache_size; 24 25 /* 26 * Change schemata. Write schemata to specified 27 * con_mon grp, mon_grp in resctrl FS. 28 * Run 5 times in order to get average values. 29 */ 30 static int cat_setup(int num, ...) 31 { 32 struct resctrl_val_param *p; 33 char schemata[64]; 34 va_list param; 35 int ret = 0; 36 37 va_start(param, num); 38 p = va_arg(param, struct resctrl_val_param *); 39 va_end(param); 40 41 /* Run NUM_OF_RUNS times */ 42 if (p->num_of_runs >= NUM_OF_RUNS) 43 return -1; 44 45 if (p->num_of_runs == 0) { 46 sprintf(schemata, "%lx", p->mask); 47 ret = write_schemata(p->ctrlgrp, schemata, p->cpu_no, 48 p->resctrl_val); 49 } 50 p->num_of_runs++; 51 52 return ret; 53 } 54 55 static void show_cache_info(unsigned long sum_llc_perf_miss, int no_of_bits, 56 unsigned long span) 57 { 58 unsigned long allocated_cache_lines = span / 64; 59 unsigned long avg_llc_perf_miss = 0; 60 float diff_percent; 61 62 avg_llc_perf_miss = sum_llc_perf_miss / (NUM_OF_RUNS - 1); 63 diff_percent = ((float)allocated_cache_lines - avg_llc_perf_miss) / 64 allocated_cache_lines * 100; 65 66 printf("%sok CAT: cache miss rate within %d%%\n", 67 !is_amd && abs((int)diff_percent) > MAX_DIFF_PERCENT ? 68 "not " : "", MAX_DIFF_PERCENT); 69 tests_run++; 70 printf("# Percent diff=%d\n", abs((int)diff_percent)); 71 printf("# Number of bits: %d\n", no_of_bits); 72 printf("# Avg_llc_perf_miss: %lu\n", avg_llc_perf_miss); 73 printf("# Allocated cache lines: %lu\n", allocated_cache_lines); 74 } 75 76 static int check_results(struct resctrl_val_param *param) 77 { 78 char *token_array[8], temp[512]; 79 unsigned long sum_llc_perf_miss = 0; 80 int runs = 0, no_of_bits = 0; 81 FILE *fp; 82 83 printf("# Checking for pass/fail\n"); 84 fp = fopen(param->filename, "r"); 85 if (!fp) { 86 perror("# Cannot open file"); 87 88 return errno; 89 } 90 91 while (fgets(temp, sizeof(temp), fp)) { 92 char *token = strtok(temp, ":\t"); 93 int fields = 0; 94 95 while (token) { 96 token_array[fields++] = token; 97 token = strtok(NULL, ":\t"); 98 } 99 /* 100 * Discard the first value which is inaccurate due to monitoring 101 * setup transition phase. 102 */ 103 if (runs > 0) 104 sum_llc_perf_miss += strtoul(token_array[3], NULL, 0); 105 runs++; 106 } 107 108 fclose(fp); 109 no_of_bits = count_bits(param->mask); 110 111 show_cache_info(sum_llc_perf_miss, no_of_bits, param->span); 112 113 return 0; 114 } 115 116 void cat_test_cleanup(void) 117 { 118 remove(RESULT_FILE_NAME1); 119 remove(RESULT_FILE_NAME2); 120 } 121 122 int cat_perf_miss_val(int cpu_no, int n, char *cache_type) 123 { 124 unsigned long l_mask, l_mask_1; 125 int ret, pipefd[2], sibling_cpu_no; 126 char pipe_message; 127 pid_t bm_pid; 128 129 cache_size = 0; 130 131 ret = remount_resctrlfs(true); 132 if (ret) 133 return ret; 134 135 if (!validate_resctrl_feature_request("cat")) 136 return -1; 137 138 /* Get default cbm mask for L3/L2 cache */ 139 ret = get_cbm_mask(cache_type); 140 if (ret) 141 return ret; 142 143 long_mask = strtoul(cbm_mask, NULL, 16); 144 145 /* Get L3/L2 cache size */ 146 ret = get_cache_size(cpu_no, cache_type, &cache_size); 147 if (ret) 148 return ret; 149 printf("cache size :%lu\n", cache_size); 150 151 /* Get max number of bits from default-cabm mask */ 152 count_of_bits = count_bits(long_mask); 153 154 if (n < 1 || n > count_of_bits - 1) { 155 printf("Invalid input value for no_of_bits n!\n"); 156 printf("Please Enter value in range 1 to %d\n", 157 count_of_bits - 1); 158 return -1; 159 } 160 161 /* Get core id from same socket for running another thread */ 162 sibling_cpu_no = get_core_sibling(cpu_no); 163 if (sibling_cpu_no < 0) 164 return -1; 165 166 struct resctrl_val_param param = { 167 .resctrl_val = "cat", 168 .cpu_no = cpu_no, 169 .mum_resctrlfs = 0, 170 .setup = cat_setup, 171 }; 172 173 l_mask = long_mask >> n; 174 l_mask_1 = ~l_mask & long_mask; 175 176 /* Set param values for parent thread which will be allocated bitmask 177 * with (max_bits - n) bits 178 */ 179 param.span = cache_size * (count_of_bits - n) / count_of_bits; 180 strcpy(param.ctrlgrp, "c2"); 181 strcpy(param.mongrp, "m2"); 182 strcpy(param.filename, RESULT_FILE_NAME2); 183 param.mask = l_mask; 184 param.num_of_runs = 0; 185 186 if (pipe(pipefd)) { 187 perror("# Unable to create pipe"); 188 return errno; 189 } 190 191 bm_pid = fork(); 192 193 /* Set param values for child thread which will be allocated bitmask 194 * with n bits 195 */ 196 if (bm_pid == 0) { 197 param.mask = l_mask_1; 198 strcpy(param.ctrlgrp, "c1"); 199 strcpy(param.mongrp, "m1"); 200 param.span = cache_size * n / count_of_bits; 201 strcpy(param.filename, RESULT_FILE_NAME1); 202 param.num_of_runs = 0; 203 param.cpu_no = sibling_cpu_no; 204 } 205 206 remove(param.filename); 207 208 ret = cat_val(¶m); 209 if (ret) 210 return ret; 211 212 ret = check_results(¶m); 213 if (ret) 214 return ret; 215 216 if (bm_pid == 0) { 217 /* Tell parent that child is ready */ 218 close(pipefd[0]); 219 pipe_message = 1; 220 if (write(pipefd[1], &pipe_message, sizeof(pipe_message)) < 221 sizeof(pipe_message)) { 222 close(pipefd[1]); 223 perror("# failed signaling parent process"); 224 return errno; 225 } 226 227 close(pipefd[1]); 228 while (1) 229 ; 230 } else { 231 /* Parent waits for child to be ready. */ 232 close(pipefd[1]); 233 pipe_message = 0; 234 while (pipe_message != 1) { 235 if (read(pipefd[0], &pipe_message, 236 sizeof(pipe_message)) < sizeof(pipe_message)) { 237 perror("# failed reading from child process"); 238 break; 239 } 240 } 241 close(pipefd[0]); 242 kill(bm_pid, SIGKILL); 243 } 244 245 cat_test_cleanup(); 246 if (bm_pid) 247 umount_resctrlfs(); 248 249 return 0; 250 } 251