xen/cpufreq: introduce new sub-hypercall to propagate CPPC data

In order to provide backward compatibility with existing governors
that represent performance as frequency, like ondemand, the _CPC
table can optionally provide processor frequency range values, Lowest
frequency and Nominal frequency, to let OS use Lowest Frequency/
Performance and Nominal Frequency/Performance as anchor points to
create linear mapping of CPPC performance to CPU frequency.

As Xen is uncapable of parsing the ACPI dynamic table, we'd like to
introduce a new sub-hypercall "XEN_PM_CPPC" to propagate required CPPC
data from dom0 kernel to Xen.
In the according handler set_cppc_pminfo(), we do _CPC and _PSD
sanitization check, as both _PSD and _CPC info are necessary for correctly
initializing cpufreq cores in CPPC mode.
Users shall be warned that if we failed at this point,
no fallback scheme, like legacy P-state could be switched to.

A new flag "XEN_CPPC_INIT" is also introduced for cpufreq core initialised in
CPPC mode. Then all .init flag checking shall be updated to
consider "XEN_CPPC_INIT" too.

We want to bypass construction of px statistic info in cpufreq_statistic_init()
for CPPC mode, while not bypassing cpufreq_statistic_lock initialization for a
good reason. The same check is unnecessary for cpufreq_statistic_exit(),
since it has already been covered by px statistic variable
"cpufreq_statistic_data" check

Signed-off-by: Penny Zheng <Penny.Zheng@amd.com>
Acked-by: Jan Beulich <jbeulich@suse.com>
This commit is contained in:
Penny Zheng authored and Jan Beulich committed 2025-08-28 14:45:33 +02:00
1 parent 511eb31bd0
commit bceab282bf
10 files changed
+193 -5

No files matched your search

