]> Git Repo - linux.git/blob - drivers/cpufreq/amd-pstate-ut.c
crypto: akcipher - Drop sign/verify operations
[linux.git] / drivers / cpufreq / amd-pstate-ut.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * AMD Processor P-state Frequency Driver Unit Test
4  *
5  * Copyright (C) 2022 Advanced Micro Devices, Inc. All Rights Reserved.
6  *
7  * Author: Meng Li <[email protected]>
8  *
9  * The AMD P-State Unit Test is a test module for testing the amd-pstate
10  * driver. 1) It can help all users to verify their processor support
11  * (SBIOS/Firmware or Hardware). 2) Kernel can have a basic function
12  * test to avoid the kernel regression during the update. 3) We can
13  * introduce more functional or performance tests to align the result
14  * together, it will benefit power and performance scale optimization.
15  *
16  * This driver implements basic framework with plans to enhance it with
17  * additional test cases to improve the depth and coverage of the test.
18  *
19  * See Documentation/admin-guide/pm/amd-pstate.rst Unit Tests for
20  * amd-pstate to get more detail.
21  */
22
23 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
24
25 #include <linux/kernel.h>
26 #include <linux/module.h>
27 #include <linux/moduleparam.h>
28 #include <linux/fs.h>
29
30 #include <acpi/cppc_acpi.h>
31
32 #include "amd-pstate.h"
33
34 /*
35  * Abbreviations:
36  * amd_pstate_ut: used as a shortform for AMD P-State unit test.
37  * It helps to keep variable names smaller, simpler
38  */
39 enum amd_pstate_ut_result {
40         AMD_PSTATE_UT_RESULT_PASS,
41         AMD_PSTATE_UT_RESULT_FAIL,
42 };
43
44 struct amd_pstate_ut_struct {
45         const char *name;
46         void (*func)(u32 index);
47         enum amd_pstate_ut_result result;
48 };
49
50 /*
51  * Kernel module for testing the AMD P-State unit test
52  */
53 static void amd_pstate_ut_acpi_cpc_valid(u32 index);
54 static void amd_pstate_ut_check_enabled(u32 index);
55 static void amd_pstate_ut_check_perf(u32 index);
56 static void amd_pstate_ut_check_freq(u32 index);
57 static void amd_pstate_ut_check_driver(u32 index);
58
59 static struct amd_pstate_ut_struct amd_pstate_ut_cases[] = {
60         {"amd_pstate_ut_acpi_cpc_valid",   amd_pstate_ut_acpi_cpc_valid   },
61         {"amd_pstate_ut_check_enabled",    amd_pstate_ut_check_enabled    },
62         {"amd_pstate_ut_check_perf",       amd_pstate_ut_check_perf       },
63         {"amd_pstate_ut_check_freq",       amd_pstate_ut_check_freq       },
64         {"amd_pstate_ut_check_driver",     amd_pstate_ut_check_driver     }
65 };
66
67 static bool get_shared_mem(void)
68 {
69         bool result = false;
70
71         if (!boot_cpu_has(X86_FEATURE_CPPC))
72                 result = true;
73
74         return result;
75 }
76
77 /*
78  * check the _CPC object is present in SBIOS.
79  */
80 static void amd_pstate_ut_acpi_cpc_valid(u32 index)
81 {
82         if (acpi_cpc_valid())
83                 amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_PASS;
84         else {
85                 amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_FAIL;
86                 pr_err("%s the _CPC object is not present in SBIOS!\n", __func__);
87         }
88 }
89
90 static void amd_pstate_ut_pstate_enable(u32 index)
91 {
92         int ret = 0;
93         u64 cppc_enable = 0;
94
95         ret = rdmsrl_safe(MSR_AMD_CPPC_ENABLE, &cppc_enable);
96         if (ret) {
97                 amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_FAIL;
98                 pr_err("%s rdmsrl_safe MSR_AMD_CPPC_ENABLE ret=%d error!\n", __func__, ret);
99                 return;
100         }
101         if (cppc_enable)
102                 amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_PASS;
103         else {
104                 amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_FAIL;
105                 pr_err("%s amd pstate must be enabled!\n", __func__);
106         }
107 }
108
109 /*
110  * check if amd pstate is enabled
111  */
112 static void amd_pstate_ut_check_enabled(u32 index)
113 {
114         if (get_shared_mem())
115                 amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_PASS;
116         else
117                 amd_pstate_ut_pstate_enable(index);
118 }
119
120 /*
121  * check if performance values are reasonable.
122  * highest_perf >= nominal_perf > lowest_nonlinear_perf > lowest_perf > 0
123  */
124 static void amd_pstate_ut_check_perf(u32 index)
125 {
126         int cpu = 0, ret = 0;
127         u32 highest_perf = 0, nominal_perf = 0, lowest_nonlinear_perf = 0, lowest_perf = 0;
128         u64 cap1 = 0;
129         struct cppc_perf_caps cppc_perf;
130         struct cpufreq_policy *policy = NULL;
131         struct amd_cpudata *cpudata = NULL;
132
133         for_each_possible_cpu(cpu) {
134                 policy = cpufreq_cpu_get(cpu);
135                 if (!policy)
136                         break;
137                 cpudata = policy->driver_data;
138
139                 if (get_shared_mem()) {
140                         ret = cppc_get_perf_caps(cpu, &cppc_perf);
141                         if (ret) {
142                                 amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_FAIL;
143                                 pr_err("%s cppc_get_perf_caps ret=%d error!\n", __func__, ret);
144                                 goto skip_test;
145                         }
146
147                         highest_perf = cppc_perf.highest_perf;
148                         nominal_perf = cppc_perf.nominal_perf;
149                         lowest_nonlinear_perf = cppc_perf.lowest_nonlinear_perf;
150                         lowest_perf = cppc_perf.lowest_perf;
151                 } else {
152                         ret = rdmsrl_safe_on_cpu(cpu, MSR_AMD_CPPC_CAP1, &cap1);
153                         if (ret) {
154                                 amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_FAIL;
155                                 pr_err("%s read CPPC_CAP1 ret=%d error!\n", __func__, ret);
156                                 goto skip_test;
157                         }
158
159                         highest_perf = AMD_CPPC_HIGHEST_PERF(cap1);
160                         nominal_perf = AMD_CPPC_NOMINAL_PERF(cap1);
161                         lowest_nonlinear_perf = AMD_CPPC_LOWNONLIN_PERF(cap1);
162                         lowest_perf = AMD_CPPC_LOWEST_PERF(cap1);
163                 }
164
165                 if (highest_perf != READ_ONCE(cpudata->highest_perf) && !cpudata->hw_prefcore) {
166                         pr_err("%s cpu%d highest=%d %d highest perf doesn't match\n",
167                                 __func__, cpu, highest_perf, cpudata->highest_perf);
168                         goto skip_test;
169                 }
170                 if ((nominal_perf != READ_ONCE(cpudata->nominal_perf)) ||
171                         (lowest_nonlinear_perf != READ_ONCE(cpudata->lowest_nonlinear_perf)) ||
172                         (lowest_perf != READ_ONCE(cpudata->lowest_perf))) {
173                         amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_FAIL;
174                         pr_err("%s cpu%d nominal=%d %d lowest_nonlinear=%d %d lowest=%d %d, they should be equal!\n",
175                                 __func__, cpu, nominal_perf, cpudata->nominal_perf,
176                                 lowest_nonlinear_perf, cpudata->lowest_nonlinear_perf,
177                                 lowest_perf, cpudata->lowest_perf);
178                         goto skip_test;
179                 }
180
181                 if (!((highest_perf >= nominal_perf) &&
182                         (nominal_perf > lowest_nonlinear_perf) &&
183                         (lowest_nonlinear_perf > lowest_perf) &&
184                         (lowest_perf > 0))) {
185                         amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_FAIL;
186                         pr_err("%s cpu%d highest=%d >= nominal=%d > lowest_nonlinear=%d > lowest=%d > 0, the formula is incorrect!\n",
187                                 __func__, cpu, highest_perf, nominal_perf,
188                                 lowest_nonlinear_perf, lowest_perf);
189                         goto skip_test;
190                 }
191                 cpufreq_cpu_put(policy);
192         }
193
194         amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_PASS;
195         return;
196 skip_test:
197         cpufreq_cpu_put(policy);
198 }
199
200 /*
201  * Check if frequency values are reasonable.
202  * max_freq >= nominal_freq > lowest_nonlinear_freq > min_freq > 0
203  * check max freq when set support boost mode.
204  */
205 static void amd_pstate_ut_check_freq(u32 index)
206 {
207         int cpu = 0;
208         struct cpufreq_policy *policy = NULL;
209         struct amd_cpudata *cpudata = NULL;
210         u32 nominal_freq_khz;
211
212         for_each_possible_cpu(cpu) {
213                 policy = cpufreq_cpu_get(cpu);
214                 if (!policy)
215                         break;
216                 cpudata = policy->driver_data;
217
218                 nominal_freq_khz = cpudata->nominal_freq*1000;
219                 if (!((cpudata->max_freq >= nominal_freq_khz) &&
220                         (nominal_freq_khz > cpudata->lowest_nonlinear_freq) &&
221                         (cpudata->lowest_nonlinear_freq > cpudata->min_freq) &&
222                         (cpudata->min_freq > 0))) {
223                         amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_FAIL;
224                         pr_err("%s cpu%d max=%d >= nominal=%d > lowest_nonlinear=%d > min=%d > 0, the formula is incorrect!\n",
225                                 __func__, cpu, cpudata->max_freq, nominal_freq_khz,
226                                 cpudata->lowest_nonlinear_freq, cpudata->min_freq);
227                         goto skip_test;
228                 }
229
230                 if (cpudata->min_freq != policy->min) {
231                         amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_FAIL;
232                         pr_err("%s cpu%d cpudata_min_freq=%d policy_min=%d, they should be equal!\n",
233                                 __func__, cpu, cpudata->min_freq, policy->min);
234                         goto skip_test;
235                 }
236
237                 if (cpudata->boost_supported) {
238                         if ((policy->max == cpudata->max_freq) ||
239                                         (policy->max == nominal_freq_khz))
240                                 amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_PASS;
241                         else {
242                                 amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_FAIL;
243                                 pr_err("%s cpu%d policy_max=%d should be equal cpu_max=%d or cpu_nominal=%d !\n",
244                                         __func__, cpu, policy->max, cpudata->max_freq,
245                                         nominal_freq_khz);
246                                 goto skip_test;
247                         }
248                 } else {
249                         amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_FAIL;
250                         pr_err("%s cpu%d must support boost!\n", __func__, cpu);
251                         goto skip_test;
252                 }
253                 cpufreq_cpu_put(policy);
254         }
255
256         amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_PASS;
257         return;
258 skip_test:
259         cpufreq_cpu_put(policy);
260 }
261
262 static int amd_pstate_set_mode(enum amd_pstate_mode mode)
263 {
264         const char *mode_str = amd_pstate_get_mode_string(mode);
265
266         pr_debug("->setting mode to %s\n", mode_str);
267
268         return amd_pstate_update_status(mode_str, strlen(mode_str));
269 }
270
271 static void amd_pstate_ut_check_driver(u32 index)
272 {
273         enum amd_pstate_mode mode1, mode2 = AMD_PSTATE_DISABLE;
274         int ret;
275
276         for (mode1 = AMD_PSTATE_DISABLE; mode1 < AMD_PSTATE_MAX; mode1++) {
277                 ret = amd_pstate_set_mode(mode1);
278                 if (ret)
279                         goto out;
280                 for (mode2 = AMD_PSTATE_DISABLE; mode2 < AMD_PSTATE_MAX; mode2++) {
281                         if (mode1 == mode2)
282                                 continue;
283                         ret = amd_pstate_set_mode(mode2);
284                         if (ret)
285                                 goto out;
286                 }
287         }
288 out:
289         if (ret)
290                 pr_warn("%s: failed to update status for %s->%s: %d\n", __func__,
291                         amd_pstate_get_mode_string(mode1),
292                         amd_pstate_get_mode_string(mode2), ret);
293
294         amd_pstate_ut_cases[index].result = ret ?
295                                             AMD_PSTATE_UT_RESULT_FAIL :
296                                             AMD_PSTATE_UT_RESULT_PASS;
297 }
298
299 static int __init amd_pstate_ut_init(void)
300 {
301         u32 i = 0, arr_size = ARRAY_SIZE(amd_pstate_ut_cases);
302
303         for (i = 0; i < arr_size; i++) {
304                 amd_pstate_ut_cases[i].func(i);
305                 switch (amd_pstate_ut_cases[i].result) {
306                 case AMD_PSTATE_UT_RESULT_PASS:
307                         pr_info("%-4d %-20s\t success!\n", i+1, amd_pstate_ut_cases[i].name);
308                         break;
309                 case AMD_PSTATE_UT_RESULT_FAIL:
310                 default:
311                         pr_info("%-4d %-20s\t fail!\n", i+1, amd_pstate_ut_cases[i].name);
312                         break;
313                 }
314         }
315
316         return 0;
317 }
318
319 static void __exit amd_pstate_ut_exit(void)
320 {
321 }
322
323 module_init(amd_pstate_ut_init);
324 module_exit(amd_pstate_ut_exit);
325
326 MODULE_AUTHOR("Meng Li <[email protected]>");
327 MODULE_DESCRIPTION("AMD P-state driver Test module");
328 MODULE_LICENSE("GPL");
This page took 0.051816 seconds and 4 git commands to generate.