xref: /linux/drivers/cpufreq/amd-pstate-ut.c (revision de85416e95c9cc9b18a3710e2554630f842b8334)
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