1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Cache Monitoring Technology (CMT) 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_NAME "result_cmt" 15 #define NUM_OF_RUNS 5 16 #define MAX_DIFF 2000000 17 #define MAX_DIFF_PERCENT 15 18 19 #define CON_MON_LCC_OCCUP_PATH \ 20 "%s/%s/mon_data/mon_L3_%02d/llc_occupancy" 21 22 /* 23 * Initialize capacity bitmasks (CBMs) of: 24 * - control group being tested per test parameters, 25 * - default resource group as inverse of control group being tested to prevent 26 * other tasks from interfering with test, 27 * - L2 resource of control group being tested to minimize allocations into 28 * L2 if possible to better predict L3 occupancy. 29 */ 30 static int cmt_init(const struct resctrl_test *test, 31 const struct user_params *uparams, 32 const struct resctrl_val_param *param, int domain_id) 33 { 34 unsigned long full_mask; 35 char schemata[64]; 36 int ret; 37 38 sprintf(llc_occup_path, CON_MON_LCC_OCCUP_PATH, RESCTRL_PATH, 39 param->ctrlgrp, domain_id); 40 41 ret = get_full_cbm(test->resource, &full_mask); 42 if (ret) 43 return ret; 44 45 snprintf(schemata, sizeof(schemata), "%lx", ~param->mask & full_mask); 46 ret = write_schemata("", schemata, uparams->cpu, test->resource); 47 if (ret) 48 return ret; 49 50 snprintf(schemata, sizeof(schemata), "%lx", param->mask); 51 ret = write_schemata(param->ctrlgrp, schemata, uparams->cpu, test->resource); 52 if (ret) 53 return ret; 54 55 return minimize_l2_occupancy(test, uparams, param); 56 } 57 58 static int cmt_setup(const struct resctrl_test *test, 59 const struct user_params *uparams, 60 struct resctrl_val_param *p) 61 { 62 /* Run NUM_OF_RUNS times */ 63 if (p->num_of_runs >= NUM_OF_RUNS) 64 return END_OF_TESTS; 65 66 p->num_of_runs++; 67 68 return 0; 69 } 70 71 static int cmt_measure(const struct user_params *uparams, 72 struct resctrl_val_param *param, pid_t bm_pid) 73 { 74 sleep(1); 75 return measure_llc_resctrl(param->filename, bm_pid); 76 } 77 78 static int show_results_info(unsigned long sum_llc_val, int no_of_bits, 79 unsigned long cache_span, unsigned long max_diff, 80 unsigned long max_diff_percent, unsigned long num_of_runs, 81 bool platform) 82 { 83 unsigned long avg_llc_val = 0; 84 float diff_percent; 85 long avg_diff = 0; 86 int ret; 87 88 avg_llc_val = sum_llc_val / num_of_runs; 89 avg_diff = (long)(cache_span - avg_llc_val); 90 diff_percent = ((float)cache_span - avg_llc_val) / cache_span * 100; 91 92 ret = platform && abs((int)diff_percent) > max_diff_percent && 93 labs(avg_diff) > max_diff; 94 95 ksft_print_msg("%s Check cache miss rate within %lu%%\n", 96 ret ? "Fail:" : "Pass:", max_diff_percent); 97 98 ksft_print_msg("Percent diff=%d\n", abs((int)diff_percent)); 99 100 show_cache_info(no_of_bits, avg_llc_val, cache_span, false); 101 102 return ret; 103 } 104 105 static int check_results(struct resctrl_val_param *param, size_t span, int no_of_bits) 106 { 107 char *token_array[8], temp[512]; 108 unsigned long sum_llc_occu_resc = 0; 109 int runs = 0; 110 FILE *fp; 111 112 ksft_print_msg("Checking for pass/fail\n"); 113 fp = fopen(param->filename, "r"); 114 if (!fp) { 115 ksft_perror("Error in opening file"); 116 117 return -1; 118 } 119 120 while (fgets(temp, sizeof(temp), fp)) { 121 char *token = strtok(temp, ":\t"); 122 int fields = 0; 123 124 while (token) { 125 token_array[fields++] = token; 126 token = strtok(NULL, ":\t"); 127 } 128 129 /* Field 3 is llc occ resc value */ 130 sum_llc_occu_resc += strtoul(token_array[3], NULL, 0); 131 runs++; 132 } 133 fclose(fp); 134 135 return show_results_info(sum_llc_occu_resc, no_of_bits, span, 136 MAX_DIFF, MAX_DIFF_PERCENT, runs, true); 137 } 138 139 static void cmt_test_cleanup(void) 140 { 141 remove(RESULT_FILE_NAME); 142 } 143 144 static int cmt_run_test(const struct resctrl_test *test, const struct user_params *uparams) 145 { 146 struct fill_buf_param fill_buf = {}; 147 unsigned long cache_total_size = 0; 148 int n = uparams->bits ? : 5; 149 unsigned long long_mask; 150 int count_of_bits; 151 size_t span; 152 int ret; 153 154 ret = get_full_cbm("L3", &long_mask); 155 if (ret) 156 return ret; 157 158 ret = get_cache_size(uparams->cpu, "L3", &cache_total_size); 159 if (ret) 160 return ret; 161 ksft_print_msg("Cache size :%lu\n", cache_total_size); 162 163 count_of_bits = count_bits(long_mask); 164 165 if (n < 1 || n > count_of_bits) { 166 ksft_print_msg("Invalid input value for numbr_of_bits n!\n"); 167 ksft_print_msg("Please enter value in range 1 to %d\n", count_of_bits); 168 return -1; 169 } 170 171 struct resctrl_val_param param = { 172 .ctrlgrp = "c1", 173 .filename = RESULT_FILE_NAME, 174 .mask = ~(long_mask << n) & long_mask, 175 .num_of_runs = 0, 176 .init = cmt_init, 177 .setup = cmt_setup, 178 .measure = cmt_measure, 179 }; 180 181 span = cache_portion_size(cache_total_size, param.mask, long_mask); 182 183 if (uparams->fill_buf) { 184 fill_buf.buf_size = span * 2; 185 fill_buf.memflush = uparams->fill_buf->memflush; 186 param.fill_buf = &fill_buf; 187 } else if (!uparams->benchmark_cmd[0]) { 188 fill_buf.buf_size = span * 2; 189 fill_buf.memflush = true; 190 param.fill_buf = &fill_buf; 191 } 192 193 remove(RESULT_FILE_NAME); 194 195 ret = resctrl_val(test, uparams, ¶m); 196 if (ret) 197 return ret; 198 199 ret = check_results(¶m, span, n); 200 if (ret && (get_vendor() == ARCH_INTEL) && !snc_kernel_support()) 201 ksft_print_msg("Kernel doesn't support Sub-NUMA Clustering but it is enabled on the system.\n"); 202 203 return ret; 204 } 205 206 static bool cmt_feature_check(const struct resctrl_test *test) 207 { 208 return test_resource_feature_check(test) && 209 resctrl_mon_feature_exists("L3_MON", "llc_occupancy"); 210 } 211 212 struct resctrl_test cmt_test = { 213 .name = "CMT", 214 .resource = "L3", 215 .feature_check = cmt_feature_check, 216 .run_test = cmt_run_test, 217 .cleanup = cmt_test_cleanup, 218 }; 219