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
19static 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
32static 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
59static 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
94void cmt_test_cleanup(void)
95{
96 remove(RESULT_FILE_NAME);
97}
98
99static 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: &param);
156 if (ret)
157 goto out;
158
159 ret = check_results(param: &param, 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
163out:
164 cmt_test_cleanup();
165 free(span_str);
166
167 return ret;
168}
169
170static 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
176struct resctrl_test cmt_test = {
177 .name = "CMT",
178 .resource = "L3",
179 .feature_check = cmt_feature_check,
180 .run_test = cmt_run_test,
181};
182

source code of linux/tools/testing/selftests/resctrl/cmt_test.c