[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [PATCH v6 11/19] xen/x86: introduce "cpufreq=amd-cppc" xen cmdline and amd-cppc driver
Users need to set "cpufreq=amd-cppc" in xen cmdline to enable amd-cppc driver, which selects ACPI Collaborative Performance and Power Control (CPPC) on supported AMD hardware to provide a finer grained frequency control mechanism. `verbose` option can also be included to support verbose print. When users setting "cpufreq=amd-cppc", a new amd-cppc driver shall be registered and used. All hooks for amd-cppc driver are transiently missing, and we temporarily make registration fail with -EOPNOTSUPP here. It will be fixed along with the implementation. New xen-pm internal flag XEN_PROCESSOR_PM_CPPC is introduced, to stand for cpufreq driver in CPPC mode. We define XEN_PROCESSOR_PM_CPPC 0x100, as it is the next value to use after 8-bits wide public xen-pm options. We also add sanity check on compile time. All XEN_PROCESSOR_PM_xxx checking shall be updated to consider "XEN_PROCESSOR_PM_CPPC" too. XEN_PROCESSOR_PM_CPPC and XEN_PROCESSOR_PM_PX are firstly set when Xen parsed relative driver signature from xen cmdline, and will become exclusive after cpufreq driver registration. It is because that platform could not support both or mixed mode (CPPC & legacy Px) operations, and only one cpufreq driver could be registerd in Xen at one time, such as on AMD, it is either amd-cppc or legacy P-states driver. Xen rely on XEN_PROCESSOR_PM_CPPC flag to tell current cpufreq driver is in CPPC mode, and accepts relative hypercall. It will neglect Px request and yields success. Signed-off-by: Penny Zheng <Penny.Zheng@xxxxxxx> --- v1 -> v2: - Obey to alphabetic sorting and also strict it with CONFIG_AMD - Remove unnecessary empty comment line - Use __initconst_cf_clobber for pre-filled structure cpufreq_driver - Make new switch-case code apply to Hygon CPUs too - Change ENOSYS with EOPNOTSUPP - Blanks around binary operator - Change all amd_/-pstate defined values to amd_/-cppc --- v2 -> v3 - refactor too long lines - Make sure XEN_PROCESSOR_PM_PX and XEN_PROCESSOR_PM_CPPC incompatible flags after cpufreq register registrantion --- v3 -> v4: - introduce XEN_PROCESSOR_PM_CPPC in xen internal header - complement "Hygon" in log message - remove unnecessary if() - grow cpufreq_xen_opts[] array --- v4 -> v5: - remove XEN_PROCESSOR_PM_xxx flag sanitization from individual driver - prefer ! over "== 0" in purely boolean contexts - Blank line between non-fall-through case blocks - add build-time checking between internal and public XEN_PROCESSOR_PM_* values - define XEN_PROCESSOR_PM_CPPC with 0x100, as it is the next value to use after public interface, and public mask SIF_PM_MASK is 8 bits wide. - as Dom0 will send the CPPC/Px data whenever it could, the return value shall be 0 instead of -ENOSYS/EOPNOTSUP when platform doesn't require these data. --- v5 -> v6: - do not register the driver when all hooks are NULL - refactor the subject and commit message - move pruning of xen_processor_pmbits into generic space - add comment and build-time check for XEN_PROCESSOR_PM_CPPC --- docs/misc/xen-command-line.pandoc | 7 ++- xen/arch/x86/acpi/cpufreq/Makefile | 1 + xen/arch/x86/acpi/cpufreq/amd-cppc.c | 59 +++++++++++++++++++ xen/arch/x86/acpi/cpufreq/cpufreq.c | 72 ++++++++++++++++++++++- xen/arch/x86/platform_hypercall.c | 14 +++++ xen/drivers/acpi/pm-op.c | 3 +- xen/drivers/acpi/pmstat.c | 3 + xen/drivers/cpufreq/cpufreq.c | 11 ++++ xen/include/acpi/cpufreq/cpufreq.h | 6 +- xen/include/acpi/cpufreq/processor_perf.h | 10 ++++ 10 files changed, 180 insertions(+), 6 deletions(-) create mode 100644 xen/arch/x86/acpi/cpufreq/amd-cppc.c diff --git a/docs/misc/xen-command-line.pandoc b/docs/misc/xen-command-line.pandoc index 6865a61220..03761d9e3c 100644 --- a/docs/misc/xen-command-line.pandoc +++ b/docs/misc/xen-command-line.pandoc @@ -515,7 +515,7 @@ If set, force use of the performance counters for oprofile, rather than detectin available support. ### cpufreq -> `= none | {{ <boolean> | xen } { [:[powersave|performance|ondemand|userspace][,[<maxfreq>]][,[<minfreq>]]] } [,verbose]} | dom0-kernel | hwp[:[<hdc>][,verbose]]` +> `= none | {{ <boolean> | xen } { [:[powersave|performance|ondemand|userspace][,[<maxfreq>]][,[<minfreq>]]] } [,verbose]} | dom0-kernel | hwp[:[<hdc>][,verbose]] | amd-cppc[:[verbose]]` > Default: `xen` @@ -526,7 +526,7 @@ choice of `dom0-kernel` is deprecated and not supported by all Dom0 kernels. * `<maxfreq>` and `<minfreq>` are integers which represent max and min processor frequencies respectively. * `verbose` option can be included as a string or also as `verbose=<integer>` - for `xen`. It is a boolean for `hwp`. + for `xen`. It is a boolean for `hwp` and `amd-cppc`. * `hwp` selects Hardware-Controlled Performance States (HWP) on supported Intel hardware. HWP is a Skylake+ feature which provides better CPU power management. The default is disabled. If `hwp` is selected, but hardware @@ -534,6 +534,9 @@ choice of `dom0-kernel` is deprecated and not supported by all Dom0 kernels. * `<hdc>` is a boolean to enable Hardware Duty Cycling (HDC). HDC enables the processor to autonomously force physical package components into idle state. The default is enabled, but the option only applies when `hwp` is enabled. +* `amd-cppc` selects ACPI Collaborative Performance and Power Control (CPPC) + on supported AMD hardware to provide finer grained frequency control + mechanism. The default is disabled. There is also support for `;`-separated fallback options: `cpufreq=hwp;xen,verbose`. This first tries `hwp` and falls back to `xen` if diff --git a/xen/arch/x86/acpi/cpufreq/Makefile b/xen/arch/x86/acpi/cpufreq/Makefile index e7dbe434a8..a2ba34bda0 100644 --- a/xen/arch/x86/acpi/cpufreq/Makefile +++ b/xen/arch/x86/acpi/cpufreq/Makefile @@ -1,4 +1,5 @@ obj-$(CONFIG_INTEL) += acpi.o +obj-$(CONFIG_AMD) += amd-cppc.o obj-y += cpufreq.o obj-$(CONFIG_INTEL) += hwp.o obj-$(CONFIG_AMD) += powernow.o diff --git a/xen/arch/x86/acpi/cpufreq/amd-cppc.c b/xen/arch/x86/acpi/cpufreq/amd-cppc.c new file mode 100644 index 0000000000..3377783f7e --- /dev/null +++ b/xen/arch/x86/acpi/cpufreq/amd-cppc.c @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * amd-cppc.c - AMD Processor CPPC Frequency Driver + * + * Copyright (C) 2025 Advanced Micro Devices, Inc. All Rights Reserved. + * + * Author: Penny Zheng <penny.zheng@xxxxxxx> + * + * AMD CPPC cpufreq driver introduces a new CPU performance scaling design + * for AMD processors using the ACPI Collaborative Performance and Power + * Control (CPPC) feature which provides finer grained frequency control range. + */ + +#include <xen/domain.h> +#include <xen/init.h> +#include <xen/param.h> +#include <acpi/cpufreq/cpufreq.h> + +static bool __init amd_cppc_handle_option(const char *s, const char *end) +{ + int ret; + + ret = parse_boolean("verbose", s, end); + if ( ret >= 0 ) + { + cpufreq_verbose = ret; + return true; + } + + return false; +} + +int __init amd_cppc_cmdline_parse(const char *s, const char *e) +{ + do { + const char *end = strpbrk(s, ",;"); + + if ( !amd_cppc_handle_option(s, end) ) + { + printk(XENLOG_WARNING + "cpufreq/amd-cppc: option '%.*s' not recognized\n", + (int)((end ?: e) - s), s); + + return -EINVAL; + } + + s = end ? end + 1 : NULL; + } while ( s && s < e ); + + return 0; +} + +int __init amd_cppc_register_driver(void) +{ + if ( !cpu_has_cppc ) + return -ENODEV; + + return -EOPNOTSUPP; +} diff --git a/xen/arch/x86/acpi/cpufreq/cpufreq.c b/xen/arch/x86/acpi/cpufreq/cpufreq.c index 45f301f354..b33699ef13 100644 --- a/xen/arch/x86/acpi/cpufreq/cpufreq.c +++ b/xen/arch/x86/acpi/cpufreq/cpufreq.c @@ -128,12 +128,14 @@ static int __init cf_check cpufreq_driver_init(void) if ( cpufreq_controller == FREQCTL_xen ) { + unsigned int i = 0; + switch ( boot_cpu_data.x86_vendor ) { case X86_VENDOR_INTEL: ret = -ENOENT; - for ( unsigned int i = 0; i < cpufreq_xen_cnt; i++ ) + for ( i = 0; i < cpufreq_xen_cnt; i++ ) { switch ( cpufreq_xen_opts[i] ) { @@ -148,6 +150,11 @@ static int __init cf_check cpufreq_driver_init(void) case CPUFREQ_none: ret = 0; break; + + default: + printk(XENLOG_WARNING + "Unsupported cpufreq driver for vendor Intel\n"); + break; } if ( !ret || ret == -EBUSY ) @@ -157,9 +164,70 @@ static int __init cf_check cpufreq_driver_init(void) case X86_VENDOR_AMD: case X86_VENDOR_HYGON: - ret = IS_ENABLED(CONFIG_AMD) ? powernow_register_driver() : -ENODEV; + if ( !IS_ENABLED(CONFIG_AMD) ) + { + ret = -ENODEV; + break; + } + ret = -ENOENT; + + for ( i = 0; i < cpufreq_xen_cnt; i++ ) + { + switch ( cpufreq_xen_opts[i] ) + { + case CPUFREQ_xen: + ret = powernow_register_driver(); + break; + + case CPUFREQ_amd_cppc: + ret = amd_cppc_register_driver(); + break; + + case CPUFREQ_none: + ret = 0; + break; + + default: + printk(XENLOG_WARNING + "Unsupported cpufreq driver for vendor AMD or Hygon\n"); + break; + } + + if ( !ret || ret == -EBUSY ) + break; + } + break; } + + /* + * After successful cpufreq driver registeration, XEN_PROCESSOR_PM_CPPC + * and XEN_PROCESSOR_PM_PX shall become exclusive flags. + */ + if ( !ret ) + { + ASSERT(i < cpufreq_xen_cnt); + switch ( cpufreq_xen_opts[i] ) + { + case CPUFREQ_amd_cppc: + xen_processor_pmbits &= ~XEN_PROCESSOR_PM_PX; + break; + + case CPUFREQ_hwp: + case CPUFREQ_xen: + xen_processor_pmbits &= ~XEN_PROCESSOR_PM_CPPC; + break; + + default: + break; + } + } else if ( ret != -EBUSY ) + /* + * No cpufreq driver gets registered, clear both + * XEN_PROCESSOR_PM_CPPC and XEN_PROCESSOR_PM_PX + */ + xen_processor_pmbits &= ~(XEN_PROCESSOR_PM_CPPC | + XEN_PROCESSOR_PM_PX); } return ret; diff --git a/xen/arch/x86/platform_hypercall.c b/xen/arch/x86/platform_hypercall.c index 42b3b8b95a..cf64b8a622 100644 --- a/xen/arch/x86/platform_hypercall.c +++ b/xen/arch/x86/platform_hypercall.c @@ -546,6 +546,8 @@ ret_t do_platform_op( ret = 0; break; } + /* Xen doesn't support mixed mode */ + ASSERT(!(xen_processor_pmbits & XEN_PROCESSOR_PM_CPPC)); ret = set_px_pminfo(op->u.set_pminfo.id, &op->u.set_pminfo.u.perf); break; @@ -578,6 +580,18 @@ ret_t do_platform_op( } case XEN_PM_CPPC: + if ( !(xen_processor_pmbits & XEN_PROCESSOR_PM_CPPC) ) + { + /* + * Neglect CPPC-info when registered cpufreq driver + * isn't in CPPC mode + */ + ret = 0; + break; + } + /* Xen doesn't support mixed mode */ + ASSERT(!(xen_processor_pmbits & XEN_PROCESSOR_PM_PX)); + ret = set_cppc_pminfo(op->u.set_pminfo.id, &op->u.set_pminfo.u.cppc_data); break; diff --git a/xen/drivers/acpi/pm-op.c b/xen/drivers/acpi/pm-op.c index 49b4067dec..d10f6db0e4 100644 --- a/xen/drivers/acpi/pm-op.c +++ b/xen/drivers/acpi/pm-op.c @@ -350,7 +350,8 @@ int do_pm_op(struct xen_sysctl_pm_op *op) switch ( op->cmd & PM_PARA_CATEGORY_MASK ) { case CPUFREQ_PARA: - if ( !(xen_processor_pmbits & XEN_PROCESSOR_PM_PX) ) + if ( !(xen_processor_pmbits & (XEN_PROCESSOR_PM_PX | + XEN_PROCESSOR_PM_CPPC)) ) return -ENODEV; if ( !pmpt || !(pmpt->init & (XEN_PX_INIT | XEN_CPPC_INIT)) ) return -EINVAL; diff --git a/xen/drivers/acpi/pmstat.c b/xen/drivers/acpi/pmstat.c index da7a1f81e1..e4e62966de 100644 --- a/xen/drivers/acpi/pmstat.c +++ b/xen/drivers/acpi/pmstat.c @@ -107,6 +107,9 @@ int cpufreq_statistic_init(unsigned int cpu) if ( !pmpt ) return -EINVAL; + if ( !(pmpt->init & XEN_PX_INIT) ) + return 0; + spin_lock(cpufreq_statistic_lock); pxpt = per_cpu(cpufreq_statistic_data, cpu); diff --git a/xen/drivers/cpufreq/cpufreq.c b/xen/drivers/cpufreq/cpufreq.c index 065fdf4106..cf1fcf1d22 100644 --- a/xen/drivers/cpufreq/cpufreq.c +++ b/xen/drivers/cpufreq/cpufreq.c @@ -98,6 +98,10 @@ static int __init handle_cpufreq_cmdline(enum cpufreq_xen_opt option) cpufreq_xen_opts[cpufreq_xen_cnt++] = option; switch ( option ) { + case CPUFREQ_amd_cppc: + xen_processor_pmbits |= XEN_PROCESSOR_PM_CPPC; + break; + case CPUFREQ_hwp: case CPUFREQ_xen: xen_processor_pmbits |= XEN_PROCESSOR_PM_PX; @@ -166,6 +170,13 @@ static int __init cf_check setup_cpufreq_option(const char *str) if ( !ret && arg[0] && arg[1] ) ret = hwp_cmdline_parse(arg + 1, end); } + else if ( IS_ENABLED(CONFIG_AMD) && choice < 0 && + !cmdline_strcmp(str, "amd-cppc") ) + { + ret = handle_cpufreq_cmdline(CPUFREQ_amd_cppc); + if ( !ret && arg[0] && arg[1] ) + ret = amd_cppc_cmdline_parse(arg + 1, end); + } else ret = -EINVAL; diff --git a/xen/include/acpi/cpufreq/cpufreq.h b/xen/include/acpi/cpufreq/cpufreq.h index 7f26b5653a..32cf905fb8 100644 --- a/xen/include/acpi/cpufreq/cpufreq.h +++ b/xen/include/acpi/cpufreq/cpufreq.h @@ -26,8 +26,9 @@ enum cpufreq_xen_opt { CPUFREQ_none, CPUFREQ_xen, CPUFREQ_hwp, + CPUFREQ_amd_cppc, }; -#define NR_CPUFREQ_OPTS 2 +#define NR_CPUFREQ_OPTS 3 extern enum cpufreq_xen_opt cpufreq_xen_opts[NR_CPUFREQ_OPTS]; extern unsigned int cpufreq_xen_cnt; struct cpufreq_governor; @@ -273,4 +274,7 @@ int set_hwp_para(struct cpufreq_policy *policy, int acpi_cpufreq_register(void); +int amd_cppc_cmdline_parse(const char *s, const char *e); +int amd_cppc_register_driver(void); + #endif /* __XEN_CPUFREQ_PM_H__ */ diff --git a/xen/include/acpi/cpufreq/processor_perf.h b/xen/include/acpi/cpufreq/processor_perf.h index f80495fc96..6d8d29d440 100644 --- a/xen/include/acpi/cpufreq/processor_perf.h +++ b/xen/include/acpi/cpufreq/processor_perf.h @@ -5,6 +5,16 @@ #include <public/sysctl.h> #include <xen/acpi.h> +/* + * Internal xen-pm options + * They are extension to public xen-pm options (XEN_PROCESSOR_PM_xxx) defined + * in public/platform.h, guarded by SIF_PM_MASK + */ +#define XEN_PROCESSOR_PM_CPPC 0x100 +#if XEN_PROCESSOR_PM_CPPC & MASK_EXTR(~0, SIF_PM_MASK) +# error "XEN_PROCESSOR_PM_CPPC shall not occupy bits reserved for public xen-pm options" +#endif + #define XEN_CPPC_INIT 0x40000000U #define XEN_PX_INIT 0x80000000U -- 2.34.1
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |