mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 02:01:24 +01:00
xen/pmstat: introduce CONFIG_PM_OP
We move the following functions into a new file drivers/acpi/pm-op.c, as they are all more fitting in performance controling and only called by do_pm_op(): - get_cpufreq_para() - set_cpufreq_para() - set_cpufreq_gov() - set_cpufreq_cppc() - cpufreq_driver_getavg() - cpufreq_update_turbo() - cpufreq_get_turbo_status() We introduce a new Kconfig CONFIG_PM_OP to wrap the new file. Also, although the following helpers are only called by do_pm_op(), they have dependency on local variable, we wrap them with CONFIG_PM_OP in place: - write_userspace_scaling_setspeed() - write_ondemand_sampling_rate() - write_ondemand_up_threshold() - get_cpufreq_ondemand_para() - cpufreq_driver.update() - get_hwp_para() Various style corrections shall be applied at the same time while moving these functions, including: - add extra space before and after bracket of if() and switch() - fix indentation - drop all the unnecessary inner figure braces We shall also provide "# CONFIG_PM_OP is not set" in preset configs for PV shim on x86. Signed-off-by: Penny Zheng <Penny.Zheng@amd.com> Reviewed-by: Stefano Stabellini <sstabellini@kernel.org> Acked-by: Jan Beulich <jbeulich@suse.com>
This commit is contained in:
1 parent
f90610e6a7
commit
a16f2b3e06
12 files changed
+421
-399
No files matched your search
@@ -466,6 +466,7 @@ static int cf_check hwp_cpufreq_cpu_exit(struct cpufreq_policy *policy)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_OP
|
||||
/*
|
||||
* The SDM reads like turbo should be disabled with MSR_IA32_PERF_CTL and
|
||||
* PERF_CTL_TURBO_DISENGAGE, but that does not seem to actually work, at least
|
||||
@@ -508,6 +509,7 @@ static int cf_check hwp_cpufreq_update(unsigned int cpu, struct cpufreq_policy *
|
||||
|
||||
return per_cpu(hwp_drv_data, cpu)->ret;
|
||||
}
|
||||
#endif /* CONFIG_PM_OP */
|
||||
|
||||
static const struct cpufreq_driver __initconst_cf_clobber
|
||||
hwp_cpufreq_driver = {
|
||||
@@ -516,9 +518,12 @@ hwp_cpufreq_driver = {
|
||||
.target = hwp_cpufreq_target,
|
||||
.init = hwp_cpufreq_cpu_init,
|
||||
.exit = hwp_cpufreq_cpu_exit,
|
||||
#ifdef CONFIG_PM_OP
|
||||
.update = hwp_cpufreq_update,
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM_OP
|
||||
int get_hwp_para(unsigned int cpu,
|
||||
struct xen_cppc_para *cppc_para)
|
||||
{
|
||||
@@ -639,6 +644,7 @@ int set_hwp_para(struct cpufreq_policy *policy,
|
||||
|
||||
return hwp_cpufreq_target(policy, 0, 0);
|
||||
}
|
||||
#endif /* CONFIG_PM_OP */
|
||||
|
||||
int __init hwp_register_driver(void)
|
||||
{
|
||||
|
||||
@@ -49,6 +49,7 @@ static void cf_check transition_pstate(void *pstate)
|
||||
wrmsrl(MSR_PSTATE_CTRL, *(unsigned int *)pstate);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_OP
|
||||
static void cf_check update_cpb(void *data)
|
||||
{
|
||||
struct cpufreq_policy *policy = data;
|
||||
@@ -77,6 +78,7 @@ static int cf_check powernow_cpufreq_update(
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_PM_OP */
|
||||
|
||||
static int cf_check powernow_cpufreq_target(
|
||||
struct cpufreq_policy *policy,
|
||||
@@ -324,7 +326,9 @@ powernow_cpufreq_driver = {
|
||||
.target = powernow_cpufreq_target,
|
||||
.init = powernow_cpufreq_cpu_init,
|
||||
.exit = powernow_cpufreq_cpu_exit,
|
||||
#ifdef CONFIG_PM_OP
|
||||
.update = powernow_cpufreq_update
|
||||
#endif
|
||||
};
|
||||
|
||||
unsigned int __init powernow_register_driver(void)
|
||||
|
||||
@@ -26,3 +26,4 @@ CONFIG_EXPERT=y
|
||||
# CONFIG_INTEL_IOMMU is not set
|
||||
# CONFIG_DEBUG is not set
|
||||
# CONFIG_GDBSX is not set
|
||||
# CONFIG_PM_OP is not set
|
||||
@@ -620,4 +620,12 @@ config SYSCTL
|
||||
|
||||
endmenu
|
||||
|
||||
config PM_OP
|
||||
bool "Enable Performance Management Operation"
|
||||
depends on ACPI && HAS_CPUFREQ && SYSCTL
|
||||
default y
|
||||
help
|
||||
This option shall enable userspace performance management control
|
||||
to do power/performance analyzing and tuning.
|
||||
|
||||
endmenu
|
||||
@@ -174,7 +174,9 @@ long do_sysctl(XEN_GUEST_HANDLE_PARAM(xen_sysctl_t) u_sysctl)
|
||||
case XEN_SYSCTL_get_pmstat:
|
||||
ret = do_get_pm_info(&op->u.get_pmstat);
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM_OP
|
||||
case XEN_SYSCTL_pm_op:
|
||||
ret = do_pm_op(&op->u.pm_op);
|
||||
if ( ret == -EAGAIN )
|
||||
|
||||
@@ -6,6 +6,7 @@ obj-bin-y += tables.init.o
|
||||
obj-$(CONFIG_ACPI_NUMA) += numa.o
|
||||
obj-y += osl.o
|
||||
obj-$(CONFIG_HAS_CPUFREQ) += pmstat.o
|
||||
obj-$(CONFIG_PM_OP) += pm-op.o
|
||||
|
||||
obj-$(CONFIG_X86) += hwregs.o
|
||||
obj-$(CONFIG_X86) += reboot.o
|
||||
@@ -0,0 +1,395 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include <xen/acpi.h>
|
||||
#include <xen/domain.h>
|
||||
#include <xen/errno.h>
|
||||
#include <xen/guest_access.h>
|
||||
#include <xen/lib.h>
|
||||
#include <xen/sched.h>
|
||||
|
||||
#include <acpi/cpufreq/cpufreq.h>
|
||||
#include <public/platform.h>
|
||||
#include <public/sysctl.h>
|
||||
|
||||
/*
|
||||
* 1. Get PM parameter
|
||||
* 2. Provide user PM control
|
||||
*/
|
||||
static int cpufreq_update_turbo(unsigned int cpu, int new_state)
|
||||
{
|
||||
struct cpufreq_policy *policy;
|
||||
int curr_state;
|
||||
int ret = 0;
|
||||
|
||||
if ( new_state != CPUFREQ_TURBO_ENABLED &&
|
||||
new_state != CPUFREQ_TURBO_DISABLED )
|
||||
return -EINVAL;
|
||||
|
||||
policy = per_cpu(cpufreq_cpu_policy, cpu);
|
||||
if ( !policy )
|
||||
return -EACCES;
|
||||
|
||||
if ( policy->turbo == CPUFREQ_TURBO_UNSUPPORTED )
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
curr_state = policy->turbo;
|
||||
if ( curr_state == new_state )
|
||||
return 0;
|
||||
|
||||
policy->turbo = new_state;
|
||||
if ( cpufreq_driver.update )
|
||||
{
|
||||
ret = alternative_call(cpufreq_driver.update, cpu, policy);
|
||||
if ( ret )
|
||||
policy->turbo = curr_state;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cpufreq_get_turbo_status(unsigned int cpu)
|
||||
{
|
||||
struct cpufreq_policy *policy;
|
||||
|
||||
policy = per_cpu(cpufreq_cpu_policy, cpu);
|
||||
return policy && policy->turbo == CPUFREQ_TURBO_ENABLED;
|
||||
}
|
||||
|
||||
static int read_scaling_available_governors(char *scaling_available_governors,
|
||||
unsigned int size)
|
||||
{
|
||||
unsigned int i = 0;
|
||||
struct cpufreq_governor *t;
|
||||
|
||||
if ( !scaling_available_governors )
|
||||
return -EINVAL;
|
||||
|
||||
list_for_each_entry(t, &cpufreq_governor_list, governor_list)
|
||||
{
|
||||
i += scnprintf(&scaling_available_governors[i],
|
||||
CPUFREQ_NAME_LEN, "%s ", t->name);
|
||||
if ( i > size )
|
||||
return -EINVAL;
|
||||
}
|
||||
scaling_available_governors[i-1] = '\0';
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_cpufreq_para(struct xen_sysctl_pm_op *op)
|
||||
{
|
||||
uint32_t ret = 0;
|
||||
const struct processor_pminfo *pmpt;
|
||||
struct cpufreq_policy *policy;
|
||||
uint32_t gov_num = 0;
|
||||
uint32_t *data;
|
||||
char *scaling_available_governors;
|
||||
struct list_head *pos;
|
||||
unsigned int cpu, i = 0;
|
||||
|
||||
pmpt = processor_pminfo[op->cpuid];
|
||||
policy = per_cpu(cpufreq_cpu_policy, op->cpuid);
|
||||
|
||||
if ( !pmpt || !pmpt->perf.states ||
|
||||
!policy || !policy->governor )
|
||||
return -EINVAL;
|
||||
|
||||
list_for_each(pos, &cpufreq_governor_list)
|
||||
gov_num++;
|
||||
|
||||
if ( (op->u.get_para.cpu_num != cpumask_weight(policy->cpus)) ||
|
||||
(op->u.get_para.freq_num != pmpt->perf.state_count) ||
|
||||
(op->u.get_para.gov_num != gov_num) )
|
||||
{
|
||||
op->u.get_para.cpu_num = cpumask_weight(policy->cpus);
|
||||
op->u.get_para.freq_num = pmpt->perf.state_count;
|
||||
op->u.get_para.gov_num = gov_num;
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
if ( !(data = xzalloc_array(uint32_t,
|
||||
max(op->u.get_para.cpu_num,
|
||||
op->u.get_para.freq_num))) )
|
||||
return -ENOMEM;
|
||||
|
||||
for_each_cpu(cpu, policy->cpus)
|
||||
data[i++] = cpu;
|
||||
ret = copy_to_guest(op->u.get_para.affected_cpus,
|
||||
data, op->u.get_para.cpu_num);
|
||||
|
||||
for ( i = 0; i < op->u.get_para.freq_num; i++ )
|
||||
data[i] = pmpt->perf.states[i].core_frequency * 1000;
|
||||
ret += copy_to_guest(op->u.get_para.scaling_available_frequencies,
|
||||
data, op->u.get_para.freq_num);
|
||||
|
||||
xfree(data);
|
||||
if ( ret )
|
||||
return -EFAULT;
|
||||
|
||||
op->u.get_para.cpuinfo_cur_freq =
|
||||
cpufreq_driver.get ? alternative_call(cpufreq_driver.get, op->cpuid)
|
||||
: policy->cur;
|
||||
op->u.get_para.cpuinfo_max_freq = policy->cpuinfo.max_freq;
|
||||
op->u.get_para.cpuinfo_min_freq = policy->cpuinfo.min_freq;
|
||||
op->u.get_para.turbo_enabled = cpufreq_get_turbo_status(op->cpuid);
|
||||
|
||||
if ( cpufreq_driver.name[0] )
|
||||
strlcpy(op->u.get_para.scaling_driver,
|
||||
cpufreq_driver.name, CPUFREQ_NAME_LEN);
|
||||
else
|
||||
strlcpy(op->u.get_para.scaling_driver, "Unknown", CPUFREQ_NAME_LEN);
|
||||
|
||||
if ( hwp_active() )
|
||||
ret = get_hwp_para(policy->cpu, &op->u.get_para.u.cppc_para);
|
||||
else
|
||||
{
|
||||
if ( !(scaling_available_governors =
|
||||
xzalloc_array(char, gov_num * CPUFREQ_NAME_LEN)) )
|
||||
return -ENOMEM;
|
||||
if ( (ret = read_scaling_available_governors(
|
||||
scaling_available_governors,
|
||||
(gov_num * CPUFREQ_NAME_LEN *
|
||||
sizeof(*scaling_available_governors)))) )
|
||||
{
|
||||
xfree(scaling_available_governors);
|
||||
return ret;
|
||||
}
|
||||
ret = copy_to_guest(op->u.get_para.scaling_available_governors,
|
||||
scaling_available_governors,
|
||||
gov_num * CPUFREQ_NAME_LEN);
|
||||
xfree(scaling_available_governors);
|
||||
if ( ret )
|
||||
return -EFAULT;
|
||||
|
||||
op->u.get_para.u.s.scaling_cur_freq = policy->cur;
|
||||
op->u.get_para.u.s.scaling_max_freq = policy->max;
|
||||
op->u.get_para.u.s.scaling_min_freq = policy->min;
|
||||
|
||||
if ( policy->governor->name[0] )
|
||||
strlcpy(op->u.get_para.u.s.scaling_governor,
|
||||
policy->governor->name, CPUFREQ_NAME_LEN);
|
||||
else
|
||||
strlcpy(op->u.get_para.u.s.scaling_governor, "Unknown",
|
||||
CPUFREQ_NAME_LEN);
|
||||
|
||||
/* governor specific para */
|
||||
if ( !strncasecmp(op->u.get_para.u.s.scaling_governor,
|
||||
"userspace", CPUFREQ_NAME_LEN) )
|
||||
op->u.get_para.u.s.u.userspace.scaling_setspeed = policy->cur;
|
||||
|
||||
if ( !strncasecmp(op->u.get_para.u.s.scaling_governor,
|
||||
"ondemand", CPUFREQ_NAME_LEN) )
|
||||
ret = get_cpufreq_ondemand_para(
|
||||
&op->u.get_para.u.s.u.ondemand.sampling_rate_max,
|
||||
&op->u.get_para.u.s.u.ondemand.sampling_rate_min,
|
||||
&op->u.get_para.u.s.u.ondemand.sampling_rate,
|
||||
&op->u.get_para.u.s.u.ondemand.up_threshold);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_cpufreq_gov(struct xen_sysctl_pm_op *op)
|
||||
{
|
||||
struct cpufreq_policy new_policy, *old_policy;
|
||||
|
||||
old_policy = per_cpu(cpufreq_cpu_policy, op->cpuid);
|
||||
if ( !old_policy )
|
||||
return -EINVAL;
|
||||
|
||||
memcpy(&new_policy, old_policy, sizeof(struct cpufreq_policy));
|
||||
|
||||
new_policy.governor = __find_governor(op->u.set_gov.scaling_governor);
|
||||
if ( new_policy.governor == NULL )
|
||||
return -EINVAL;
|
||||
|
||||
return __cpufreq_set_policy(old_policy, &new_policy);
|
||||
}
|
||||
|
||||
static int set_cpufreq_para(struct xen_sysctl_pm_op *op)
|
||||
{
|
||||
int ret = 0;
|
||||
struct cpufreq_policy *policy;
|
||||
|
||||
policy = per_cpu(cpufreq_cpu_policy, op->cpuid);
|
||||
|
||||
if ( !policy || !policy->governor )
|
||||
return -EINVAL;
|
||||
|
||||
if ( hwp_active() )
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
switch( op->u.set_para.ctrl_type )
|
||||
{
|
||||
case SCALING_MAX_FREQ:
|
||||
{
|
||||
struct cpufreq_policy new_policy;
|
||||
|
||||
memcpy(&new_policy, policy, sizeof(struct cpufreq_policy));
|
||||
new_policy.max = op->u.set_para.ctrl_value;
|
||||
ret = __cpufreq_set_policy(policy, &new_policy);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SCALING_MIN_FREQ:
|
||||
{
|
||||
struct cpufreq_policy new_policy;
|
||||
|
||||
memcpy(&new_policy, policy, sizeof(struct cpufreq_policy));
|
||||
new_policy.min = op->u.set_para.ctrl_value;
|
||||
ret = __cpufreq_set_policy(policy, &new_policy);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SCALING_SETSPEED:
|
||||
{
|
||||
unsigned int freq =op->u.set_para.ctrl_value;
|
||||
|
||||
if ( !strncasecmp(policy->governor->name,
|
||||
"userspace", CPUFREQ_NAME_LEN) )
|
||||
ret = write_userspace_scaling_setspeed(op->cpuid, freq);
|
||||
else
|
||||
ret = -EINVAL;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SAMPLING_RATE:
|
||||
{
|
||||
unsigned int sampling_rate = op->u.set_para.ctrl_value;
|
||||
|
||||
if ( !strncasecmp(policy->governor->name,
|
||||
"ondemand", CPUFREQ_NAME_LEN) )
|
||||
ret = write_ondemand_sampling_rate(sampling_rate);
|
||||
else
|
||||
ret = -EINVAL;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case UP_THRESHOLD:
|
||||
{
|
||||
unsigned int up_threshold = op->u.set_para.ctrl_value;
|
||||
|
||||
if ( !strncasecmp(policy->governor->name,
|
||||
"ondemand", CPUFREQ_NAME_LEN) )
|
||||
ret = write_ondemand_up_threshold(up_threshold);
|
||||
else
|
||||
ret = -EINVAL;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_cpufreq_cppc(struct xen_sysctl_pm_op *op)
|
||||
{
|
||||
struct cpufreq_policy *policy = per_cpu(cpufreq_cpu_policy, op->cpuid);
|
||||
|
||||
if ( !policy || !policy->governor )
|
||||
return -ENOENT;
|
||||
|
||||
if ( !hwp_active() )
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return set_hwp_para(policy, &op->u.set_cppc);
|
||||
}
|
||||
|
||||
int do_pm_op(struct xen_sysctl_pm_op *op)
|
||||
{
|
||||
int ret = 0;
|
||||
const struct processor_pminfo *pmpt;
|
||||
|
||||
switch ( op->cmd )
|
||||
{
|
||||
case XEN_SYSCTL_pm_op_set_sched_opt_smt:
|
||||
{
|
||||
uint32_t saved_value = sched_smt_power_savings;
|
||||
|
||||
if ( op->cpuid != 0 )
|
||||
return -EINVAL;
|
||||
sched_smt_power_savings = !!op->u.set_sched_opt_smt;
|
||||
op->u.set_sched_opt_smt = saved_value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
case XEN_SYSCTL_pm_op_get_max_cstate:
|
||||
BUILD_BUG_ON(XEN_SYSCTL_CX_UNLIMITED != UINT_MAX);
|
||||
if ( op->cpuid == 0 )
|
||||
op->u.get_max_cstate = acpi_get_cstate_limit();
|
||||
else if ( op->cpuid == 1 )
|
||||
op->u.get_max_cstate = acpi_get_csubstate_limit();
|
||||
else
|
||||
ret = -EINVAL;
|
||||
return ret;
|
||||
|
||||
case XEN_SYSCTL_pm_op_set_max_cstate:
|
||||
if ( op->cpuid == 0 )
|
||||
acpi_set_cstate_limit(op->u.set_max_cstate);
|
||||
else if ( op->cpuid == 1 )
|
||||
acpi_set_csubstate_limit(op->u.set_max_cstate);
|
||||
else
|
||||
ret = -EINVAL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ( op->cpuid >= nr_cpu_ids || !cpu_online(op->cpuid) )
|
||||
return -EINVAL;
|
||||
pmpt = processor_pminfo[op->cpuid];
|
||||
|
||||
switch ( op->cmd & PM_PARA_CATEGORY_MASK )
|
||||
{
|
||||
case CPUFREQ_PARA:
|
||||
if ( !(xen_processor_pmbits & XEN_PROCESSOR_PM_PX) )
|
||||
return -ENODEV;
|
||||
if ( !pmpt || !(pmpt->init & XEN_PX_INIT) )
|
||||
return -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
switch ( op->cmd )
|
||||
{
|
||||
case GET_CPUFREQ_PARA:
|
||||
ret = get_cpufreq_para(op);
|
||||
break;
|
||||
|
||||
case SET_CPUFREQ_GOV:
|
||||
ret = set_cpufreq_gov(op);
|
||||
break;
|
||||
|
||||
case SET_CPUFREQ_PARA:
|
||||
ret = set_cpufreq_para(op);
|
||||
break;
|
||||
|
||||
case SET_CPUFREQ_CPPC:
|
||||
ret = set_cpufreq_cppc(op);
|
||||
break;
|
||||
|
||||
case GET_CPUFREQ_AVGFREQ:
|
||||
op->u.get_avgfreq = cpufreq_driver_getavg(op->cpuid, USR_GETAVG);
|
||||
break;
|
||||
|
||||
case XEN_SYSCTL_pm_op_enable_turbo:
|
||||
ret = cpufreq_update_turbo(op->cpuid, CPUFREQ_TURBO_ENABLED);
|
||||
break;
|
||||
|
||||
case XEN_SYSCTL_pm_op_disable_turbo:
|
||||
ret = cpufreq_update_turbo(op->cpuid, CPUFREQ_TURBO_DISABLED);
|
||||
break;
|
||||
|
||||
default:
|
||||
printk("not defined sub-hypercall @ do_pm_op\n");
|
||||
ret = -ENOSYS;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -337,358 +337,3 @@ int do_get_pm_info(struct xen_sysctl_get_pmstat *op)
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* 1. Get PM parameter
|
||||
* 2. Provide user PM control
|
||||
*/
|
||||
static int read_scaling_available_governors(char *scaling_available_governors,
|
||||
unsigned int size)
|
||||
{
|
||||
unsigned int i = 0;
|
||||
struct cpufreq_governor *t;
|
||||
|
||||
if ( !scaling_available_governors )
|
||||
return -EINVAL;
|
||||
|
||||
list_for_each_entry(t, &cpufreq_governor_list, governor_list)
|
||||
{
|
||||
i += scnprintf(&scaling_available_governors[i],
|
||||
CPUFREQ_NAME_LEN, "%s ", t->name);
|
||||
if ( i > size )
|
||||
return -EINVAL;
|
||||
}
|
||||
scaling_available_governors[i-1] = '\0';
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_cpufreq_para(struct xen_sysctl_pm_op *op)
|
||||
{
|
||||
uint32_t ret = 0;
|
||||
const struct processor_pminfo *pmpt;
|
||||
struct cpufreq_policy *policy;
|
||||
uint32_t gov_num = 0;
|
||||
uint32_t *data;
|
||||
char *scaling_available_governors;
|
||||
struct list_head *pos;
|
||||
unsigned int cpu, i = 0;
|
||||
|
||||
pmpt = processor_pminfo[op->cpuid];
|
||||
policy = per_cpu(cpufreq_cpu_policy, op->cpuid);
|
||||
|
||||
if ( !pmpt || !pmpt->perf.states ||
|
||||
!policy || !policy->governor )
|
||||
return -EINVAL;
|
||||
|
||||
list_for_each(pos, &cpufreq_governor_list)
|
||||
gov_num++;
|
||||
|
||||
if ( (op->u.get_para.cpu_num != cpumask_weight(policy->cpus)) ||
|
||||
(op->u.get_para.freq_num != pmpt->perf.state_count) ||
|
||||
(op->u.get_para.gov_num != gov_num) )
|
||||
{
|
||||
op->u.get_para.cpu_num = cpumask_weight(policy->cpus);
|
||||
op->u.get_para.freq_num = pmpt->perf.state_count;
|
||||
op->u.get_para.gov_num = gov_num;
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
if ( !(data = xzalloc_array(uint32_t,
|
||||
max(op->u.get_para.cpu_num,
|
||||
op->u.get_para.freq_num))) )
|
||||
return -ENOMEM;
|
||||
|
||||
for_each_cpu(cpu, policy->cpus)
|
||||
data[i++] = cpu;
|
||||
ret = copy_to_guest(op->u.get_para.affected_cpus,
|
||||
data, op->u.get_para.cpu_num);
|
||||
|
||||
for ( i = 0; i < op->u.get_para.freq_num; i++ )
|
||||
data[i] = pmpt->perf.states[i].core_frequency * 1000;
|
||||
ret += copy_to_guest(op->u.get_para.scaling_available_frequencies,
|
||||
data, op->u.get_para.freq_num);
|
||||
|
||||
xfree(data);
|
||||
if ( ret )
|
||||
return -EFAULT;
|
||||
|
||||
op->u.get_para.cpuinfo_cur_freq =
|
||||
cpufreq_driver.get ? alternative_call(cpufreq_driver.get, op->cpuid)
|
||||
: policy->cur;
|
||||
op->u.get_para.cpuinfo_max_freq = policy->cpuinfo.max_freq;
|
||||
op->u.get_para.cpuinfo_min_freq = policy->cpuinfo.min_freq;
|
||||
op->u.get_para.turbo_enabled = cpufreq_get_turbo_status(op->cpuid);
|
||||
|
||||
if ( cpufreq_driver.name[0] )
|
||||
strlcpy(op->u.get_para.scaling_driver,
|
||||
cpufreq_driver.name, CPUFREQ_NAME_LEN);
|
||||
else
|
||||
strlcpy(op->u.get_para.scaling_driver, "Unknown", CPUFREQ_NAME_LEN);
|
||||
|
||||
if ( hwp_active() )
|
||||
ret = get_hwp_para(policy->cpu, &op->u.get_para.u.cppc_para);
|
||||
else
|
||||
{
|
||||
if ( !(scaling_available_governors =
|
||||
xzalloc_array(char, gov_num * CPUFREQ_NAME_LEN)) )
|
||||
return -ENOMEM;
|
||||
if ( (ret = read_scaling_available_governors(
|
||||
scaling_available_governors,
|
||||
(gov_num * CPUFREQ_NAME_LEN *
|
||||
sizeof(*scaling_available_governors)))) )
|
||||
{
|
||||
xfree(scaling_available_governors);
|
||||
return ret;
|
||||
}
|
||||
ret = copy_to_guest(op->u.get_para.scaling_available_governors,
|
||||
scaling_available_governors,
|
||||
gov_num * CPUFREQ_NAME_LEN);
|
||||
xfree(scaling_available_governors);
|
||||
if ( ret )
|
||||
return -EFAULT;
|
||||
|
||||
op->u.get_para.u.s.scaling_cur_freq = policy->cur;
|
||||
op->u.get_para.u.s.scaling_max_freq = policy->max;
|
||||
op->u.get_para.u.s.scaling_min_freq = policy->min;
|
||||
|
||||
if ( policy->governor->name[0] )
|
||||
strlcpy(op->u.get_para.u.s.scaling_governor,
|
||||
policy->governor->name, CPUFREQ_NAME_LEN);
|
||||
else
|
||||
strlcpy(op->u.get_para.u.s.scaling_governor, "Unknown",
|
||||
CPUFREQ_NAME_LEN);
|
||||
|
||||
/* governor specific para */
|
||||
if ( !strncasecmp(op->u.get_para.u.s.scaling_governor,
|
||||
"userspace", CPUFREQ_NAME_LEN) )
|
||||
op->u.get_para.u.s.u.userspace.scaling_setspeed = policy->cur;
|
||||
|
||||
if ( !strncasecmp(op->u.get_para.u.s.scaling_governor,
|
||||
"ondemand", CPUFREQ_NAME_LEN) )
|
||||
ret = get_cpufreq_ondemand_para(
|
||||
&op->u.get_para.u.s.u.ondemand.sampling_rate_max,
|
||||
&op->u.get_para.u.s.u.ondemand.sampling_rate_min,
|
||||
&op->u.get_para.u.s.u.ondemand.sampling_rate,
|
||||
&op->u.get_para.u.s.u.ondemand.up_threshold);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_cpufreq_gov(struct xen_sysctl_pm_op *op)
|
||||
{
|
||||
struct cpufreq_policy new_policy, *old_policy;
|
||||
|
||||
old_policy = per_cpu(cpufreq_cpu_policy, op->cpuid);
|
||||
if ( !old_policy )
|
||||
return -EINVAL;
|
||||
|
||||
memcpy(&new_policy, old_policy, sizeof(struct cpufreq_policy));
|
||||
|
||||
new_policy.governor = __find_governor(op->u.set_gov.scaling_governor);
|
||||
if (new_policy.governor == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
return __cpufreq_set_policy(old_policy, &new_policy);
|
||||
}
|
||||
|
||||
static int set_cpufreq_para(struct xen_sysctl_pm_op *op)
|
||||
{
|
||||
int ret = 0;
|
||||
struct cpufreq_policy *policy;
|
||||
|
||||
policy = per_cpu(cpufreq_cpu_policy, op->cpuid);
|
||||
|
||||
if ( !policy || !policy->governor )
|
||||
return -EINVAL;
|
||||
|
||||
if ( hwp_active() )
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
switch(op->u.set_para.ctrl_type)
|
||||
{
|
||||
case SCALING_MAX_FREQ:
|
||||
{
|
||||
struct cpufreq_policy new_policy;
|
||||
|
||||
memcpy(&new_policy, policy, sizeof(struct cpufreq_policy));
|
||||
new_policy.max = op->u.set_para.ctrl_value;
|
||||
ret = __cpufreq_set_policy(policy, &new_policy);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SCALING_MIN_FREQ:
|
||||
{
|
||||
struct cpufreq_policy new_policy;
|
||||
|
||||
memcpy(&new_policy, policy, sizeof(struct cpufreq_policy));
|
||||
new_policy.min = op->u.set_para.ctrl_value;
|
||||
ret = __cpufreq_set_policy(policy, &new_policy);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SCALING_SETSPEED:
|
||||
{
|
||||
unsigned int freq =op->u.set_para.ctrl_value;
|
||||
|
||||
if ( !strncasecmp(policy->governor->name,
|
||||
"userspace", CPUFREQ_NAME_LEN) )
|
||||
ret = write_userspace_scaling_setspeed(op->cpuid, freq);
|
||||
else
|
||||
ret = -EINVAL;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SAMPLING_RATE:
|
||||
{
|
||||
unsigned int sampling_rate = op->u.set_para.ctrl_value;
|
||||
|
||||
if ( !strncasecmp(policy->governor->name,
|
||||
"ondemand", CPUFREQ_NAME_LEN) )
|
||||
ret = write_ondemand_sampling_rate(sampling_rate);
|
||||
else
|
||||
ret = -EINVAL;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case UP_THRESHOLD:
|
||||
{
|
||||
unsigned int up_threshold = op->u.set_para.ctrl_value;
|
||||
|
||||
if ( !strncasecmp(policy->governor->name,
|
||||
"ondemand", CPUFREQ_NAME_LEN) )
|
||||
ret = write_ondemand_up_threshold(up_threshold);
|
||||
else
|
||||
ret = -EINVAL;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_cpufreq_cppc(struct xen_sysctl_pm_op *op)
|
||||
{
|
||||
struct cpufreq_policy *policy = per_cpu(cpufreq_cpu_policy, op->cpuid);
|
||||
|
||||
if ( !policy || !policy->governor )
|
||||
return -ENOENT;
|
||||
|
||||
if ( !hwp_active() )
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return set_hwp_para(policy, &op->u.set_cppc);
|
||||
}
|
||||
|
||||
int do_pm_op(struct xen_sysctl_pm_op *op)
|
||||
{
|
||||
int ret = 0;
|
||||
const struct processor_pminfo *pmpt;
|
||||
|
||||
switch ( op->cmd )
|
||||
{
|
||||
case XEN_SYSCTL_pm_op_set_sched_opt_smt:
|
||||
{
|
||||
uint32_t saved_value = sched_smt_power_savings;
|
||||
|
||||
if ( op->cpuid != 0 )
|
||||
return -EINVAL;
|
||||
sched_smt_power_savings = !!op->u.set_sched_opt_smt;
|
||||
op->u.set_sched_opt_smt = saved_value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
case XEN_SYSCTL_pm_op_get_max_cstate:
|
||||
BUILD_BUG_ON(XEN_SYSCTL_CX_UNLIMITED != UINT_MAX);
|
||||
if ( op->cpuid == 0 )
|
||||
op->u.get_max_cstate = acpi_get_cstate_limit();
|
||||
else if ( op->cpuid == 1 )
|
||||
op->u.get_max_cstate = acpi_get_csubstate_limit();
|
||||
else
|
||||
ret = -EINVAL;
|
||||
return ret;
|
||||
|
||||
case XEN_SYSCTL_pm_op_set_max_cstate:
|
||||
if ( op->cpuid == 0 )
|
||||
acpi_set_cstate_limit(op->u.set_max_cstate);
|
||||
else if ( op->cpuid == 1 )
|
||||
acpi_set_csubstate_limit(op->u.set_max_cstate);
|
||||
else
|
||||
ret = -EINVAL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ( op->cpuid >= nr_cpu_ids || !cpu_online(op->cpuid) )
|
||||
return -EINVAL;
|
||||
pmpt = processor_pminfo[op->cpuid];
|
||||
|
||||
switch ( op->cmd & PM_PARA_CATEGORY_MASK )
|
||||
{
|
||||
case CPUFREQ_PARA:
|
||||
if ( !(xen_processor_pmbits & XEN_PROCESSOR_PM_PX) )
|
||||
return -ENODEV;
|
||||
if ( !pmpt || !(pmpt->init & XEN_PX_INIT) )
|
||||
return -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
switch ( op->cmd )
|
||||
{
|
||||
case GET_CPUFREQ_PARA:
|
||||
{
|
||||
ret = get_cpufreq_para(op);
|
||||
break;
|
||||
}
|
||||
|
||||
case SET_CPUFREQ_GOV:
|
||||
{
|
||||
ret = set_cpufreq_gov(op);
|
||||
break;
|
||||
}
|
||||
|
||||
case SET_CPUFREQ_PARA:
|
||||
{
|
||||
ret = set_cpufreq_para(op);
|
||||
break;
|
||||
}
|
||||
|
||||
case SET_CPUFREQ_CPPC:
|
||||
ret = set_cpufreq_cppc(op);
|
||||
break;
|
||||
|
||||
case GET_CPUFREQ_AVGFREQ:
|
||||
{
|
||||
op->u.get_avgfreq = cpufreq_driver_getavg(op->cpuid, USR_GETAVG);
|
||||
break;
|
||||
}
|
||||
|
||||
case XEN_SYSCTL_pm_op_enable_turbo:
|
||||
{
|
||||
ret = cpufreq_update_turbo(op->cpuid, CPUFREQ_TURBO_ENABLED);
|
||||
break;
|
||||
}
|
||||
|
||||
case XEN_SYSCTL_pm_op_disable_turbo:
|
||||
{
|
||||
ret = cpufreq_update_turbo(op->cpuid, CPUFREQ_TURBO_DISABLED);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
printk("not defined sub-hypercall @ do_pm_op\n");
|
||||
ret = -ENOSYS;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -64,6 +64,7 @@ static int cf_check cpufreq_governor_userspace(
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_OP
|
||||
int write_userspace_scaling_setspeed(unsigned int cpu, unsigned int freq)
|
||||
{
|
||||
struct cpufreq_policy *policy;
|
||||
@@ -80,6 +81,7 @@ int write_userspace_scaling_setspeed(unsigned int cpu, unsigned int freq)
|
||||
|
||||
return __cpufreq_driver_target(policy, freq, CPUFREQ_RELATION_L);
|
||||
}
|
||||
#endif /* CONFIG_PM_OP */
|
||||
|
||||
static bool __init cf_check
|
||||
cpufreq_userspace_handle_option(const char *name, const char *val)
|
||||
|
||||
@@ -57,6 +57,7 @@ static struct dbs_tuners {
|
||||
|
||||
static DEFINE_PER_CPU(struct timer, dbs_timer);
|
||||
|
||||
#ifdef CONFIG_PM_OP
|
||||
int write_ondemand_sampling_rate(unsigned int sampling_rate)
|
||||
{
|
||||
if ( (sampling_rate > MAX_SAMPLING_RATE / MICROSECS(1)) ||
|
||||
@@ -93,6 +94,7 @@ int get_cpufreq_ondemand_para(uint32_t *sampling_rate_max,
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_PM_OP */
|
||||
|
||||
static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info)
|
||||
{
|
||||
|
||||
@@ -224,47 +224,6 @@ int cpufreq_driver_getavg(unsigned int cpu, unsigned int flag)
|
||||
return policy->cur;
|
||||
}
|
||||
|
||||
int cpufreq_update_turbo(unsigned int cpu, int new_state)
|
||||
{
|
||||
struct cpufreq_policy *policy;
|
||||
int curr_state;
|
||||
int ret = 0;
|
||||
|
||||
if (new_state != CPUFREQ_TURBO_ENABLED &&
|
||||
new_state != CPUFREQ_TURBO_DISABLED)
|
||||
return -EINVAL;
|
||||
|
||||
policy = per_cpu(cpufreq_cpu_policy, cpu);
|
||||
if (!policy)
|
||||
return -EACCES;
|
||||
|
||||
if (policy->turbo == CPUFREQ_TURBO_UNSUPPORTED)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
curr_state = policy->turbo;
|
||||
if (curr_state == new_state)
|
||||
return 0;
|
||||
|
||||
policy->turbo = new_state;
|
||||
if (cpufreq_driver.update)
|
||||
{
|
||||
ret = alternative_call(cpufreq_driver.update, cpu, policy);
|
||||
if (ret)
|
||||
policy->turbo = curr_state;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int cpufreq_get_turbo_status(unsigned int cpu)
|
||||
{
|
||||
struct cpufreq_policy *policy;
|
||||
|
||||
policy = per_cpu(cpufreq_cpu_policy, cpu);
|
||||
return policy && policy->turbo == CPUFREQ_TURBO_ENABLED;
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* POLICY *
|
||||
*********************************************************************/
|
||||
|
||||
@@ -143,9 +143,6 @@ extern int cpufreq_driver_getavg(unsigned int cpu, unsigned int flag);
|
||||
#define CPUFREQ_TURBO_UNSUPPORTED 0
|
||||
#define CPUFREQ_TURBO_ENABLED 1
|
||||
|
||||
int cpufreq_update_turbo(unsigned int cpu, int new_state);
|
||||
int cpufreq_get_turbo_status(unsigned int cpu);
|
||||
|
||||
static inline int
|
||||
__cpufreq_governor(struct cpufreq_policy *policy, unsigned int event)
|
||||
{
|
||||
|
||||