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