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