amd-pstate-ut.c 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. // SPDX-License-Identifier: GPL-1.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. #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  23. #include <linux/kernel.h>
  24. #include <linux/module.h>
  25. #include <linux/moduleparam.h>
  26. #include <linux/fs.h>
  27. #include <linux/amd-pstate.h>
  28. #include <acpi/cppc_acpi.h>
  29. /*
  30. * Abbreviations:
  31. * amd_pstate_ut: used as a shortform for AMD P-State unit test.
  32. * It helps to keep variable names smaller, simpler
  33. */
  34. enum amd_pstate_ut_result {
  35. AMD_PSTATE_UT_RESULT_PASS,
  36. AMD_PSTATE_UT_RESULT_FAIL,
  37. };
  38. struct amd_pstate_ut_struct {
  39. const char *name;
  40. void (*func)(u32 index);
  41. enum amd_pstate_ut_result result;
  42. };
  43. /*
  44. * Kernel module for testing the AMD P-State unit test
  45. */
  46. static void amd_pstate_ut_acpi_cpc_valid(u32 index);
  47. static void amd_pstate_ut_check_enabled(u32 index);
  48. static void amd_pstate_ut_check_perf(u32 index);
  49. static void amd_pstate_ut_check_freq(u32 index);
  50. static struct amd_pstate_ut_struct amd_pstate_ut_cases[] = {
  51. {"amd_pstate_ut_acpi_cpc_valid", amd_pstate_ut_acpi_cpc_valid },
  52. {"amd_pstate_ut_check_enabled", amd_pstate_ut_check_enabled },
  53. {"amd_pstate_ut_check_perf", amd_pstate_ut_check_perf },
  54. {"amd_pstate_ut_check_freq", amd_pstate_ut_check_freq }
  55. };
  56. static bool get_shared_mem(void)
  57. {
  58. bool result = false;
  59. if (!boot_cpu_has(X86_FEATURE_CPPC))
  60. result = true;
  61. return result;
  62. }
  63. /*
  64. * check the _CPC object is present in SBIOS.
  65. */
  66. static void amd_pstate_ut_acpi_cpc_valid(u32 index)
  67. {
  68. if (acpi_cpc_valid())
  69. amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_PASS;
  70. else {
  71. amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_FAIL;
  72. pr_err("%s the _CPC object is not present in SBIOS!\n", __func__);
  73. }
  74. }
  75. static void amd_pstate_ut_pstate_enable(u32 index)
  76. {
  77. int ret = 0;
  78. u64 cppc_enable = 0;
  79. ret = rdmsrl_safe(MSR_AMD_CPPC_ENABLE, &cppc_enable);
  80. if (ret) {
  81. amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_FAIL;
  82. pr_err("%s rdmsrl_safe MSR_AMD_CPPC_ENABLE ret=%d error!\n", __func__, ret);
  83. return;
  84. }
  85. if (cppc_enable)
  86. amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_PASS;
  87. else {
  88. amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_FAIL;
  89. pr_err("%s amd pstate must be enabled!\n", __func__);
  90. }
  91. }
  92. /*
  93. * check if amd pstate is enabled
  94. */
  95. static void amd_pstate_ut_check_enabled(u32 index)
  96. {
  97. if (get_shared_mem())
  98. amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_PASS;
  99. else
  100. amd_pstate_ut_pstate_enable(index);
  101. }
  102. /*
  103. * check if performance values are reasonable.
  104. * highest_perf >= nominal_perf > lowest_nonlinear_perf > lowest_perf > 0
  105. */
  106. static void amd_pstate_ut_check_perf(u32 index)
  107. {
  108. int cpu = 0, ret = 0;
  109. u32 highest_perf = 0, nominal_perf = 0, lowest_nonlinear_perf = 0, lowest_perf = 0;
  110. u64 cap1 = 0;
  111. struct cppc_perf_caps cppc_perf;
  112. struct cpufreq_policy *policy = NULL;
  113. struct amd_cpudata *cpudata = NULL;
  114. highest_perf = amd_get_highest_perf();
  115. for_each_possible_cpu(cpu) {
  116. policy = cpufreq_cpu_get(cpu);
  117. if (!policy)
  118. break;
  119. cpudata = policy->driver_data;
  120. if (get_shared_mem()) {
  121. ret = cppc_get_perf_caps(cpu, &cppc_perf);
  122. if (ret) {
  123. amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_FAIL;
  124. pr_err("%s cppc_get_perf_caps ret=%d error!\n", __func__, ret);
  125. goto skip_test;
  126. }
  127. nominal_perf = cppc_perf.nominal_perf;
  128. lowest_nonlinear_perf = cppc_perf.lowest_nonlinear_perf;
  129. lowest_perf = cppc_perf.lowest_perf;
  130. } else {
  131. ret = rdmsrl_safe_on_cpu(cpu, MSR_AMD_CPPC_CAP1, &cap1);
  132. if (ret) {
  133. amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_FAIL;
  134. pr_err("%s read CPPC_CAP1 ret=%d error!\n", __func__, ret);
  135. goto skip_test;
  136. }
  137. nominal_perf = AMD_CPPC_NOMINAL_PERF(cap1);
  138. lowest_nonlinear_perf = AMD_CPPC_LOWNONLIN_PERF(cap1);
  139. lowest_perf = AMD_CPPC_LOWEST_PERF(cap1);
  140. }
  141. if ((highest_perf != READ_ONCE(cpudata->highest_perf)) ||
  142. (nominal_perf != READ_ONCE(cpudata->nominal_perf)) ||
  143. (lowest_nonlinear_perf != READ_ONCE(cpudata->lowest_nonlinear_perf)) ||
  144. (lowest_perf != READ_ONCE(cpudata->lowest_perf))) {
  145. amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_FAIL;
  146. pr_err("%s cpu%d highest=%d %d nominal=%d %d lowest_nonlinear=%d %d lowest=%d %d, they should be equal!\n",
  147. __func__, cpu, highest_perf, cpudata->highest_perf,
  148. nominal_perf, cpudata->nominal_perf,
  149. lowest_nonlinear_perf, cpudata->lowest_nonlinear_perf,
  150. lowest_perf, cpudata->lowest_perf);
  151. goto skip_test;
  152. }
  153. if (!((highest_perf >= nominal_perf) &&
  154. (nominal_perf > lowest_nonlinear_perf) &&
  155. (lowest_nonlinear_perf > lowest_perf) &&
  156. (lowest_perf > 0))) {
  157. amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_FAIL;
  158. pr_err("%s cpu%d highest=%d >= nominal=%d > lowest_nonlinear=%d > lowest=%d > 0, the formula is incorrect!\n",
  159. __func__, cpu, highest_perf, nominal_perf,
  160. lowest_nonlinear_perf, lowest_perf);
  161. goto skip_test;
  162. }
  163. cpufreq_cpu_put(policy);
  164. }
  165. amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_PASS;
  166. return;
  167. skip_test:
  168. cpufreq_cpu_put(policy);
  169. }
  170. /*
  171. * Check if frequency values are reasonable.
  172. * max_freq >= nominal_freq > lowest_nonlinear_freq > min_freq > 0
  173. * check max freq when set support boost mode.
  174. */
  175. static void amd_pstate_ut_check_freq(u32 index)
  176. {
  177. int cpu = 0;
  178. struct cpufreq_policy *policy = NULL;
  179. struct amd_cpudata *cpudata = NULL;
  180. for_each_possible_cpu(cpu) {
  181. policy = cpufreq_cpu_get(cpu);
  182. if (!policy)
  183. break;
  184. cpudata = policy->driver_data;
  185. if (!((cpudata->max_freq >= cpudata->nominal_freq) &&
  186. (cpudata->nominal_freq > cpudata->lowest_nonlinear_freq) &&
  187. (cpudata->lowest_nonlinear_freq > cpudata->min_freq) &&
  188. (cpudata->min_freq > 0))) {
  189. amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_FAIL;
  190. pr_err("%s cpu%d max=%d >= nominal=%d > lowest_nonlinear=%d > min=%d > 0, the formula is incorrect!\n",
  191. __func__, cpu, cpudata->max_freq, cpudata->nominal_freq,
  192. cpudata->lowest_nonlinear_freq, cpudata->min_freq);
  193. goto skip_test;
  194. }
  195. if (cpudata->min_freq != policy->min) {
  196. amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_FAIL;
  197. pr_err("%s cpu%d cpudata_min_freq=%d policy_min=%d, they should be equal!\n",
  198. __func__, cpu, cpudata->min_freq, policy->min);
  199. goto skip_test;
  200. }
  201. if (cpudata->boost_supported) {
  202. if ((policy->max == cpudata->max_freq) ||
  203. (policy->max == cpudata->nominal_freq))
  204. amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_PASS;
  205. else {
  206. amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_FAIL;
  207. pr_err("%s cpu%d policy_max=%d should be equal cpu_max=%d or cpu_nominal=%d !\n",
  208. __func__, cpu, policy->max, cpudata->max_freq,
  209. cpudata->nominal_freq);
  210. goto skip_test;
  211. }
  212. } else {
  213. amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_FAIL;
  214. pr_err("%s cpu%d must support boost!\n", __func__, cpu);
  215. goto skip_test;
  216. }
  217. cpufreq_cpu_put(policy);
  218. }
  219. amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_PASS;
  220. return;
  221. skip_test:
  222. cpufreq_cpu_put(policy);
  223. }
  224. static int __init amd_pstate_ut_init(void)
  225. {
  226. u32 i = 0, arr_size = ARRAY_SIZE(amd_pstate_ut_cases);
  227. for (i = 0; i < arr_size; i++) {
  228. amd_pstate_ut_cases[i].func(i);
  229. switch (amd_pstate_ut_cases[i].result) {
  230. case AMD_PSTATE_UT_RESULT_PASS:
  231. pr_info("%-4d %-20s\t success!\n", i+1, amd_pstate_ut_cases[i].name);
  232. break;
  233. case AMD_PSTATE_UT_RESULT_FAIL:
  234. default:
  235. pr_info("%-4d %-20s\t fail!\n", i+1, amd_pstate_ut_cases[i].name);
  236. break;
  237. }
  238. }
  239. return 0;
  240. }
  241. static void __exit amd_pstate_ut_exit(void)
  242. {
  243. }
  244. module_init(amd_pstate_ut_init);
  245. module_exit(amd_pstate_ut_exit);
  246. MODULE_AUTHOR("Meng Li <[email protected]>");
  247. MODULE_DESCRIPTION("AMD P-state driver Test module");
  248. MODULE_LICENSE("GPL");