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 | static int cmt_setup(const struct resctrl_test *test, |
20 | const struct user_params *uparams, |
21 | struct resctrl_val_param *p) |
22 | { |
23 | /* Run NUM_OF_RUNS times */ |
24 | if (p->num_of_runs >= NUM_OF_RUNS) |
25 | return END_OF_TESTS; |
26 | |
27 | p->num_of_runs++; |
28 | |
29 | return 0; |
30 | } |
31 | |
32 | static int show_results_info(unsigned long sum_llc_val, int no_of_bits, |
33 | unsigned long cache_span, unsigned long max_diff, |
34 | unsigned long max_diff_percent, unsigned long num_of_runs, |
35 | bool platform) |
36 | { |
37 | unsigned long avg_llc_val = 0; |
38 | float diff_percent; |
39 | long avg_diff = 0; |
40 | int ret; |
41 | |
42 | avg_llc_val = sum_llc_val / num_of_runs; |
43 | avg_diff = (long)abs(cache_span - avg_llc_val); |
44 | diff_percent = ((float)cache_span - avg_llc_val) / cache_span * 100; |
45 | |
46 | ret = platform && abs((int)diff_percent) > max_diff_percent && |
47 | abs(avg_diff) > max_diff; |
48 | |
49 | ksft_print_msg(msg: "%s Check cache miss rate within %lu%%\n" , |
50 | ret ? "Fail:" : "Pass:" , max_diff_percent); |
51 | |
52 | ksft_print_msg(msg: "Percent diff=%d\n" , abs((int)diff_percent)); |
53 | |
54 | show_cache_info(no_of_bits, avg_llc_val, cache_span, lines: false); |
55 | |
56 | return ret; |
57 | } |
58 | |
59 | static int check_results(struct resctrl_val_param *param, size_t span, int no_of_bits) |
60 | { |
61 | char *token_array[8], temp[512]; |
62 | unsigned long sum_llc_occu_resc = 0; |
63 | int runs = 0; |
64 | FILE *fp; |
65 | |
66 | ksft_print_msg(msg: "Checking for pass/fail\n" ); |
67 | fp = fopen(param->filename, "r" ); |
68 | if (!fp) { |
69 | ksft_perror(msg: "Error in opening file" ); |
70 | |
71 | return -1; |
72 | } |
73 | |
74 | while (fgets(temp, sizeof(temp), fp)) { |
75 | char *token = strtok(temp, ":\t" ); |
76 | int fields = 0; |
77 | |
78 | while (token) { |
79 | token_array[fields++] = token; |
80 | token = strtok(NULL, ":\t" ); |
81 | } |
82 | |
83 | /* Field 3 is llc occ resc value */ |
84 | if (runs > 0) |
85 | sum_llc_occu_resc += strtoul(token_array[3], NULL, 0); |
86 | runs++; |
87 | } |
88 | fclose(fp); |
89 | |
90 | return show_results_info(sum_llc_val: sum_llc_occu_resc, no_of_bits, cache_span: span, |
91 | MAX_DIFF, MAX_DIFF_PERCENT, num_of_runs: runs - 1, platform: true); |
92 | } |
93 | |
94 | void cmt_test_cleanup(void) |
95 | { |
96 | remove(RESULT_FILE_NAME); |
97 | } |
98 | |
99 | static int cmt_run_test(const struct resctrl_test *test, const struct user_params *uparams) |
100 | { |
101 | const char * const *cmd = uparams->benchmark_cmd; |
102 | const char *new_cmd[BENCHMARK_ARGS]; |
103 | unsigned long cache_total_size = 0; |
104 | int n = uparams->bits ? : 5; |
105 | unsigned long long_mask; |
106 | char *span_str = NULL; |
107 | int count_of_bits; |
108 | size_t span; |
109 | int ret, i; |
110 | |
111 | ret = get_full_cbm(cache_type: "L3" , mask: &long_mask); |
112 | if (ret) |
113 | return ret; |
114 | |
115 | ret = get_cache_size(cpu_no: uparams->cpu, cache_type: "L3" , cache_size: &cache_total_size); |
116 | if (ret) |
117 | return ret; |
118 | ksft_print_msg(msg: "Cache size :%lu\n" , cache_total_size); |
119 | |
120 | count_of_bits = count_bits(n: long_mask); |
121 | |
122 | if (n < 1 || n > count_of_bits) { |
123 | ksft_print_msg(msg: "Invalid input value for numbr_of_bits n!\n" ); |
124 | ksft_print_msg(msg: "Please enter value in range 1 to %d\n" , count_of_bits); |
125 | return -1; |
126 | } |
127 | |
128 | struct resctrl_val_param param = { |
129 | .resctrl_val = CMT_STR, |
130 | .ctrlgrp = "c1" , |
131 | .mongrp = "m1" , |
132 | .filename = RESULT_FILE_NAME, |
133 | .mask = ~(long_mask << n) & long_mask, |
134 | .num_of_runs = 0, |
135 | .setup = cmt_setup, |
136 | }; |
137 | |
138 | span = cache_portion_size(cache_size: cache_total_size, portion_mask: param.mask, full_cache_mask: long_mask); |
139 | |
140 | if (strcmp(cmd[0], "fill_buf" ) == 0) { |
141 | /* Duplicate the command to be able to replace span in it */ |
142 | for (i = 0; uparams->benchmark_cmd[i]; i++) |
143 | new_cmd[i] = uparams->benchmark_cmd[i]; |
144 | new_cmd[i] = NULL; |
145 | |
146 | ret = asprintf(&span_str, "%zu" , span); |
147 | if (ret < 0) |
148 | return -1; |
149 | new_cmd[1] = span_str; |
150 | cmd = new_cmd; |
151 | } |
152 | |
153 | remove(RESULT_FILE_NAME); |
154 | |
155 | ret = resctrl_val(test, uparams, benchmark_cmd: cmd, param: ¶m); |
156 | if (ret) |
157 | goto out; |
158 | |
159 | ret = check_results(param: ¶m, span, no_of_bits: n); |
160 | if (ret && (get_vendor() == ARCH_INTEL)) |
161 | ksft_print_msg(msg: "Intel CMT may be inaccurate when Sub-NUMA Clustering is enabled. Check BIOS configuration.\n" ); |
162 | |
163 | out: |
164 | cmt_test_cleanup(); |
165 | free(span_str); |
166 | |
167 | return ret; |
168 | } |
169 | |
170 | static bool cmt_feature_check(const struct resctrl_test *test) |
171 | { |
172 | return test_resource_feature_check(test) && |
173 | resctrl_mon_feature_exists(resource: "L3_MON" , feature: "llc_occupancy" ); |
174 | } |
175 | |
176 | struct resctrl_test cmt_test = { |
177 | .name = "CMT" , |
178 | .resource = "L3" , |
179 | .feature_check = cmt_feature_check, |
180 | .run_test = cmt_run_test, |
181 | }; |
182 | |