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 static int count_of_bits; 21 static char cbm_mask[256]; 22 static unsigned long long_mask; 23 static 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 END_OF_TESTS; 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 int check_results(struct resctrl_val_param *param) 56 { 57 char *token_array[8], temp[512]; 58 unsigned long sum_llc_perf_miss = 0; 59 int runs = 0, no_of_bits = 0; 60 FILE *fp; 61 62 ksft_print_msg("Checking for pass/fail\n"); 63 fp = fopen(param->filename, "r"); 64 if (!fp) { 65 perror("# Cannot open file"); 66 67 return errno; 68 } 69 70 while (fgets(temp, sizeof(temp), fp)) { 71 char *token = strtok(temp, ":\t"); 72 int fields = 0; 73 74 while (token) { 75 token_array[fields++] = token; 76 token = strtok(NULL, ":\t"); 77 } 78 /* 79 * Discard the first value which is inaccurate due to monitoring 80 * setup transition phase. 81 */ 82 if (runs > 0) 83 sum_llc_perf_miss += strtoul(token_array[3], NULL, 0); 84 runs++; 85 } 86 87 fclose(fp); 88 no_of_bits = count_bits(param->mask); 89 90 return show_cache_info(sum_llc_perf_miss, no_of_bits, param->span / 64, 91 MAX_DIFF, MAX_DIFF_PERCENT, NUM_OF_RUNS, 92 get_vendor() == ARCH_INTEL, false); 93 } 94 95 void cat_test_cleanup(void) 96 { 97 remove(RESULT_FILE_NAME1); 98 remove(RESULT_FILE_NAME2); 99 } 100 101 int cat_perf_miss_val(int cpu_no, int n, char *cache_type) 102 { 103 unsigned long l_mask, l_mask_1; 104 int ret, pipefd[2], sibling_cpu_no; 105 char pipe_message; 106 107 cache_size = 0; 108 109 ret = remount_resctrlfs(true); 110 if (ret) 111 return ret; 112 113 /* Get default cbm mask for L3/L2 cache */ 114 ret = get_cbm_mask(cache_type, cbm_mask); 115 if (ret) 116 return ret; 117 118 long_mask = strtoul(cbm_mask, NULL, 16); 119 120 /* Get L3/L2 cache size */ 121 ret = get_cache_size(cpu_no, cache_type, &cache_size); 122 if (ret) 123 return ret; 124 ksft_print_msg("Cache size :%lu\n", cache_size); 125 126 /* Get max number of bits from default-cabm mask */ 127 count_of_bits = count_bits(long_mask); 128 129 if (!n) 130 n = count_of_bits / 2; 131 132 if (n > count_of_bits - 1) { 133 ksft_print_msg("Invalid input value for no_of_bits n!\n"); 134 ksft_print_msg("Please enter value in range 1 to %d\n", 135 count_of_bits - 1); 136 return -1; 137 } 138 139 /* Get core id from same socket for running another thread */ 140 sibling_cpu_no = get_core_sibling(cpu_no); 141 if (sibling_cpu_no < 0) 142 return -1; 143 144 struct resctrl_val_param param = { 145 .resctrl_val = CAT_STR, 146 .cpu_no = cpu_no, 147 .mum_resctrlfs = false, 148 .setup = cat_setup, 149 }; 150 151 l_mask = long_mask >> n; 152 l_mask_1 = ~l_mask & long_mask; 153 154 /* Set param values for parent thread which will be allocated bitmask 155 * with (max_bits - n) bits 156 */ 157 param.span = cache_size * (count_of_bits - n) / count_of_bits; 158 strcpy(param.ctrlgrp, "c2"); 159 strcpy(param.mongrp, "m2"); 160 strcpy(param.filename, RESULT_FILE_NAME2); 161 param.mask = l_mask; 162 param.num_of_runs = 0; 163 164 if (pipe(pipefd)) { 165 perror("# Unable to create pipe"); 166 return errno; 167 } 168 169 fflush(stdout); 170 bm_pid = fork(); 171 172 /* Set param values for child thread which will be allocated bitmask 173 * with n bits 174 */ 175 if (bm_pid == 0) { 176 param.mask = l_mask_1; 177 strcpy(param.ctrlgrp, "c1"); 178 strcpy(param.mongrp, "m1"); 179 param.span = cache_size * n / count_of_bits; 180 strcpy(param.filename, RESULT_FILE_NAME1); 181 param.num_of_runs = 0; 182 param.cpu_no = sibling_cpu_no; 183 } else { 184 ret = signal_handler_register(); 185 if (ret) { 186 kill(bm_pid, SIGKILL); 187 goto out; 188 } 189 } 190 191 remove(param.filename); 192 193 ret = cat_val(¶m); 194 if (ret == 0) 195 ret = check_results(¶m); 196 197 if (bm_pid == 0) { 198 /* Tell parent that child is ready */ 199 close(pipefd[0]); 200 pipe_message = 1; 201 if (write(pipefd[1], &pipe_message, sizeof(pipe_message)) < 202 sizeof(pipe_message)) 203 /* 204 * Just print the error message. 205 * Let while(1) run and wait for itself to be killed. 206 */ 207 perror("# failed signaling parent process"); 208 209 close(pipefd[1]); 210 while (1) 211 ; 212 } else { 213 /* Parent waits for child to be ready. */ 214 close(pipefd[1]); 215 pipe_message = 0; 216 while (pipe_message != 1) { 217 if (read(pipefd[0], &pipe_message, 218 sizeof(pipe_message)) < sizeof(pipe_message)) { 219 perror("# failed reading from child process"); 220 break; 221 } 222 } 223 close(pipefd[0]); 224 kill(bm_pid, SIGKILL); 225 signal_handler_unregister(); 226 } 227 228 out: 229 cat_test_cleanup(); 230 if (bm_pid) 231 umount_resctrlfs(); 232 233 return ret; 234 } 235