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 <li.meng@amd.com>
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/bitfield.h>
26 #include <linux/cpufeature.h>
27 #include <linux/cpufreq.h>
28 #include <linux/kernel.h>
29 #include <linux/module.h>
30 #include <linux/moduleparam.h>
31 #include <linux/mm.h>
32 #include <linux/fs.h>
33 #include <linux/cleanup.h>
34
35 #include <acpi/cppc_acpi.h>
36
37 #include <asm/msr.h>
38
39 #include "amd-pstate.h"
40
41 static char *test_list;
42 module_param(test_list, charp, 0444);
43 MODULE_PARM_DESC(test_list,
44 "Comma-delimited list of tests to run (empty means run all tests)");
45 DEFINE_FREE(cleanup_page, void *, if (_T) free_page((unsigned long)_T))
46
47 struct amd_pstate_ut_struct {
48 const char *name;
49 int (*func)(u32 index);
50 };
51
52 /*
53 * Kernel module for testing the AMD P-State unit test
54 */
55 static int amd_pstate_ut_acpi_cpc_valid(u32 index);
56 static int amd_pstate_ut_check_enabled(u32 index);
57 static int amd_pstate_ut_check_perf(u32 index);
58 static int amd_pstate_ut_check_freq(u32 index);
59 static int amd_pstate_ut_epp(u32 index);
60 static int amd_pstate_ut_check_driver(u32 index);
61 static int amd_pstate_ut_check_freq_attrs(u32 index);
62
63 static struct amd_pstate_ut_struct amd_pstate_ut_cases[] = {
64 {"amd_pstate_ut_acpi_cpc_valid", amd_pstate_ut_acpi_cpc_valid },
65 {"amd_pstate_ut_check_enabled", amd_pstate_ut_check_enabled },
66 {"amd_pstate_ut_check_perf", amd_pstate_ut_check_perf },
67 {"amd_pstate_ut_check_freq", amd_pstate_ut_check_freq },
68 {"amd_pstate_ut_epp", amd_pstate_ut_epp },
69 {"amd_pstate_ut_check_driver", amd_pstate_ut_check_driver },
70 {"amd_pstate_ut_check_freq_attrs", amd_pstate_ut_check_freq_attrs },
71 };
72
test_in_list(const char * list,const char * name)73 static bool test_in_list(const char *list, const char *name)
74 {
75 size_t name_len = strlen(name);
76 const char *p = list;
77
78 while (*p) {
79 const char *sep = strchr(p, ',');
80 size_t token_len = sep ? sep - p : strlen(p);
81
82 if (token_len == name_len && !strncmp(p, name, token_len))
83 return true;
84 if (!sep)
85 break;
86 p = sep + 1;
87 }
88
89 return false;
90 }
91
get_shared_mem(void)92 static bool get_shared_mem(void)
93 {
94 bool result = false;
95
96 if (!boot_cpu_has(X86_FEATURE_CPPC))
97 result = true;
98
99 return result;
100 }
101
102 /*
103 * check the _CPC object is present in SBIOS.
104 */
amd_pstate_ut_acpi_cpc_valid(u32 index)105 static int amd_pstate_ut_acpi_cpc_valid(u32 index)
106 {
107 if (!acpi_cpc_valid()) {
108 pr_err("%s the _CPC object is not present in SBIOS!\n", __func__);
109 return -EINVAL;
110 }
111
112 return 0;
113 }
114
115 /*
116 * check if amd pstate is enabled
117 */
amd_pstate_ut_check_enabled(u32 index)118 static int amd_pstate_ut_check_enabled(u32 index)
119 {
120 u64 cppc_enable = 0;
121 int ret;
122
123 if (get_shared_mem())
124 return 0;
125
126 ret = rdmsrq_safe(MSR_AMD_CPPC_ENABLE, &cppc_enable);
127 if (ret) {
128 pr_err("%s rdmsrq_safe MSR_AMD_CPPC_ENABLE ret=%d error!\n", __func__, ret);
129 return ret;
130 }
131
132 if (!cppc_enable) {
133 pr_err("%s amd pstate must be enabled!\n", __func__);
134 return -EINVAL;
135 }
136
137 return 0;
138 }
139
140 /*
141 * check if performance values are reasonable.
142 * highest_perf >= nominal_perf > lowest_nonlinear_perf > lowest_perf > 0
143 */
amd_pstate_ut_check_perf(u32 index)144 static int amd_pstate_ut_check_perf(u32 index)
145 {
146 int cpu = 0, ret = 0;
147 u32 highest_perf = 0, nominal_perf = 0, lowest_nonlinear_perf = 0, lowest_perf = 0;
148 u64 cap1 = 0;
149 struct cppc_perf_caps cppc_perf;
150 union perf_cached cur_perf;
151
152 for_each_online_cpu(cpu) {
153 struct cpufreq_policy *policy __free(put_cpufreq_policy) = NULL;
154 struct amd_cpudata *cpudata;
155
156 policy = cpufreq_cpu_get(cpu);
157 if (!policy)
158 continue;
159 cpudata = policy->driver_data;
160
161 if (get_shared_mem()) {
162 ret = cppc_get_perf_caps(cpu, &cppc_perf);
163 if (ret) {
164 pr_err("%s cppc_get_perf_caps ret=%d error!\n", __func__, ret);
165 return ret;
166 }
167
168 highest_perf = cppc_perf.highest_perf;
169 nominal_perf = cppc_perf.nominal_perf;
170 lowest_nonlinear_perf = cppc_perf.lowest_nonlinear_perf;
171 lowest_perf = cppc_perf.lowest_perf;
172 } else {
173 ret = rdmsrq_safe_on_cpu(cpu, MSR_AMD_CPPC_CAP1, &cap1);
174 if (ret) {
175 pr_err("%s read CPPC_CAP1 ret=%d error!\n", __func__, ret);
176 return ret;
177 }
178
179 highest_perf = FIELD_GET(AMD_CPPC_HIGHEST_PERF_MASK, cap1);
180 nominal_perf = FIELD_GET(AMD_CPPC_NOMINAL_PERF_MASK, cap1);
181 lowest_nonlinear_perf = FIELD_GET(AMD_CPPC_LOWNONLIN_PERF_MASK, cap1);
182 lowest_perf = FIELD_GET(AMD_CPPC_LOWEST_PERF_MASK, cap1);
183 }
184
185 cur_perf = READ_ONCE(cpudata->perf);
186 if (highest_perf != cur_perf.highest_perf && !cpudata->hw_prefcore) {
187 pr_err("%s cpu%d highest=%d %d highest perf doesn't match\n",
188 __func__, cpu, highest_perf, cur_perf.highest_perf);
189 return -EINVAL;
190 }
191 if (nominal_perf != cur_perf.nominal_perf ||
192 (lowest_nonlinear_perf != cur_perf.lowest_nonlinear_perf) ||
193 (lowest_perf != cur_perf.lowest_perf)) {
194 pr_err("%s cpu%d nominal=%d %d lowest_nonlinear=%d %d lowest=%d %d, they should be equal!\n",
195 __func__, cpu, nominal_perf, cur_perf.nominal_perf,
196 lowest_nonlinear_perf, cur_perf.lowest_nonlinear_perf,
197 lowest_perf, cur_perf.lowest_perf);
198 return -EINVAL;
199 }
200
201 if (!((highest_perf >= nominal_perf) &&
202 (nominal_perf > lowest_nonlinear_perf) &&
203 (lowest_nonlinear_perf >= lowest_perf) &&
204 (lowest_perf > 0))) {
205 pr_err("%s cpu%d highest=%d >= nominal=%d > lowest_nonlinear=%d > lowest=%d > 0, the formula is incorrect!\n",
206 __func__, cpu, highest_perf, nominal_perf,
207 lowest_nonlinear_perf, lowest_perf);
208 return -EINVAL;
209 }
210 }
211
212 return 0;
213 }
214
215 /*
216 * Check if frequency values are reasonable.
217 * max_freq >= nominal_freq > lowest_nonlinear_freq > min_freq > 0
218 * check max freq when set support boost mode.
219 */
amd_pstate_ut_check_freq(u32 index)220 static int amd_pstate_ut_check_freq(u32 index)
221 {
222 int cpu = 0;
223
224 for_each_online_cpu(cpu) {
225 struct cpufreq_policy *policy __free(put_cpufreq_policy) = NULL;
226 struct amd_cpudata *cpudata;
227
228 policy = cpufreq_cpu_get(cpu);
229 if (!policy)
230 continue;
231 cpudata = policy->driver_data;
232
233 if (!((policy->cpuinfo.max_freq >= cpudata->nominal_freq) &&
234 (cpudata->nominal_freq > cpudata->lowest_nonlinear_freq) &&
235 (cpudata->lowest_nonlinear_freq >= policy->cpuinfo.min_freq) &&
236 (policy->cpuinfo.min_freq > 0))) {
237 pr_err("%s cpu%d max=%d >= nominal=%d > lowest_nonlinear=%d > min=%d > 0, the formula is incorrect!\n",
238 __func__, cpu, policy->cpuinfo.max_freq, cpudata->nominal_freq,
239 cpudata->lowest_nonlinear_freq, policy->cpuinfo.min_freq);
240 return -EINVAL;
241 }
242
243 if (cpudata->lowest_nonlinear_freq != policy->min) {
244 pr_err("%s cpu%d cpudata_lowest_nonlinear_freq=%d policy_min=%d, they should be equal!\n",
245 __func__, cpu, cpudata->lowest_nonlinear_freq, policy->min);
246 return -EINVAL;
247 }
248
249 if (cpudata->boost_supported) {
250 if ((policy->max != policy->cpuinfo.max_freq) &&
251 (policy->max != cpudata->nominal_freq)) {
252 pr_err("%s cpu%d policy_max=%d should be equal cpu_max=%d or cpu_nominal=%d !\n",
253 __func__, cpu, policy->max, policy->cpuinfo.max_freq,
254 cpudata->nominal_freq);
255 return -EINVAL;
256 }
257 } else {
258 pr_err("%s cpu%d must support boost!\n", __func__, cpu);
259 return -EINVAL;
260 }
261 }
262
263 return 0;
264 }
265
amd_pstate_set_mode(enum amd_pstate_mode mode)266 static int amd_pstate_set_mode(enum amd_pstate_mode mode)
267 {
268 const char *mode_str = amd_pstate_get_mode_string(mode);
269
270 pr_debug("->setting mode to %s\n", mode_str);
271
272 return amd_pstate_update_status(mode_str, strlen(mode_str));
273 }
274
amd_pstate_ut_epp(u32 index)275 static int amd_pstate_ut_epp(u32 index)
276 {
277 static const char * const epp_strings[] = {
278 "power",
279 "balance_power",
280 "balance_performance",
281 "performance",
282 };
283 char *buf __free(cleanup_page) = NULL;
284 struct cpufreq_policy *policy = NULL;
285 enum amd_pstate_mode orig_mode;
286 struct amd_cpudata *cpudata;
287 unsigned long orig_policy;
288 bool orig_dynamic_epp;
289 int ret, cpu = 0;
290 u16 epp;
291 int i;
292
293 policy = cpufreq_cpu_get(cpu);
294 if (!policy)
295 return -ENODEV;
296
297 cpudata = policy->driver_data;
298 orig_mode = amd_pstate_get_status();
299 orig_dynamic_epp = cpudata->dynamic_epp;
300
301 /* Drop reference before potential driver change. */
302 cpufreq_cpu_put(policy);
303 policy = NULL;
304
305 buf = (char *)__get_free_page(GFP_KERNEL);
306 if (!buf)
307 return -ENOMEM;
308
309 ret = amd_pstate_set_mode(AMD_PSTATE_ACTIVE);
310 if (ret)
311 goto out;
312
313 policy = cpufreq_cpu_get(cpu);
314 if (!policy) {
315 ret = -ENODEV;
316 goto out;
317 }
318
319 down_write(&policy->rwsem);
320 cpudata = policy->driver_data;
321 orig_policy = cpudata->policy;
322 cpudata->policy = CPUFREQ_POLICY_POWERSAVE;
323
324 /*
325 * Disable dynamic EPP before running test. If "orig_dynamic_epp" is
326 * true, the driver will do a redundant switch at the end and there
327 * is no need for enabling it again at the end of the test.
328 */
329 if (cpudata->dynamic_epp) {
330 pr_debug("Dynamic EPP is enabled, disabling it\n");
331 amd_pstate_clear_dynamic_epp(policy);
332 }
333
334 for (epp = 0; epp <= U8_MAX; epp++) {
335 u8 val;
336
337 /* write all EPP values */
338 memset(buf, 0, PAGE_SIZE);
339 snprintf(buf, PAGE_SIZE, "%d", epp);
340 ret = store_energy_performance_preference(policy, buf, strlen(buf));
341 if (ret < 0)
342 goto out;
343
344 /* check if the EPP value reads back correctly for raw numbers */
345 memset(buf, 0, PAGE_SIZE);
346 ret = show_energy_performance_preference(policy, buf);
347 if (ret < 0)
348 goto out;
349 strreplace(buf, '\n', '\0');
350 ret = kstrtou8(buf, 0, &val);
351 if (!ret && epp != val) {
352 pr_err("Raw EPP value mismatch: %d != %d\n", epp, val);
353 ret = -EINVAL;
354 goto out;
355 }
356 }
357
358 for (i = 0; i < ARRAY_SIZE(epp_strings); i++) {
359 memset(buf, 0, PAGE_SIZE);
360 snprintf(buf, PAGE_SIZE, "%s", epp_strings[i]);
361 ret = store_energy_performance_preference(policy, buf, strlen(buf));
362 if (ret < 0)
363 goto out;
364
365 memset(buf, 0, PAGE_SIZE);
366 ret = show_energy_performance_preference(policy, buf);
367 if (ret < 0)
368 goto out;
369 strreplace(buf, '\n', '\0');
370
371 if (strcmp(buf, epp_strings[i])) {
372 pr_err("String EPP value mismatch: %s != %s\n", buf, epp_strings[i]);
373 ret = -EINVAL;
374 goto out;
375 }
376 }
377
378 ret = 0;
379
380 out:
381 if (policy) {
382 cpudata->policy = orig_policy;
383 up_write(&policy->rwsem);
384 cpufreq_cpu_put(policy);
385 }
386
387 if (orig_dynamic_epp) {
388 int ret2;
389
390 ret2 = amd_pstate_set_mode(AMD_PSTATE_DISABLE);
391 if (!ret && ret2)
392 ret = ret2;
393 }
394
395 if (orig_mode != amd_pstate_get_status()) {
396 int ret2;
397
398 ret2 = amd_pstate_set_mode(orig_mode);
399 if (!ret && ret2)
400 ret = ret2;
401 }
402
403 return ret;
404 }
405
amd_pstate_ut_check_driver(u32 index)406 static int amd_pstate_ut_check_driver(u32 index)
407 {
408 enum amd_pstate_mode mode1, mode2 = AMD_PSTATE_DISABLE;
409 enum amd_pstate_mode orig_mode = amd_pstate_get_status();
410 int ret;
411
412 for (mode1 = AMD_PSTATE_DISABLE; mode1 < AMD_PSTATE_MAX; mode1++) {
413 ret = amd_pstate_set_mode(mode1);
414 if (ret)
415 return ret;
416 for (mode2 = AMD_PSTATE_DISABLE; mode2 < AMD_PSTATE_MAX; mode2++) {
417 if (mode1 == mode2)
418 continue;
419 ret = amd_pstate_set_mode(mode2);
420 if (ret)
421 goto out;
422 }
423 }
424
425 out:
426 if (ret)
427 pr_warn("%s: failed to update status for %s->%s: %d\n", __func__,
428 amd_pstate_get_mode_string(mode1),
429 amd_pstate_get_mode_string(mode2), ret);
430
431 amd_pstate_set_mode(orig_mode);
432 return ret;
433 }
434
435 enum attr_category {
436 ATTR_ALWAYS,
437 ATTR_PREFCORE,
438 ATTR_EPP,
439 ATTR_FLOOR_FREQ,
440 };
441
442 static const struct {
443 const char *name;
444 enum attr_category category;
445 } expected_freq_attrs[] = {
446 {"amd_pstate_max_freq", ATTR_ALWAYS},
447 {"amd_pstate_lowest_nonlinear_freq", ATTR_ALWAYS},
448 {"amd_pstate_highest_perf", ATTR_ALWAYS},
449 {"amd_pstate_prefcore_ranking", ATTR_PREFCORE},
450 {"amd_pstate_hw_prefcore", ATTR_PREFCORE},
451 {"energy_performance_preference", ATTR_EPP},
452 {"energy_performance_available_preferences", ATTR_EPP},
453 {"amd_pstate_floor_freq", ATTR_FLOOR_FREQ},
454 {"amd_pstate_floor_count", ATTR_FLOOR_FREQ},
455 };
456
attr_in_driver(struct freq_attr ** driver_attrs,const char * name)457 static bool attr_in_driver(struct freq_attr **driver_attrs, const char *name)
458 {
459 int j;
460
461 for (j = 0; driver_attrs[j]; j++) {
462 if (!strcmp(driver_attrs[j]->attr.name, name))
463 return true;
464 }
465 return false;
466 }
467
468 /*
469 * Verify that for each mode the driver's live ->attr array contains exactly
470 * the attributes that should be visible. Expected visibility is derived
471 * independently from hw_prefcore, cpu features, and the current mode —
472 * not from the driver's own visibility functions.
473 */
amd_pstate_ut_check_freq_attrs(u32 index)474 static int amd_pstate_ut_check_freq_attrs(u32 index)
475 {
476 enum amd_pstate_mode orig_mode = amd_pstate_get_status();
477 static const enum amd_pstate_mode modes[] = {
478 AMD_PSTATE_PASSIVE, AMD_PSTATE_ACTIVE, AMD_PSTATE_GUIDED,
479 };
480 bool has_prefcore, has_floor_freq;
481 int m, i, ret;
482
483 has_floor_freq = cpu_feature_enabled(X86_FEATURE_CPPC_PERF_PRIO);
484
485 /*
486 * Determine prefcore support from any online CPU's cpudata.
487 * hw_prefcore reflects the platform-wide decision made at init.
488 */
489 has_prefcore = false;
490 for_each_online_cpu(i) {
491 struct cpufreq_policy *policy __free(put_cpufreq_policy) = NULL;
492 struct amd_cpudata *cpudata;
493
494 policy = cpufreq_cpu_get(i);
495 if (!policy)
496 continue;
497 cpudata = policy->driver_data;
498 has_prefcore = cpudata->hw_prefcore;
499 break;
500 }
501
502 for (m = 0; m < ARRAY_SIZE(modes); m++) {
503 struct freq_attr **driver_attrs;
504
505 ret = amd_pstate_set_mode(modes[m]);
506 if (ret)
507 goto out;
508
509 driver_attrs = amd_pstate_get_current_attrs();
510 if (!driver_attrs) {
511 pr_err("%s: no driver attrs in mode %s\n",
512 __func__, amd_pstate_get_mode_string(modes[m]));
513 ret = -EINVAL;
514 goto out;
515 }
516
517 for (i = 0; i < ARRAY_SIZE(expected_freq_attrs); i++) {
518 bool expected, found;
519
520 switch (expected_freq_attrs[i].category) {
521 case ATTR_ALWAYS:
522 expected = true;
523 break;
524 case ATTR_PREFCORE:
525 expected = has_prefcore;
526 break;
527 case ATTR_EPP:
528 expected = (modes[m] == AMD_PSTATE_ACTIVE);
529 break;
530 case ATTR_FLOOR_FREQ:
531 expected = has_floor_freq;
532 break;
533 default:
534 expected = false;
535 break;
536 }
537
538 found = attr_in_driver(driver_attrs,
539 expected_freq_attrs[i].name);
540
541 if (expected != found) {
542 pr_err("%s: mode %s: attr %s expected %s but is %s\n",
543 __func__,
544 amd_pstate_get_mode_string(modes[m]),
545 expected_freq_attrs[i].name,
546 expected ? "visible" : "hidden",
547 found ? "visible" : "hidden");
548 ret = -EINVAL;
549 goto out;
550 }
551 }
552 }
553
554 ret = 0;
555 out:
556 amd_pstate_set_mode(orig_mode);
557 return ret;
558 }
559
amd_pstate_ut_init(void)560 static int __init amd_pstate_ut_init(void)
561 {
562 u32 i = 0, arr_size = ARRAY_SIZE(amd_pstate_ut_cases);
563
564 for (i = 0; i < arr_size; i++) {
565 int ret;
566
567 if (test_list && *test_list &&
568 !test_in_list(test_list, amd_pstate_ut_cases[i].name))
569 continue;
570
571 ret = amd_pstate_ut_cases[i].func(i);
572
573 if (ret)
574 pr_err("%-4d %-20s\t fail: %d!\n", i+1, amd_pstate_ut_cases[i].name, ret);
575 else
576 pr_info("%-4d %-20s\t success!\n", i+1, amd_pstate_ut_cases[i].name);
577 }
578
579 return 0;
580 }
581
amd_pstate_ut_exit(void)582 static void __exit amd_pstate_ut_exit(void)
583 {
584 }
585
586 module_init(amd_pstate_ut_init);
587 module_exit(amd_pstate_ut_exit);
588
589 MODULE_AUTHOR("Meng Li <li.meng@amd.com>");
590 MODULE_DESCRIPTION("AMD P-state driver Test module");
591 MODULE_LICENSE("GPL");
592