+5
View File
@@ -577,6 +577,11 @@ ret_t do_platform_op(
break;
}
case XEN_PM_CPPC:
ret = set_cppc_pminfo(op->u.set_pminfo.id,
&op->u.set_pminfo.u.cppc_data);
break;
default:
ret = -EINVAL;
break;
+19
View File
@@ -54,3 +54,22 @@ int compat_set_px_pminfo(uint32_t acpi_id,
return set_px_pminfo(acpi_id, xen_perf);
}
int compat_set_cppc_pminfo(unsigned int acpi_id,
const struct compat_processor_cppc *cppc_data)
{
struct xen_processor_cppc *xen_cppc;
unsigned long xlat_page_current;
xlat_malloc_init(xlat_page_current);
xen_cppc = xlat_malloc_array(xlat_page_current,
struct xen_processor_cppc, 1);
if ( unlikely(xen_cppc == NULL) )
return -ENOMEM;
XLAT_processor_cppc(xen_cppc, cppc_data);
return set_cppc_pminfo(acpi_id, xen_cppc);
}
+3
View File
@@ -14,6 +14,9 @@ EMIT_FILE;
#define efi_get_info efi_compat_get_info
#define efi_runtime_call(x) efi_compat_runtime_call(x)
#define xen_processor_cppc compat_processor_cppc
#define set_cppc_pminfo compat_set_cppc_pminfo
#define xen_processor_performance compat_processor_performance
#define set_px_pminfo compat_set_px_pminfo
+5 -2
View File
@@ -91,7 +91,10 @@ static int get_cpufreq_para(struct xen_sysctl_pm_op *op)
pmpt = processor_pminfo[op->cpuid];
policy = per_cpu(cpufreq_cpu_policy, op->cpuid);
if ( !pmpt || !pmpt->perf.states ||
if ( !pmpt ||
((pmpt->init & XEN_PX_INIT) && !pmpt->perf.states) ||
/* Avoid NULL deref of pmpt->perf.states[] */
((pmpt->init & XEN_CPPC_INIT) && pmpt->perf.state_count) ||
!policy || !policy->governor )
return -EINVAL;
@@ -351,7 +354,7 @@ int do_pm_op(struct xen_sysctl_pm_op *op)
case CPUFREQ_PARA:
if ( !(xen_processor_pmbits & XEN_PROCESSOR_PM_PX) )
return -ENODEV;
if ( !pmpt || !(pmpt->init & XEN_PX_INIT) )
if ( !pmpt || !(pmpt->init & (XEN_PX_INIT | XEN_CPPC_INIT)) )
return -EINVAL;
break;
}
+4
View File
@@ -108,6 +108,10 @@ int cpufreq_statistic_init(unsigned int cpu)
if ( !pmpt )
return -EINVAL;
/* Only need to initialize in Px mode */
if ( !(pmpt->init & XEN_PX_INIT) )
return 0;
spin_lock(cpufreq_statistic_lock);
pxpt = per_cpu(cpufreq_statistic_data, cpu);
+122 -2
View File
@@ -40,6 +40,7 @@
#include <xen/domain.h>
#include <xen/cpu.h>
#include <xen/pmstat.h>
#include <xen/xvmalloc.h>
#include <asm/io.h>
#include <asm/processor.h>
@@ -234,6 +235,11 @@ static int get_psd_info(unsigned int cpu, unsigned int *shared_type,
*domain_info = &processor_pminfo[cpu]->perf.domain_info;
break;
case XEN_CPPC_INIT:
*shared_type = processor_pminfo[cpu]->cppc_data.shared_type;
*domain_info = &processor_pminfo[cpu]->cppc_data.domain_info;
break;
default:
ret = -EINVAL;
break;
@@ -259,7 +265,7 @@ int cpufreq_add_cpu(unsigned int cpu)
if ( !processor_pminfo[cpu] || !cpu_online(cpu) )
return -EINVAL;
if ( !(processor_pminfo[cpu]->init & XEN_PX_INIT) )
if ( !(processor_pminfo[cpu]->init & (XEN_PX_INIT | XEN_CPPC_INIT)) )
return -EINVAL;
if (!cpufreq_driver.init)
@@ -434,7 +440,7 @@ int cpufreq_del_cpu(unsigned int cpu)
if ( !processor_pminfo[cpu] || !cpu_online(cpu) )
return -EINVAL;
if ( !(processor_pminfo[cpu]->init & XEN_PX_INIT) )
if ( !(processor_pminfo[cpu]->init & (XEN_PX_INIT | XEN_CPPC_INIT)) )
return -EINVAL;
if (!per_cpu(cpufreq_cpu_policy, cpu))
@@ -693,6 +699,120 @@ int acpi_set_pdc_bits(unsigned int acpi_id, XEN_GUEST_HANDLE(uint32) pdc)
return ret;
}
static void print_CPPC(const struct xen_processor_cppc *cppc_data)
{
printk("\t_CPC: highest_perf=%u, lowest_perf=%u, "
"nominal_perf=%u, lowest_nonlinear_perf=%u, "
"nominal_mhz=%uMHz, lowest_mhz=%uMHz\n",
cppc_data->cpc.highest_perf, cppc_data->cpc.lowest_perf,
cppc_data->cpc.nominal_perf, cppc_data->cpc.lowest_nonlinear_perf,
cppc_data->cpc.nominal_mhz, cppc_data->cpc.lowest_mhz);
}
int set_cppc_pminfo(unsigned int acpi_id,
const struct xen_processor_cppc *cppc_data)
{
int ret = 0, cpu_id;
struct processor_pminfo *pm_info;
cpu_id = get_cpu_id(acpi_id);
if ( cpu_id < 0 )
{
ret = -EINVAL;
goto out;
}
if ( cppc_data->pad[0] || cppc_data->pad[1] || cppc_data->pad[2] )
{
ret = -EINVAL;
goto out;
}
if ( cpufreq_verbose )
printk("Set CPU%d (ACPI ID %u) CPPC state info:\n",
cpu_id, acpi_id);
pm_info = processor_pminfo[cpu_id];
if ( !pm_info )
{
pm_info = xvzalloc(struct processor_pminfo);
if ( !pm_info )
{
ret = -ENOMEM;
goto out;
}
processor_pminfo[cpu_id] = pm_info;
}
pm_info->acpi_id = acpi_id;
pm_info->id = cpu_id;
pm_info->cppc_data = *cppc_data;
if ( (cppc_data->flags & XEN_CPPC_PSD) &&
!check_psd_pminfo(cppc_data->shared_type) )
{
ret = -EINVAL;
goto out;
}
if ( cppc_data->flags & XEN_CPPC_CPC )
{
if ( cppc_data->cpc.highest_perf == 0 ||
cppc_data->cpc.highest_perf > UINT8_MAX ||
cppc_data->cpc.nominal_perf == 0 ||
cppc_data->cpc.lowest_nonlinear_perf == 0 ||
cppc_data->cpc.lowest_perf == 0 ||
cppc_data->cpc.lowest_perf >
cppc_data->cpc.lowest_nonlinear_perf ||
cppc_data->cpc.lowest_nonlinear_perf >
cppc_data->cpc.nominal_perf ||
cppc_data->cpc.nominal_perf > cppc_data->cpc.highest_perf )
/*
* Right now, Xen doesn't actually use highest_perf/nominal_perf/
* lowest_nonlinear_perf/lowest_perf values read from ACPI _CPC
* table. Xen reads CPPC capability MSR to get these four values.
* So warning is enough.
*/
printk_once(XENLOG_WARNING
"Broken CPPC perf values: lowest(%u), nonlinear_lowest(%u), nominal(%u), highest(%u)\n",
cppc_data->cpc.lowest_perf,
cppc_data->cpc.lowest_nonlinear_perf,
cppc_data->cpc.nominal_perf,
cppc_data->cpc.highest_perf);
/* lowest_mhz and nominal_mhz are optional value */
if ( cppc_data->cpc.nominal_mhz &&
cppc_data->cpc.lowest_mhz > cppc_data->cpc.nominal_mhz )
{
printk_once(XENLOG_WARNING
"Broken CPPC freq values: lowest(%u), nominal(%u)\n",
cppc_data->cpc.lowest_mhz,
cppc_data->cpc.nominal_mhz);
/* Re-set with zero values, instead of keeping invalid values */
pm_info->cppc_data.cpc.nominal_mhz = 0;
pm_info->cppc_data.cpc.lowest_mhz = 0;
}
}
if ( cppc_data->flags == (XEN_CPPC_PSD | XEN_CPPC_CPC) )
{
if ( cpufreq_verbose )
{
print_PSD(&pm_info->cppc_data.domain_info);
print_CPPC(&pm_info->cppc_data);
}
pm_info->init = XEN_CPPC_INIT;
ret = cpufreq_cpu_init(cpu_id);
if ( ret )
printk_once(XENLOG_WARNING
"CPU%d failed amd-cppc mode init; use \"cpufreq=xen\" instead",
cpu_id);
}
out:
return ret;
}
static void cpufreq_cmdline_common_para(struct cpufreq_policy *new_policy)
{
if (usr_max_freq)
+3 -1
View File
@@ -5,7 +5,8 @@
#include <public/sysctl.h>
#include <xen/acpi.h>
#define XEN_PX_INIT 0x80000000U
#define XEN_CPPC_INIT 0x40000000U
#define XEN_PX_INIT 0x80000000U
unsigned int powernow_register_driver(void);
unsigned int get_measured_perf(unsigned int cpu, unsigned int flag);
@@ -43,6 +44,7 @@ struct processor_pminfo {
uint32_t acpi_id;
uint32_t id;
struct processor_performance perf;
struct xen_processor_cppc cppc_data;
uint32_t init;
};
+26
View File
@@ -363,6 +363,7 @@ DEFINE_XEN_GUEST_HANDLE(xenpf_getidletime_t);
#define XEN_PM_PX 1
#define XEN_PM_TX 2
#define XEN_PM_PDC 3
#define XEN_PM_CPPC 4
/* Px sub info type */
#define XEN_PX_PCT 1
@@ -370,6 +371,10 @@ DEFINE_XEN_GUEST_HANDLE(xenpf_getidletime_t);
#define XEN_PX_PPC 4
#define XEN_PX_PSD 8
/* CPPC sub info type */
#define XEN_CPPC_PSD (1U << 0)
#define XEN_CPPC_CPC (1U << 1)
struct xen_power_register {
uint32_t space_id;
uint32_t bit_width;
@@ -457,6 +462,26 @@ struct xen_processor_performance {
typedef struct xen_processor_performance xen_processor_performance_t;
DEFINE_XEN_GUEST_HANDLE(xen_processor_performance_t);
struct xen_processor_cppc {
uint8_t flags; /* IN: XEN_CPPC_xxx */
uint8_t pad[3];
/*
* IN: Subset _CPC fields useful for CPPC-compatible cpufreq
* driver's initialization
*/
struct {
uint32_t highest_perf;
uint32_t nominal_perf;
uint32_t lowest_nonlinear_perf;
uint32_t lowest_perf;
uint32_t lowest_mhz;
uint32_t nominal_mhz;
} cpc;
uint32_t shared_type; /* IN: XEN_CPUPERF_SHARED_TYPE_xxx */
struct xen_psd_package domain_info; /* IN: _PSD */
};
typedef struct xen_processor_cppc xen_processor_cppc_t;
struct xenpf_set_processor_pminfo {
/* IN variables */
uint32_t id; /* ACPI CPU ID */
@@ -465,6 +490,7 @@ struct xenpf_set_processor_pminfo {
struct xen_processor_power power;/* Cx: _CST/_CSD */
struct xen_processor_performance perf; /* Px: _PPC/_PCT/_PSS/_PSD */
XEN_GUEST_HANDLE(uint32) pdc; /* _PDC */
xen_processor_cppc_t cppc_data; /* CPPC: _CPC and _PSD */
} u;
};
typedef struct xenpf_set_processor_pminfo xenpf_set_processor_pminfo_t;
+5
View File
@@ -7,12 +7,17 @@
int set_px_pminfo(uint32_t acpi_id, struct xen_processor_performance *perf);
long set_cx_pminfo(uint32_t acpi_id, struct xen_processor_power *power);
int set_cppc_pminfo(unsigned int acpi_id,
const struct xen_processor_cppc *cppc_data);
#ifdef CONFIG_COMPAT
struct compat_processor_performance;
int compat_set_px_pminfo(uint32_t acpi_id, struct compat_processor_performance *perf);
struct compat_processor_power;
long compat_set_cx_pminfo(uint32_t acpi_id, struct compat_processor_power *power);
struct compat_processor_cppc;
int compat_set_cppc_pminfo(unsigned int acpi_id,
const struct compat_processor_cppc *cppc_data);
#endif
uint32_t pmstat_get_cx_nr(unsigned int cpu);
+1
View File
@@ -160,6 +160,7 @@
! pct_register platform.h
! power_register platform.h
! processor_cppc platform.h
? processor_csd platform.h
! processor_cx platform.h
! processor_flags platform.h
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.