All of lore.kernel.org
 help / color / mirror / Atom feed
From: Meng Li <li.meng@amd.com>
To: Shuah Khan <skhan@linuxfoundation.org>,
	"Rafael J . Wysocki" <rafael.j.wysocki@intel.com>,
	Huang Rui <ray.huang@amd.com>, <linux-pm@vger.kernel.org>
Cc: Nathan Fontenot <nathan.fontenot@amd.com>,
	Deepak Sharma <deepak.sharma@amd.com>,
	Alex Deucher <alexander.deucher@amd.com>,
	"Mario Limonciello" <mario.limonciello@amd.com>,
	Jinzhou Su <Jinzhou.Su@amd.com>, Perry Yuan <Perry.Yuan@amd.com>,
	Xiaojian Du <Xiaojian.Du@amd.com>,
	"Viresh Kumar" <viresh.kumar@linaro.org>,
	Borislav Petkov <bp@alien8.de>, <linux-kernel@vger.kernel.org>,
	Meng Li <li.meng@amd.com>
Subject: [PATCH 2/3] cpupower: Introduce a new unit test module for AMD P-State driver
Date: Wed, 23 Mar 2022 15:15:01 +0800	[thread overview]
Message-ID: <20220323071502.2674156-3-li.meng@amd.com> (raw)
In-Reply-To: <20220323071502.2674156-1-li.meng@amd.com>

amd-pstate-ut is a kernel module for testing the functions of AMD P-State driver.

It can verify the required conditions and basic functions of AMD P-State
driver before integration test.

Signed-off-by: Meng Li <li.meng@amd.com>
---
 tools/power/cpupower/debug/kernel/Makefile    |  10 +-
 .../cpupower/debug/kernel/amd-pstate-ut.c     | 618 ++++++++++++++++++
 2 files changed, 627 insertions(+), 1 deletion(-)
 create mode 100644 tools/power/cpupower/debug/kernel/amd-pstate-ut.c

diff --git a/tools/power/cpupower/debug/kernel/Makefile b/tools/power/cpupower/debug/kernel/Makefile
index 7b5c43684be1..314827f106b4 100644
--- a/tools/power/cpupower/debug/kernel/Makefile
+++ b/tools/power/cpupower/debug/kernel/Makefile
@@ -8,11 +8,19 @@ ifeq ("$(CONFIG_X86_TSC)", "y")
   obj-m	 += cpufreq-test_tsc.o
 endif
 
+amd_pstate_ut-y := amd-pstate-ut.o
+
+ifeq ("$(CONFIG_X86_AMD_PSTATE)", "y")
+  obj-m	 += amd_pstate_ut.o
+else ifeq ("$(CONFIG_X86_AMD_PSTATE)", "m")
+  obj-m	 += amd_pstate_ut.o
+endif
+
 default:
 	$(MAKE) -C $(KDIR) M=$(CURDIR)
 
 clean:
-	- rm -rf *.o *.ko .*.cmd .*.mod.* *.mod.c
+	- rm -rf *.o *.ko .*.cmd *.mod .*.mod.* *.mod.c
 	- rm -rf Module.symvers modules.order
 
 install: default
