xref: /linux/drivers/cpufreq/amd-pstate-ut.c (revision 68ca50bc0fa64841cd73b8a1538df1d7f7eb4108)
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 
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 
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  */
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  */
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  */
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  */
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 
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 
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 	/* disable dynamic EPP before running test */
306 	if (cpudata->dynamic_epp) {
307 		pr_debug("Dynamic EPP is enabled, disabling it\n");
308 		amd_pstate_clear_dynamic_epp(policy);
309 	}
310 
311 	buf = (char *)__get_free_page(GFP_KERNEL);
312 	if (!buf)
313 		return -ENOMEM;
314 
315 	ret = amd_pstate_set_mode(AMD_PSTATE_ACTIVE);
316 	if (ret)
317 		goto out;
318 
319 	policy = cpufreq_cpu_get(cpu);
320 	if (!policy) {
321 		ret = -ENODEV;
322 		goto out;
323 	}
324 
325 	down_write(&policy->rwsem);
326 	cpudata = policy->driver_data;
327 	orig_policy = cpudata->policy;
328 	cpudata->policy = CPUFREQ_POLICY_POWERSAVE;
329 
330 	for (epp = 0; epp <= U8_MAX; epp++) {
331 		u8 val;
332 
333 		/* write all EPP values */
334 		memset(buf, 0, PAGE_SIZE);
335 		snprintf(buf, PAGE_SIZE, "%d", epp);
336 		ret = store_energy_performance_preference(policy, buf, strlen(buf));
337 		if (ret < 0)
338 			goto out;
339 
340 		/* check if the EPP value reads back correctly for raw numbers */
341 		memset(buf, 0, PAGE_SIZE);
342 		ret = show_energy_performance_preference(policy, buf);
343 		if (ret < 0)
344 			goto out;
345 		strreplace(buf, '\n', '\0');
346 		ret = kstrtou8(buf, 0, &val);
347 		if (!ret && epp != val) {
348 			pr_err("Raw EPP value mismatch: %d != %d\n", epp, val);
349 			ret = -EINVAL;
350 			goto out;
351 		}
352 	}
353 
354 	for (i = 0; i < ARRAY_SIZE(epp_strings); i++) {
355 		memset(buf, 0, PAGE_SIZE);
356 		snprintf(buf, PAGE_SIZE, "%s", epp_strings[i]);
357 		ret = store_energy_performance_preference(policy, buf, strlen(buf));
358 		if (ret < 0)
359 			goto out;
360 
361 		memset(buf, 0, PAGE_SIZE);
362 		ret = show_energy_performance_preference(policy, buf);
363 		if (ret < 0)
364 			goto out;
365 		strreplace(buf, '\n', '\0');
366 
367 		if (strcmp(buf, epp_strings[i])) {
368 			pr_err("String EPP value mismatch: %s != %s\n", buf, epp_strings[i]);
369 			ret = -EINVAL;
370 			goto out;
371 		}
372 	}
373 
374 	ret = 0;
375 
376 out:
377 	if (policy) {
378 		cpudata->policy = orig_policy;
379 		up_write(&policy->rwsem);
380 		cpufreq_cpu_put(policy);
381 	}
382 
383 	if (orig_dynamic_epp) {
384 		int ret2;
385 
386 		ret2 = amd_pstate_set_mode(AMD_PSTATE_DISABLE);
387 		if (!ret && ret2)
388 			ret = ret2;
389 	}
390 
391 	if (orig_mode != amd_pstate_get_status()) {
392 		int ret2;
393 
394 		ret2 = amd_pstate_set_mode(orig_mode);
395 		if (!ret && ret2)
396 			ret = ret2;
397 	}
398 
399 	return ret;
400 }
401 
402 static int amd_pstate_ut_check_driver(u32 index)
403 {
404 	enum amd_pstate_mode mode1, mode2 = AMD_PSTATE_DISABLE;
405 	enum amd_pstate_mode orig_mode = amd_pstate_get_status();
406 	int ret;
407 
408 	for (mode1 = AMD_PSTATE_DISABLE; mode1 < AMD_PSTATE_MAX; mode1++) {
409 		ret = amd_pstate_set_mode(mode1);
410 		if (ret)
411 			return ret;
412 		for (mode2 = AMD_PSTATE_DISABLE; mode2 < AMD_PSTATE_MAX; mode2++) {
413 			if (mode1 == mode2)
414 				continue;
415 			ret = amd_pstate_set_mode(mode2);
416 			if (ret)
417 				goto out;
418 		}
419 	}
420 
421 out:
422 	if (ret)
423 		pr_warn("%s: failed to update status for %s->%s: %d\n", __func__,
424 			amd_pstate_get_mode_string(mode1),
425 			amd_pstate_get_mode_string(mode2), ret);
426 
427 	amd_pstate_set_mode(orig_mode);
428 	return ret;
429 }
430 
431 enum attr_category {
432 	ATTR_ALWAYS,
433 	ATTR_PREFCORE,
434 	ATTR_EPP,
435 	ATTR_FLOOR_FREQ,
436 };
437 
438 static const struct {
439 	const char	*name;
440 	enum attr_category category;
441 } expected_freq_attrs[] = {
442 	{"amd_pstate_max_freq",				ATTR_ALWAYS},
443 	{"amd_pstate_lowest_nonlinear_freq",		ATTR_ALWAYS},
444 	{"amd_pstate_highest_perf",			ATTR_ALWAYS},
445 	{"amd_pstate_prefcore_ranking",			ATTR_PREFCORE},
446 	{"amd_pstate_hw_prefcore",			ATTR_PREFCORE},
447 	{"energy_performance_preference",		ATTR_EPP},
448 	{"energy_performance_available_preferences",	ATTR_EPP},
449 	{"amd_pstate_floor_freq",			ATTR_FLOOR_FREQ},
450 	{"amd_pstate_floor_count",			ATTR_FLOOR_FREQ},
451 };
452 
453 static bool attr_in_driver(struct freq_attr **driver_attrs, const char *name)
454 {
455 	int j;
456 
457 	for (j = 0; driver_attrs[j]; j++) {
458 		if (!strcmp(driver_attrs[j]->attr.name, name))
459 			return true;
460 	}
461 	return false;
462 }
463 
464 /*
465  * Verify that for each mode the driver's live ->attr array contains exactly
466  * the attributes that should be visible.  Expected visibility is derived
467  * independently from hw_prefcore, cpu features, and the current mode —
468  * not from the driver's own visibility functions.
469  */
470 static int amd_pstate_ut_check_freq_attrs(u32 index)
471 {
472 	enum amd_pstate_mode orig_mode = amd_pstate_get_status();
473 	static const enum amd_pstate_mode modes[] = {
474 		AMD_PSTATE_PASSIVE, AMD_PSTATE_ACTIVE, AMD_PSTATE_GUIDED,
475 	};
476 	bool has_prefcore, has_floor_freq;
477 	int m, i, ret;
478 
479 	has_floor_freq = cpu_feature_enabled(X86_FEATURE_CPPC_PERF_PRIO);
480 
481 	/*
482 	 * Determine prefcore support from any online CPU's cpudata.
483 	 * hw_prefcore reflects the platform-wide decision made at init.
484 	 */
485 	has_prefcore = false;
486 	for_each_online_cpu(i) {
487 		struct cpufreq_policy *policy __free(put_cpufreq_policy) = NULL;
488 		struct amd_cpudata *cpudata;
489 
490 		policy = cpufreq_cpu_get(i);
491 		if (!policy)
492 			continue;
493 		cpudata = policy->driver_data;
494 		has_prefcore = cpudata->hw_prefcore;
495 		break;
496 	}
497 
498 	for (m = 0; m < ARRAY_SIZE(modes); m++) {
499 		struct freq_attr **driver_attrs;
500 
501 		ret = amd_pstate_set_mode(modes[m]);
502 		if (ret)
503 			goto out;
504 
505 		driver_attrs = amd_pstate_get_current_attrs();
506 		if (!driver_attrs) {
507 			pr_err("%s: no driver attrs in mode %s\n",
508 			       __func__, amd_pstate_get_mode_string(modes[m]));
509 			ret = -EINVAL;
510 			goto out;
511 		}
512 
513 		for (i = 0; i < ARRAY_SIZE(expected_freq_attrs); i++) {
514 			bool expected, found;
515 
516 			switch (expected_freq_attrs[i].category) {
517 			case ATTR_ALWAYS:
518 				expected = true;
519 				break;
520 			case ATTR_PREFCORE:
521 				expected = has_prefcore;
522 				break;
523 			case ATTR_EPP:
524 				expected = (modes[m] == AMD_PSTATE_ACTIVE);
525 				break;
526 			case ATTR_FLOOR_FREQ:
527 				expected = has_floor_freq;
528 				break;
529 			default:
530 				expected = false;
531 				break;
532 			}
533 
534 			found = attr_in_driver(driver_attrs,
535 					       expected_freq_attrs[i].name);
536 
537 			if (expected != found) {
538 				pr_err("%s: mode %s: attr %s expected %s but is %s\n",
539 				       __func__,
540 				       amd_pstate_get_mode_string(modes[m]),
541 				       expected_freq_attrs[i].name,
542 				       expected ? "visible" : "hidden",
543 				       found ? "visible" : "hidden");
544 				ret = -EINVAL;
545 				goto out;
546 			}
547 		}
548 	}
549 
550 	ret = 0;
551 out:
552 	amd_pstate_set_mode(orig_mode);
553 	return ret;
554 }
555 
556 static int __init amd_pstate_ut_init(void)
557 {
558 	u32 i = 0, arr_size = ARRAY_SIZE(amd_pstate_ut_cases);
559 
560 	for (i = 0; i < arr_size; i++) {
561 		int ret;
562 
563 		if (test_list && *test_list &&
564 		    !test_in_list(test_list, amd_pstate_ut_cases[i].name))
565 			continue;
566 
567 		ret = amd_pstate_ut_cases[i].func(i);
568 
569 		if (ret)
570 			pr_err("%-4d %-20s\t fail: %d!\n", i+1, amd_pstate_ut_cases[i].name, ret);
571 		else
572 			pr_info("%-4d %-20s\t success!\n", i+1, amd_pstate_ut_cases[i].name);
573 	}
574 
575 	return 0;
576 }
577 
578 static void __exit amd_pstate_ut_exit(void)
579 {
580 }
581 
582 module_init(amd_pstate_ut_init);
583 module_exit(amd_pstate_ut_exit);
584 
585 MODULE_AUTHOR("Meng Li <li.meng@amd.com>");
586 MODULE_DESCRIPTION("AMD P-state driver Test module");
587 MODULE_LICENSE("GPL");
588