[ACPI/CPUFREQ] Introduce bios_limit per cpu cpufreq sysfs interface
This interface is mainly intended (and implemented) for ACPI _PPC BIOS frequency limitations, but other cpufreq drivers can also use it for similar use-cases. Why is this needed: Currently it's not obvious why cpufreq got limited. People see cpufreq/scaling_max_freq reduced, but this could have happened by: - any userspace prog writing to scaling_max_freq - thermal limitations - hardware (_PPC in ACPI case) limitiations Therefore export bios_limit (in kHz) to: - Point the user that it's the BIOS (broken or intended) which limits frequency - Export it as a sysfs interface for userspace progs. While this was a rarely used feature on laptops, there will appear more and more server implemenations providing "Green IT" features like allowing the service processor to limit the frequency. People want to know about HW/BIOS frequency limitations. All ACPI P-state driven cpufreq drivers are covered with this patch: - powernow-k8 - powernow-k7 - acpi-cpufreq Tested with a patched DSDT which limits the first two cores (_PPC returns 1) via _PPC, exposed by bios_limit: # echo 2200000 >cpu2/cpufreq/scaling_max_freq # cat cpu*/cpufreq/scaling_max_freq 2600000 2600000 2200000 2200000 # #scaling_max_freq shows general user/thermal/BIOS limitations # cat cpu*/cpufreq/bios_limit 2600000 2600000 2800000 2800000 # #bios_limit only shows the HW/BIOS limitation CC: Pallipadi Venkatesh <venkatesh.pallipadi@intel.com> CC: Len Brown <lenb@kernel.org> CC: davej@codemonkey.org.uk CC: linux@dominikbrodowski.net Signed-off-by: Thomas Renninger <trenn@suse.de> Signed-off-by: Dave Jones <davej@redhat.com>
This commit is contained in:
Родитель
cf3289d0e7
Коммит
e2f74f355e
|
@ -203,6 +203,17 @@ scaling_cur_freq : Current frequency of the CPU as determined by
|
|||
the frequency the kernel thinks the CPU runs
|
||||
at.
|
||||
|
||||
bios_limit : If the BIOS tells the OS to limit a CPU to
|
||||
lower frequencies, the user can read out the
|
||||
maximum available frequency from this file.
|
||||
This typically can happen through (often not
|
||||
intended) BIOS settings, restrictions
|
||||
triggered through a service processor or other
|
||||
BIOS/HW based implementations.
|
||||
This does not cover thermal ACPI limitations
|
||||
which can be detected through the generic
|
||||
thermal driver.
|
||||
|
||||
If you have selected the "userspace" governor which allows you to
|
||||
set the CPU operating frequency to a specific value, you can read out
|
||||
the current frequency in
|
||||
|
|
|
@ -764,14 +764,15 @@ static struct freq_attr *acpi_cpufreq_attr[] = {
|
|||
};
|
||||
|
||||
static struct cpufreq_driver acpi_cpufreq_driver = {
|
||||
.verify = acpi_cpufreq_verify,
|
||||
.target = acpi_cpufreq_target,
|
||||
.init = acpi_cpufreq_cpu_init,
|
||||
.exit = acpi_cpufreq_cpu_exit,
|
||||
.resume = acpi_cpufreq_resume,
|
||||
.name = "acpi-cpufreq",
|
||||
.owner = THIS_MODULE,
|
||||
.attr = acpi_cpufreq_attr,
|
||||
.verify = acpi_cpufreq_verify,
|
||||
.target = acpi_cpufreq_target,
|
||||
.bios_limit = acpi_processor_get_bios_limit,
|
||||
.init = acpi_cpufreq_cpu_init,
|
||||
.exit = acpi_cpufreq_cpu_exit,
|
||||
.resume = acpi_cpufreq_resume,
|
||||
.name = "acpi-cpufreq",
|
||||
.owner = THIS_MODULE,
|
||||
.attr = acpi_cpufreq_attr,
|
||||
};
|
||||
|
||||
static int __init acpi_cpufreq_init(void)
|
||||
|
|
|
@ -714,14 +714,17 @@ static struct freq_attr *powernow_table_attr[] = {
|
|||
};
|
||||
|
||||
static struct cpufreq_driver powernow_driver = {
|
||||
.verify = powernow_verify,
|
||||
.target = powernow_target,
|
||||
.get = powernow_get,
|
||||
.init = powernow_cpu_init,
|
||||
.exit = powernow_cpu_exit,
|
||||
.name = "powernow-k7",
|
||||
.owner = THIS_MODULE,
|
||||
.attr = powernow_table_attr,
|
||||
.verify = powernow_verify,
|
||||
.target = powernow_target,
|
||||
.get = powernow_get,
|
||||
#ifdef CONFIG_X86_POWERNOW_K7_ACPI
|
||||
.bios_limit = acpi_processor_get_bios_limit,
|
||||
#endif
|
||||
.init = powernow_cpu_init,
|
||||
.exit = powernow_cpu_exit,
|
||||
.name = "powernow-k7",
|
||||
.owner = THIS_MODULE,
|
||||
.attr = powernow_table_attr,
|
||||
};
|
||||
|
||||
static int __init powernow_init(void)
|
||||
|
|
|
@ -1398,14 +1398,15 @@ static struct freq_attr *powernow_k8_attr[] = {
|
|||
};
|
||||
|
||||
static struct cpufreq_driver cpufreq_amd64_driver = {
|
||||
.verify = powernowk8_verify,
|
||||
.target = powernowk8_target,
|
||||
.init = powernowk8_cpu_init,
|
||||
.exit = __devexit_p(powernowk8_cpu_exit),
|
||||
.get = powernowk8_get,
|
||||
.name = "powernow-k8",
|
||||
.owner = THIS_MODULE,
|
||||
.attr = powernow_k8_attr,
|
||||
.verify = powernowk8_verify,
|
||||
.target = powernowk8_target,
|
||||
.bios_limit = acpi_processor_get_bios_limit,
|
||||
.init = powernowk8_cpu_init,
|
||||
.exit = __devexit_p(powernowk8_cpu_exit),
|
||||
.get = powernowk8_get,
|
||||
.name = "powernow-k8",
|
||||
.owner = THIS_MODULE,
|
||||
.attr = powernow_k8_attr,
|
||||
};
|
||||
|
||||
/* driver entry point for init */
|
||||
|
|
|
@ -167,6 +167,19 @@ int acpi_processor_ppc_has_changed(struct acpi_processor *pr)
|
|||
return cpufreq_update_policy(pr->id);
|
||||
}
|
||||
|
||||
int acpi_processor_get_bios_limit(int cpu, unsigned int *limit)
|
||||
{
|
||||
struct acpi_processor *pr;
|
||||
|
||||
pr = per_cpu(processors, cpu);
|
||||
if (!pr || !pr->performance || !pr->performance->state_count)
|
||||
return -ENODEV;
|
||||
*limit = pr->performance->states[pr->performance_platform_limit].
|
||||
core_frequency * 1000;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(acpi_processor_get_bios_limit);
|
||||
|
||||
void acpi_processor_ppc_init(void)
|
||||
{
|
||||
if (!cpufreq_register_notifier
|
||||
|
|
|
@ -647,6 +647,21 @@ static ssize_t show_scaling_setspeed(struct cpufreq_policy *policy, char *buf)
|
|||
return policy->governor->show_setspeed(policy, buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* show_scaling_driver - show the current cpufreq HW/BIOS limitation
|
||||
*/
|
||||
static ssize_t show_bios_limit(struct cpufreq_policy *policy, char *buf)
|
||||
{
|
||||
unsigned int limit;
|
||||
int ret;
|
||||
if (cpufreq_driver->bios_limit) {
|
||||
ret = cpufreq_driver->bios_limit(policy->cpu, &limit);
|
||||
if (!ret)
|
||||
return sprintf(buf, "%u\n", limit);
|
||||
}
|
||||
return sprintf(buf, "%u\n", policy->cpuinfo.max_freq);
|
||||
}
|
||||
|
||||
#define define_one_ro(_name) \
|
||||
static struct freq_attr _name = \
|
||||
__ATTR(_name, 0444, show_##_name, NULL)
|
||||
|
@ -666,6 +681,7 @@ define_one_ro(cpuinfo_transition_latency);
|
|||
define_one_ro(scaling_available_governors);
|
||||
define_one_ro(scaling_driver);
|
||||
define_one_ro(scaling_cur_freq);
|
||||
define_one_ro(bios_limit);
|
||||
define_one_ro(related_cpus);
|
||||
define_one_ro(affected_cpus);
|
||||
define_one_rw(scaling_min_freq);
|
||||
|
@ -905,6 +921,11 @@ static int cpufreq_add_dev_interface(unsigned int cpu,
|
|||
if (ret)
|
||||
goto err_out_kobj_put;
|
||||
}
|
||||
if (cpufreq_driver->bios_limit) {
|
||||
ret = sysfs_create_file(&policy->kobj, &bios_limit.attr);
|
||||
if (ret)
|
||||
goto err_out_kobj_put;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&cpufreq_driver_lock, flags);
|
||||
for_each_cpu(j, policy->cpus) {
|
||||
|
|
|
@ -295,6 +295,7 @@ static inline void acpi_processor_ffh_cstate_enter(struct acpi_processor_cx
|
|||
void acpi_processor_ppc_init(void);
|
||||
void acpi_processor_ppc_exit(void);
|
||||
int acpi_processor_ppc_has_changed(struct acpi_processor *pr);
|
||||
extern int acpi_processor_get_bios_limit(int cpu, unsigned int *limit);
|
||||
#else
|
||||
static inline void acpi_processor_ppc_init(void)
|
||||
{
|
||||
|
@ -316,6 +317,11 @@ static inline int acpi_processor_ppc_has_changed(struct acpi_processor *pr)
|
|||
}
|
||||
return 0;
|
||||
}
|
||||
static inline int acpi_processor_get_bios_limit(int cpu, unsigned int *limit)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_CPU_FREQ */
|
||||
|
||||
/* in processor_throttling.c */
|
||||
|
|
|
@ -232,6 +232,7 @@ struct cpufreq_driver {
|
|||
/* optional */
|
||||
unsigned int (*getavg) (struct cpufreq_policy *policy,
|
||||
unsigned int cpu);
|
||||
int (*bios_limit) (int cpu, unsigned int *limit);
|
||||
|
||||
int (*exit) (struct cpufreq_policy *policy);
|
||||
int (*suspend) (struct cpufreq_policy *policy, pm_message_t pmsg);
|
||||
|
|
Загрузка…
Ссылка в новой задаче