diff --git a/tools/power/cpupower/debug/kernel/amd-pstate-ut.c b/tools/power/cpupower/debug/kernel/amd-pstate-ut.c
new file mode 100644
index 000000000000..0cb08e50947b
--- /dev/null
+++ b/tools/power/cpupower/debug/kernel/amd-pstate-ut.c
@@ -0,0 +1,618 @@
+// SPDX-License-Identifier: GPL-1.0-or-later
+/*
+ * AMD Processor P-state Frequency Driver Unit Test
+ *
+ * Copyright (C) 2022 Advanced Micro Devices, Inc. All Rights Reserved.
+ *
+ * Author: Meng Li <li.meng@amd.com>
+ *
+ */
+
+#define pr_fmt(fmt) "AMD P-state UT: " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/fs.h>
+#include <linux/amd-pstate.h>
+
+#include <acpi/cppc_acpi.h>
+
+#define AMD_PSTATE_UT_ERR(...) pr_err(__VA_ARGS__)
+#define AMD_PSTATE_UT_DEBUG(...) pr_debug(__VA_ARGS__)
+#define AMD_PSTATE_UT_INFO(...) pr_info(__VA_ARGS__)
+
+#define UT_LOG_BEGIN(index) AMD_PSTATE_UT_DEBUG("****** Begin %-5d\t %-20s\t ******\n", index, ut_cases[index].name)
+#define UT_LOG_END(index) AMD_PSTATE_UT_DEBUG("****** End   %-5d\t %-20s\t ******\n", index, ut_cases[index].name)
+
+enum ut_index {
+	UT_INDEX_BEGIN=0,
+	UT_INDEX_END,
+	MAX_UT_INDEX,
+};
+
+enum ut_result {
+	UT_RESULT_NOT_TEST=0,	//' '
+	UT_RESULT_PASS,		//'P'
+	UT_RESULT_FAIL,		//'F'
+	MAX_UT_RESULT,
+};
+
+struct ut_struct {
+	const char *name;
+	void (*func)(u32 index);
+	u32 index;
+	enum ut_result result;
+};
+
+static enum ut_result ut_special(u32 begin_index,
+		u32 end_index);
+
+static void ut_stop(u32 index);
+static void ut_all(u32 index);
+static void ut_x86_vendor(u32 index);
+static void ut_acpi_cpc(u32 index);
+static void ut_modprobed_driver(u32 index);
+static void ut_capability_check(u32 index);
+static void ut_enable(u32 index);
+static void ut_init_perf(u32 index);
+static void ut_support_boost(u32 index);
+static void ut_clear_status(u32 index);
+
+/* Amd-pstate unit test cases
+ * 0 : stop
+ * 1 : all
+ * n : from n to start
+ */
+static u32 ut_array_index[MAX_UT_INDEX] = {0};
+
+static struct ut_struct ut_cases[] = {
+	{"stop",                ut_stop                }, //0
+	{"all",                 ut_all                 }, //1
+	{"x86_vendor",          ut_x86_vendor          },
+	{"acpi_cpc_valid",      ut_acpi_cpc            },
+	{"modprobed_driver",    ut_modprobed_driver    },
+	{"capability_check",    ut_capability_check    },
+	{"enable",              ut_enable              },
+	{"init_perf",           ut_init_perf           },
+	{"support_boost",       ut_support_boost       },
+	{"clear_status",        ut_clear_status        } //max
+};
+
+static bool get_shared_mem(void)
+{
+	bool result = false;
+	char buf[5] = {0};
+	struct file *filp = NULL;
+	loff_t pos = 0;
+	ssize_t ret;
+
+	filp = filp_open("/sys/module/amd_pstate/parameters/shared_mem", FMODE_PREAD, 0);
+	if (IS_ERR(filp))
+	{
+		AMD_PSTATE_UT_ERR("%s Open param file fail!", __func__);
+	}
+	else
+	{
+		ret = kernel_read(filp, &buf, sizeof(buf), &pos);
+		if (ret < 0)
+		{
+			AMD_PSTATE_UT_ERR("%s ret=%ld unable to read from param file!", __func__, ret);
+		}
+	}
+	filp_close(filp, NULL);
+
+	if ('Y' == *buf)
+	{
+		result = true;
+	}
+
+	return (result);
+}
+
+static bool ut_check_index(u32 index,
+		const char *func_name)
+{
+	bool ret = true;
+
+	if (index != ut_cases[index].index)
+	{
+		AMD_PSTATE_UT_ERR("%s index=%d is error!", func_name, index);
+		ret = false;
+	}
+
+	return (ret);
+}
+
+static void ut_stop(u32 index)
+{
+	if (!ut_check_index(index, __func__))
+	{
+		return;
+	}
+
+	AMD_PSTATE_UT_DEBUG("!====! Stop unit test! !====! \n");
+
+	//memset begin/end index
+	memset(ut_array_index, 0, sizeof(ut_array_index));
+}
+
+static void ut_all(u32 index)
+{
+	u32 arr_size = ARRAY_SIZE(ut_cases);
+	enum ut_result result = UT_RESULT_PASS;
+
+	if (!ut_check_index(index, __func__))
+	{
+		return;
+	}
+
+	if (index+1 < arr_size)
+	{
+		AMD_PSTATE_UT_DEBUG("!====! Begin all unit test! !====!\n");
+
+		//Not include ut_clear_status
+		result = ut_special(index+1, arr_size-1-1);
+
+		AMD_PSTATE_UT_DEBUG("!====! End   all unit test! !====!\n");
+	}
+	else
+	{
+		result = UT_RESULT_FAIL;
+		AMD_PSTATE_UT_ERR("%s arr_size=%d is error!", __func__, arr_size);
+	}
+	ut_cases[index].result = result;
+}
+
+static enum ut_result ut_special(u32 begin_index,
+		u32 end_index)
+{
+	u32 i = 0;
+	enum ut_result result = UT_RESULT_PASS;
+
+	for (i=begin_index; i<=end_index; i++)
+	{
+		if ((0 == ut_array_index[UT_INDEX_BEGIN]) &&
+			(0 == ut_array_index[UT_INDEX_END]))
+		{
+			AMD_PSTATE_UT_DEBUG("!====! Stop unit test : End %-5d\t %-20s\t! !====! \n", i, ut_cases[i].name);
+			break;
+		}
+		else
+		{
+			ut_cases[i].func(i);
+			if (UT_RESULT_PASS != ut_cases[i].result)
+			{
+				result = ut_cases[i].result;
+			}
+		}
+	}
+
+	return (result);
+}
+
+static void ut_x86_vendor(u32 index)
+{
+	if (!ut_check_index(index, __func__))
+	{
+		return;
+	}
+
+	UT_LOG_BEGIN(index);
+
+	if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD)
+	{
+		ut_cases[index].result = UT_RESULT_PASS;
+	}
+	else
+	{
+		ut_cases[index].result = UT_RESULT_FAIL;
+	}
+
+	UT_LOG_END(index);
+}
+
+static void ut_acpi_cpc(u32 index)
+{
+	if (!ut_check_index(index, __func__))
+	{
+		return;
+	}
+
+	UT_LOG_BEGIN(index);
+
+	if (acpi_cpc_valid())
+	{
+		ut_cases[index].result = UT_RESULT_PASS;
+	}
+	else
+	{
+		ut_cases[index].result = UT_RESULT_FAIL;
+	}
+
+	UT_LOG_END(index);
+}
+
+static void ut_modprobed_driver(u32 index)
+{
+	if (!ut_check_index(index, __func__))
+	{
+		return;
+	}
+
+	UT_LOG_BEGIN(index);
+
+	if (cpufreq_get_current_driver())
+	{
+		ut_cases[index].result = UT_RESULT_PASS;
+	}
+	else
+	{
+		ut_cases[index].result = UT_RESULT_FAIL;
+	}
+
+	UT_LOG_END(index);
+}
+
+static void ut_capability_check(u32 index)
+{
+	if (!ut_check_index(index, __func__))
+	{
+		return;
+	}
+
+	UT_LOG_BEGIN(index);
+
+	if (boot_cpu_has(X86_FEATURE_CPPC))
+	{
+		ut_cases[index].result = UT_RESULT_PASS;
+	}
+	else
+	{
+		//shared memory
+		if (get_shared_mem())
+		{
+			ut_cases[index].result = UT_RESULT_PASS;
+		}
+		else
+		{
+			ut_cases[index].result = UT_RESULT_FAIL;
+		}
+	}
+
+	UT_LOG_END(index);
+}
+
+static void ut_pstate_enable(u32 index)
+{
+	int ret = 0;
+	u64 cppc_enable = 0;
+
+	ret = rdmsrl_safe(MSR_AMD_CPPC_ENABLE, &cppc_enable);
+	if (ret)
+	{
+		ut_cases[index].result = UT_RESULT_FAIL;
+		AMD_PSTATE_UT_ERR("%s rdmsrl_safe MSR_AMD_CPPC_ENABLE ret=%d is error!", __func__, ret);
+		return;
+	}
+	if (cppc_enable)
+	{
+		ut_cases[index].result = UT_RESULT_PASS;
+	}
+	else
+	{
+		ut_cases[index].result = UT_RESULT_FAIL;
+	}
+}
+
+static void ut_enable(u32 index)
+{
+	if (!ut_check_index(index, __func__))
+	{
+		return;
+	}
+
+	UT_LOG_BEGIN(index);
+
+	if (get_shared_mem())
+	{
+		//not check
+		ut_cases[index].result = UT_RESULT_PASS;
+	}
+	else
+	{
+		ut_pstate_enable(index);
+	}
+
+	UT_LOG_END(index);
+}
+
+static void ut_init_perf(u32 index)
+{
+	int cpu = 0, ret = 0;
+	u32 highest_perf = 0, nominal_perf = 0, lowest_nonlinear_perf = 0, lowest_perf = 0;
+	u64 cap1 = 0;
+	struct cppc_perf_caps cppc_perf;
+	struct cpufreq_policy *policy = NULL;
+        struct amd_cpudata *cpudata = NULL;
+
+	if (!ut_check_index(index, __func__))
+	{
+		return;
+	}
+
+	UT_LOG_BEGIN(index);
+
+	//get perf
+	highest_perf = amd_get_highest_perf();
+
+	for_each_possible_cpu(cpu)
+	{
+		//get amd cpudata
+		policy = cpufreq_cpu_get(cpu);
+		if (!policy)
+			break;
+		cpudata = policy->driver_data;
+
+		if (get_shared_mem())
+		{
+			ret = cppc_get_perf_caps(cpu, &cppc_perf);
+			if (ret)
+			{
+				ut_cases[index].result = UT_RESULT_FAIL;
+				AMD_PSTATE_UT_ERR("%s cppc_get_perf_caps ret=%d is error!", __func__, ret);
+				return;
+			}
+
+			nominal_perf = cppc_perf.nominal_perf;
+			lowest_nonlinear_perf = cppc_perf.lowest_nonlinear_perf;
+			lowest_perf = cppc_perf.lowest_perf;
+		}
+		else
+		{
+			ret = rdmsrl_safe_on_cpu(cpu, MSR_AMD_CPPC_CAP1, &cap1);
+			if (ret)
+			{
+				ut_cases[index].result = UT_RESULT_FAIL;
+				AMD_PSTATE_UT_ERR("%s rdmsrl_safe_on_cpu MSR_AMD_CPPC_CAP1 ret=%d is error!", __func__, ret);
+				return;
+			}
+
+			//get perf from MSR
+			nominal_perf = AMD_CPPC_NOMINAL_PERF(cap1);
+			lowest_nonlinear_perf = AMD_CPPC_LOWNONLIN_PERF(cap1);
+			lowest_perf = AMD_CPPC_LOWEST_PERF(cap1);
+		}
+
+		//check highest_perf,nominal_perf,lowest_nonlinear_perf
+		if ((highest_perf != READ_ONCE(cpudata->highest_perf)) ||
+			(nominal_perf != READ_ONCE(cpudata->nominal_perf)) ||
+			(lowest_nonlinear_perf != READ_ONCE(cpudata->lowest_nonlinear_perf)) ||
+			(lowest_perf != READ_ONCE(cpudata->lowest_perf)))
+		{
+			ut_cases[index].result = UT_RESULT_FAIL;
+			AMD_PSTATE_UT_ERR("%s cpu%d highest_perf=%d %d nominal_perf=%d %d lowest_nonlinear_perf=%d %d lowest_perf=%d %d are not equal!",
+					__func__, cpu, highest_perf, cpudata->highest_perf,
+					nominal_perf, cpudata->nominal_perf,
+					lowest_nonlinear_perf, cpudata->lowest_nonlinear_perf,
+					lowest_perf, cpudata->lowest_perf);
+			return;
+		}
+	}
+
+	ut_cases[index].result = UT_RESULT_PASS;
+
+	UT_LOG_END(index);
+}
+
+static void ut_support_boost(u32 index)
+{
+	int cpu = 0;
+	struct cpufreq_policy *policy = NULL;
+        struct amd_cpudata *cpudata = NULL;
+
+	if (!ut_check_index(index, __func__))
+	{
+		return;
+	}
+
+	UT_LOG_BEGIN(index);
+
+	for_each_possible_cpu(cpu)
+	{
+		//get amd cpudata
+		policy = cpufreq_cpu_get(cpu);
+		if (!policy)
+			break;
+		cpudata = policy->driver_data;
+
+		if (READ_ONCE(cpudata->boost_supported))
+		{
+			ut_cases[index].result = UT_RESULT_PASS;
+		}
+		else
+		{
+			ut_cases[index].result = UT_RESULT_FAIL;
+			break;
+		}
+	}
+
+	UT_LOG_END(index);
+}
+
+static void ut_clear_status(u32 index)
+{
+	int i = 0;
+
+	if (!ut_check_index(index, __func__))
+	{
+		return;
+	}
+
+	UT_LOG_BEGIN(index);
+
+	for (i = 0; i < ARRAY_SIZE(ut_cases); i++)
+	{
+		ut_cases[i].result = UT_RESULT_NOT_TEST;
+	}
+
+	//memset begin/end index
+	memset(ut_array_index, 0, sizeof(ut_array_index));
+
+	UT_LOG_END(index);
+}
+
+static int param_set_index(const char *str)
+{
+	int i = 0, result = 0;
+
+	//memset begin/end index
+	memset(ut_array_index, 0, sizeof(ut_array_index));
+
+	//get begin/end index
+	while ((NULL != str) && ('\0' != *str) && ('\n' != *str))
+	{
+		ut_array_index[i] = ut_array_index[i]*10 + *str - '0';
+		str++;
+		if (' ' == *str)
+		{
+			str++;
+			i++;
+			if (MAX_UT_INDEX <= i)
+			{
+				result = -EINVAL;
+				AMD_PSTATE_UT_ERR("%s arg_num=%d, Invalid argument! \n", __func__, i);
+				break;
+			}
+		}
+	}
+
+	AMD_PSTATE_UT_DEBUG("%s %d %d result=%d! \n", __func__, ut_array_index[0], ut_array_index[1], result);
+
+	return (result);
+}
+
+static void ut_init_index(void)
+{
+	int i = 0;
+
+	if (1 != ut_cases[1].index)
+	{
+		//init all unit test cases index
+		for (i = 0; i < ARRAY_SIZE(ut_cases); i++)
+		{
+			ut_cases[i].index = i;
+		}
+	}
+}
+
+/* Parameters */
+static int param_set_unit_test(const char *buffer,
+		const struct kernel_param *kp)
+{
+	int result = 0;
+	u32 begin_index = 0, end_index = 0;
+
+	//init test case index
+	ut_init_index();
+
+	//get begin/end index
+	result = param_set_index(buffer);
+	if (result < 0)
+		return result;
+
+	begin_index  = ut_array_index[UT_INDEX_BEGIN];
+	end_index  = ut_array_index[UT_INDEX_END];
+	if (0 == begin_index)
+	{
+		ut_array_index[UT_INDEX_END] = 0;
+		ut_stop(0);
+	}
+	else if (1 == begin_index)
+	{
+		//Not include ut_clear_status
+		ut_array_index[UT_INDEX_END] = ARRAY_SIZE(ut_cases) - 1 - 1;
+		ut_all(1);
+	}
+	else
+	{
+		if (begin_index > end_index)
+		{
+			end_index = begin_index;
+			ut_array_index[UT_INDEX_END] = end_index;
+		}
+		ut_special(begin_index, end_index);
+	}
+
+	return result;
+};
+
+static int param_get_unit_test(char *buffer,
+		const struct kernel_param *kp)
+{
+	char ret = 'N';
+	int i = 0, result = 0;
+
+	result = sprintf(buffer, "%-5s\t %-20s\t Status\n", "Index", "Test cases");
+
+	for (i = 0; i < ARRAY_SIZE(ut_cases); i++)
+	{
+		switch (ut_cases[i].result)
+		{
+		case UT_RESULT_NOT_TEST:
+			ret = ' ';
+			break;
+
+		case UT_RESULT_PASS:
+			ret = 'P';
+			break;
+
+		case UT_RESULT_FAIL:
+			ret = 'F';
+			break;
+
+		default:
+			ret = ' ';
+			break;
+		}
+
+		result += sprintf(buffer + result, "%-5d\t %-20s\t [%c]\n",
+				ut_cases[i].index,
+				ut_cases[i].name,
+				ret);
+	}
+
+	result += sprintf(buffer + result, "------------------------------------------\nbegin_index = %d end_index= %d\n",
+			ut_array_index[UT_INDEX_BEGIN], ut_array_index[UT_INDEX_END]);
+
+	return result;
+};
+
+static const struct kernel_param_ops param_ops_unittest = {
+	.set = param_set_unit_test,
+	.get = param_get_unit_test,
+};
+
+module_param_cb(unit_test, &param_ops_unittest, &ut_array_index, 0664);
+MODULE_PARM_DESC(unit_test,
+		" AMD P-state unit test 0 : stop 1 : start all x: start from x");
+
+/* Module */
+static int __init amd_pstate_ut_init(void)
+{
+	//init test case index
+	ut_init_index();
+
+	return 0;
+}
+
+static void __exit amd_pstate_ut_exit(void)
+{
+}
+
+module_init(amd_pstate_ut_init);
+module_exit(amd_pstate_ut_exit);
+
+MODULE_AUTHOR("Meng Li <li.meng@amd.com>");
+MODULE_DESCRIPTION("Unit test for AMD P-state driver");
+MODULE_LICENSE("GPL");
-- 
2.25.1


  parent reply	other threads:[~2022-03-23  7:16 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-03-23  7:14 [PATCH 0/3] Add unit test module for AMD P-State driver Meng Li
2022-03-23  7:15 ` [PATCH 1/3] cpufreq: amd-pstate: Expose struct amd_cpudata Meng Li
2022-03-23  7:15 ` Meng Li [this message]
2022-03-23 14:15   ` [PATCH 2/3] cpupower: Introduce a new unit test module for AMD P-State driver Shuah Khan
2022-03-28 13:57     ` Meng, Li (Jassmine)
2022-03-28 14:56       ` Shuah Khan
2022-03-23  7:15 ` [PATCH 3/3] Documentation: amd-pstate: Add unit test introduction Meng Li

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20220323071502.2674156-3-li.meng@amd.com \
    --to=li.meng@amd.com \
    --cc=Jinzhou.Su@amd.com \
    --cc=Perry.Yuan@amd.com \
    --cc=Xiaojian.Du@amd.com \
    --cc=alexander.deucher@amd.com \
    --cc=bp@alien8.de \
    --cc=deepak.sharma@amd.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pm@vger.kernel.org \
    --cc=mario.limonciello@amd.com \
    --cc=nathan.fontenot@amd.com \
    --cc=rafael.j.wysocki@intel.com \
    --cc=ray.huang@amd.com \
    --cc=skhan@linuxfoundation.org \
    --cc=viresh.kumar@linaro.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.