brintos

brintos / linux-shallow public Read only

0
0
Text · 9.6 KiB · f667015 Raw
329 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * AMD Processor P-state Frequency Driver Unit Test4 *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-pstate10 * driver. 1) It can help all users to verify their processor support11 * (SBIOS/Firmware or Hardware). 2) Kernel can have a basic function12 * test to avoid the kernel regression during the update. 3) We can13 * introduce more functional or performance tests to align the result14 * together, it will benefit power and performance scale optimization.15 *16 * This driver implements basic framework with plans to enhance it with17 * additional test cases to improve the depth and coverage of the test.18 *19 * See Documentation/admin-guide/pm/amd-pstate.rst Unit Tests for20 * amd-pstate to get more detail.21 */22 23#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt24 25#include <linux/kernel.h>26#include <linux/module.h>27#include <linux/moduleparam.h>28#include <linux/fs.h>29 30#include <acpi/cppc_acpi.h>31 32#include "amd-pstate.h"33 34/*35 * Abbreviations:36 * amd_pstate_ut: used as a shortform for AMD P-State unit test.37 * It helps to keep variable names smaller, simpler38 */39enum amd_pstate_ut_result {40	AMD_PSTATE_UT_RESULT_PASS,41	AMD_PSTATE_UT_RESULT_FAIL,42};43 44struct amd_pstate_ut_struct {45	const char *name;46	void (*func)(u32 index);47	enum amd_pstate_ut_result result;48};49 50/*51 * Kernel module for testing the AMD P-State unit test52 */53static void amd_pstate_ut_acpi_cpc_valid(u32 index);54static void amd_pstate_ut_check_enabled(u32 index);55static void amd_pstate_ut_check_perf(u32 index);56static void amd_pstate_ut_check_freq(u32 index);57static void amd_pstate_ut_check_driver(u32 index);58 59static struct amd_pstate_ut_struct amd_pstate_ut_cases[] = {60	{"amd_pstate_ut_acpi_cpc_valid",   amd_pstate_ut_acpi_cpc_valid   },61	{"amd_pstate_ut_check_enabled",    amd_pstate_ut_check_enabled    },62	{"amd_pstate_ut_check_perf",       amd_pstate_ut_check_perf       },63	{"amd_pstate_ut_check_freq",       amd_pstate_ut_check_freq       },64	{"amd_pstate_ut_check_driver",	   amd_pstate_ut_check_driver     }65};66 67static bool get_shared_mem(void)68{69	bool result = false;70 71	if (!boot_cpu_has(X86_FEATURE_CPPC))72		result = true;73 74	return result;75}76 77/*78 * check the _CPC object is present in SBIOS.79 */80static void amd_pstate_ut_acpi_cpc_valid(u32 index)81{82	if (acpi_cpc_valid())83		amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_PASS;84	else {85		amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_FAIL;86		pr_err("%s the _CPC object is not present in SBIOS!\n", __func__);87	}88}89 90static void amd_pstate_ut_pstate_enable(u32 index)91{92	int ret = 0;93	u64 cppc_enable = 0;94 95	ret = rdmsrl_safe(MSR_AMD_CPPC_ENABLE, &cppc_enable);96	if (ret) {97		amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_FAIL;98		pr_err("%s rdmsrl_safe MSR_AMD_CPPC_ENABLE ret=%d error!\n", __func__, ret);99		return;100	}101	if (cppc_enable)102		amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_PASS;103	else {104		amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_FAIL;105		pr_err("%s amd pstate must be enabled!\n", __func__);106	}107}108 109/*110 * check if amd pstate is enabled111 */112static void amd_pstate_ut_check_enabled(u32 index)113{114	if (get_shared_mem())115		amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_PASS;116	else117		amd_pstate_ut_pstate_enable(index);118}119 120/*121 * check if performance values are reasonable.122 * highest_perf >= nominal_perf > lowest_nonlinear_perf > lowest_perf > 0123 */124static void amd_pstate_ut_check_perf(u32 index)125{126	int cpu = 0, ret = 0;127	u32 highest_perf = 0, nominal_perf = 0, lowest_nonlinear_perf = 0, lowest_perf = 0;128	u64 cap1 = 0;129	struct cppc_perf_caps cppc_perf;130	struct cpufreq_policy *policy = NULL;131	struct amd_cpudata *cpudata = NULL;132 133	for_each_possible_cpu(cpu) {134		policy = cpufreq_cpu_get(cpu);135		if (!policy)136			break;137		cpudata = policy->driver_data;138 139		if (get_shared_mem()) {140			ret = cppc_get_perf_caps(cpu, &cppc_perf);141			if (ret) {142				amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_FAIL;143				pr_err("%s cppc_get_perf_caps ret=%d error!\n", __func__, ret);144				goto skip_test;145			}146 147			highest_perf = cppc_perf.highest_perf;148			nominal_perf = cppc_perf.nominal_perf;149			lowest_nonlinear_perf = cppc_perf.lowest_nonlinear_perf;150			lowest_perf = cppc_perf.lowest_perf;151		} else {152			ret = rdmsrl_safe_on_cpu(cpu, MSR_AMD_CPPC_CAP1, &cap1);153			if (ret) {154				amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_FAIL;155				pr_err("%s read CPPC_CAP1 ret=%d error!\n", __func__, ret);156				goto skip_test;157			}158 159			highest_perf = AMD_CPPC_HIGHEST_PERF(cap1);160			nominal_perf = AMD_CPPC_NOMINAL_PERF(cap1);161			lowest_nonlinear_perf = AMD_CPPC_LOWNONLIN_PERF(cap1);162			lowest_perf = AMD_CPPC_LOWEST_PERF(cap1);163		}164 165		if (highest_perf != READ_ONCE(cpudata->highest_perf) && !cpudata->hw_prefcore) {166			pr_err("%s cpu%d highest=%d %d highest perf doesn't match\n",167				__func__, cpu, highest_perf, cpudata->highest_perf);168			goto skip_test;169		}170		if ((nominal_perf != READ_ONCE(cpudata->nominal_perf)) ||171			(lowest_nonlinear_perf != READ_ONCE(cpudata->lowest_nonlinear_perf)) ||172			(lowest_perf != READ_ONCE(cpudata->lowest_perf))) {173			amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_FAIL;174			pr_err("%s cpu%d nominal=%d %d lowest_nonlinear=%d %d lowest=%d %d, they should be equal!\n",175				__func__, cpu, nominal_perf, cpudata->nominal_perf,176				lowest_nonlinear_perf, cpudata->lowest_nonlinear_perf,177				lowest_perf, cpudata->lowest_perf);178			goto skip_test;179		}180 181		if (!((highest_perf >= nominal_perf) &&182			(nominal_perf > lowest_nonlinear_perf) &&183			(lowest_nonlinear_perf > lowest_perf) &&184			(lowest_perf > 0))) {185			amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_FAIL;186			pr_err("%s cpu%d highest=%d >= nominal=%d > lowest_nonlinear=%d > lowest=%d > 0, the formula is incorrect!\n",187				__func__, cpu, highest_perf, nominal_perf,188				lowest_nonlinear_perf, lowest_perf);189			goto skip_test;190		}191		cpufreq_cpu_put(policy);192	}193 194	amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_PASS;195	return;196skip_test:197	cpufreq_cpu_put(policy);198}199 200/*201 * Check if frequency values are reasonable.202 * max_freq >= nominal_freq > lowest_nonlinear_freq > min_freq > 0203 * check max freq when set support boost mode.204 */205static void amd_pstate_ut_check_freq(u32 index)206{207	int cpu = 0;208	struct cpufreq_policy *policy = NULL;209	struct amd_cpudata *cpudata = NULL;210	u32 nominal_freq_khz;211 212	for_each_possible_cpu(cpu) {213		policy = cpufreq_cpu_get(cpu);214		if (!policy)215			break;216		cpudata = policy->driver_data;217 218		nominal_freq_khz = cpudata->nominal_freq*1000;219		if (!((cpudata->max_freq >= nominal_freq_khz) &&220			(nominal_freq_khz > cpudata->lowest_nonlinear_freq) &&221			(cpudata->lowest_nonlinear_freq > cpudata->min_freq) &&222			(cpudata->min_freq > 0))) {223			amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_FAIL;224			pr_err("%s cpu%d max=%d >= nominal=%d > lowest_nonlinear=%d > min=%d > 0, the formula is incorrect!\n",225				__func__, cpu, cpudata->max_freq, nominal_freq_khz,226				cpudata->lowest_nonlinear_freq, cpudata->min_freq);227			goto skip_test;228		}229 230		if (cpudata->min_freq != policy->min) {231			amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_FAIL;232			pr_err("%s cpu%d cpudata_min_freq=%d policy_min=%d, they should be equal!\n",233				__func__, cpu, cpudata->min_freq, policy->min);234			goto skip_test;235		}236 237		if (cpudata->boost_supported) {238			if ((policy->max == cpudata->max_freq) ||239					(policy->max == nominal_freq_khz))240				amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_PASS;241			else {242				amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_FAIL;243				pr_err("%s cpu%d policy_max=%d should be equal cpu_max=%d or cpu_nominal=%d !\n",244					__func__, cpu, policy->max, cpudata->max_freq,245					nominal_freq_khz);246				goto skip_test;247			}248		} else {249			amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_FAIL;250			pr_err("%s cpu%d must support boost!\n", __func__, cpu);251			goto skip_test;252		}253		cpufreq_cpu_put(policy);254	}255 256	amd_pstate_ut_cases[index].result = AMD_PSTATE_UT_RESULT_PASS;257	return;258skip_test:259	cpufreq_cpu_put(policy);260}261 262static int amd_pstate_set_mode(enum amd_pstate_mode mode)263{264	const char *mode_str = amd_pstate_get_mode_string(mode);265 266	pr_debug("->setting mode to %s\n", mode_str);267 268	return amd_pstate_update_status(mode_str, strlen(mode_str));269}270 271static void amd_pstate_ut_check_driver(u32 index)272{273	enum amd_pstate_mode mode1, mode2 = AMD_PSTATE_DISABLE;274	int ret;275 276	for (mode1 = AMD_PSTATE_DISABLE; mode1 < AMD_PSTATE_MAX; mode1++) {277		ret = amd_pstate_set_mode(mode1);278		if (ret)279			goto out;280		for (mode2 = AMD_PSTATE_DISABLE; mode2 < AMD_PSTATE_MAX; mode2++) {281			if (mode1 == mode2)282				continue;283			ret = amd_pstate_set_mode(mode2);284			if (ret)285				goto out;286		}287	}288out:289	if (ret)290		pr_warn("%s: failed to update status for %s->%s: %d\n", __func__,291			amd_pstate_get_mode_string(mode1),292			amd_pstate_get_mode_string(mode2), ret);293 294	amd_pstate_ut_cases[index].result = ret ?295					    AMD_PSTATE_UT_RESULT_FAIL :296					    AMD_PSTATE_UT_RESULT_PASS;297}298 299static int __init amd_pstate_ut_init(void)300{301	u32 i = 0, arr_size = ARRAY_SIZE(amd_pstate_ut_cases);302 303	for (i = 0; i < arr_size; i++) {304		amd_pstate_ut_cases[i].func(i);305		switch (amd_pstate_ut_cases[i].result) {306		case AMD_PSTATE_UT_RESULT_PASS:307			pr_info("%-4d %-20s\t success!\n", i+1, amd_pstate_ut_cases[i].name);308			break;309		case AMD_PSTATE_UT_RESULT_FAIL:310		default:311			pr_info("%-4d %-20s\t fail!\n", i+1, amd_pstate_ut_cases[i].name);312			break;313		}314	}315 316	return 0;317}318 319static void __exit amd_pstate_ut_exit(void)320{321}322 323module_init(amd_pstate_ut_init);324module_exit(amd_pstate_ut_exit);325 326MODULE_AUTHOR("Meng Li <li.meng@amd.com>");327MODULE_DESCRIPTION("AMD P-state driver Test module");328MODULE_LICENSE("GPL");329