Commit 394122ab authored by Rusty Russell's avatar Rusty Russell Committed by Dave Jones

[CPUFREQ] cpumask: avoid playing with cpus_allowed in speedstep-ich.c

Impact: don't play with current's cpumask

It's generally a very bad idea to mug some process's cpumask: it could
legitimately and reasonably be changed by root, which could break us
(if done before our code) or them (if we restore the wrong value).

We use smp_call_function_single: this had the advantage of being more
efficient, too.
Signed-off-by: default avatarRusty Russell <rusty@rustcorp.com.au>
To: cpufreq@vger.kernel.org
Cc: Dominik Brodowski <linux@brodo.de>
Signed-off-by: default avatarDave Jones <davej@redhat.com>
parent e15bc455
...@@ -89,7 +89,8 @@ static int speedstep_find_register(void) ...@@ -89,7 +89,8 @@ static int speedstep_find_register(void)
* speedstep_set_state - set the SpeedStep state * speedstep_set_state - set the SpeedStep state
* @state: new processor frequency state (SPEEDSTEP_LOW or SPEEDSTEP_HIGH) * @state: new processor frequency state (SPEEDSTEP_LOW or SPEEDSTEP_HIGH)
* *
* Tries to change the SpeedStep state. * Tries to change the SpeedStep state. Can be called from
* smp_call_function_single.
*/ */
static void speedstep_set_state(unsigned int state) static void speedstep_set_state(unsigned int state)
{ {
...@@ -143,6 +144,11 @@ static void speedstep_set_state(unsigned int state) ...@@ -143,6 +144,11 @@ static void speedstep_set_state(unsigned int state)
return; return;
} }
/* Wrapper for smp_call_function_single. */
static void _speedstep_set_state(void *_state)
{
speedstep_set_state(*(unsigned int *)_state);
}
/** /**
* speedstep_activate - activate SpeedStep control in the chipset * speedstep_activate - activate SpeedStep control in the chipset
...@@ -226,22 +232,28 @@ static unsigned int speedstep_detect_chipset(void) ...@@ -226,22 +232,28 @@ static unsigned int speedstep_detect_chipset(void)
return 0; return 0;
} }
static unsigned int _speedstep_get(const struct cpumask *cpus) struct get_freq_data {
{
unsigned int speed; unsigned int speed;
cpumask_t cpus_allowed; unsigned int processor;
};
cpus_allowed = current->cpus_allowed;
set_cpus_allowed_ptr(current, cpus); static void get_freq_data(void *_data)
speed = speedstep_get_frequency(speedstep_processor); {
set_cpus_allowed_ptr(current, &cpus_allowed); struct get_freq_data *data = _data;
dprintk("detected %u kHz as current frequency\n", speed);
return speed; data->speed = speedstep_get_frequency(data->processor);
} }
static unsigned int speedstep_get(unsigned int cpu) static unsigned int speedstep_get(unsigned int cpu)
{ {
return _speedstep_get(cpumask_of(cpu)); struct get_freq_data data = { .processor = cpu };
/* You're supposed to ensure CPU is online. */
if (smp_call_function_single(cpu, get_freq_data, &data, 1) != 0)
BUG();
dprintk("detected %u kHz as current frequency\n", data.speed);
return data.speed;
} }
/** /**
...@@ -257,16 +269,16 @@ static int speedstep_target(struct cpufreq_policy *policy, ...@@ -257,16 +269,16 @@ static int speedstep_target(struct cpufreq_policy *policy,
unsigned int target_freq, unsigned int target_freq,
unsigned int relation) unsigned int relation)
{ {
unsigned int newstate = 0; unsigned int newstate = 0, policy_cpu;
struct cpufreq_freqs freqs; struct cpufreq_freqs freqs;
cpumask_t cpus_allowed;
int i; int i;
if (cpufreq_frequency_table_target(policy, &speedstep_freqs[0], if (cpufreq_frequency_table_target(policy, &speedstep_freqs[0],
target_freq, relation, &newstate)) target_freq, relation, &newstate))
return -EINVAL; return -EINVAL;
freqs.old = _speedstep_get(policy->cpus); policy_cpu = cpumask_any_and(policy->cpus, cpu_online_mask);
freqs.old = speedstep_get(policy_cpu);
freqs.new = speedstep_freqs[newstate].frequency; freqs.new = speedstep_freqs[newstate].frequency;
freqs.cpu = policy->cpu; freqs.cpu = policy->cpu;
...@@ -276,20 +288,13 @@ static int speedstep_target(struct cpufreq_policy *policy, ...@@ -276,20 +288,13 @@ static int speedstep_target(struct cpufreq_policy *policy,
if (freqs.old == freqs.new) if (freqs.old == freqs.new)
return 0; return 0;
cpus_allowed = current->cpus_allowed;
for_each_cpu(i, policy->cpus) { for_each_cpu(i, policy->cpus) {
freqs.cpu = i; freqs.cpu = i;
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
} }
/* switch to physical CPU where state is to be changed */ smp_call_function_single(policy_cpu, _speedstep_set_state, &newstate,
set_cpus_allowed_ptr(current, policy->cpus); true);
speedstep_set_state(newstate);
/* allow to be run on all CPUs */
set_cpus_allowed_ptr(current, &cpus_allowed);
for_each_cpu(i, policy->cpus) { for_each_cpu(i, policy->cpus) {
freqs.cpu = i; freqs.cpu = i;
...@@ -312,33 +317,43 @@ static int speedstep_verify(struct cpufreq_policy *policy) ...@@ -312,33 +317,43 @@ static int speedstep_verify(struct cpufreq_policy *policy)
return cpufreq_frequency_table_verify(policy, &speedstep_freqs[0]); return cpufreq_frequency_table_verify(policy, &speedstep_freqs[0]);
} }
struct get_freqs {
struct cpufreq_policy *policy;
int ret;
};
static void get_freqs_on_cpu(void *_get_freqs)
{
struct get_freqs *get_freqs = _get_freqs;
get_freqs->ret =
speedstep_get_freqs(speedstep_processor,
&speedstep_freqs[SPEEDSTEP_LOW].frequency,
&speedstep_freqs[SPEEDSTEP_HIGH].frequency,
&get_freqs->policy->cpuinfo.transition_latency,
&speedstep_set_state);
}
static int speedstep_cpu_init(struct cpufreq_policy *policy) static int speedstep_cpu_init(struct cpufreq_policy *policy)
{ {
int result = 0; int result;
unsigned int speed; unsigned int policy_cpu, speed;
cpumask_t cpus_allowed; struct get_freqs gf;
/* only run on CPU to be set, or on its sibling */ /* only run on CPU to be set, or on its sibling */
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
cpumask_copy(policy->cpus, cpu_sibling_mask(policy->cpu)); cpumask_copy(policy->cpus, cpu_sibling_mask(policy->cpu));
#endif #endif
policy_cpu = cpumask_any_and(policy->cpus, cpu_online_mask);
cpus_allowed = current->cpus_allowed;
set_cpus_allowed_ptr(current, policy->cpus);
/* detect low and high frequency and transition latency */ /* detect low and high frequency and transition latency */
result = speedstep_get_freqs(speedstep_processor, gf.policy = policy;
&speedstep_freqs[SPEEDSTEP_LOW].frequency, smp_call_function_single(policy_cpu, get_freqs_on_cpu, &gf, 1);
&speedstep_freqs[SPEEDSTEP_HIGH].frequency, if (gf.ret)
&policy->cpuinfo.transition_latency, return gf.ret;
&speedstep_set_state);
set_cpus_allowed_ptr(current, &cpus_allowed);
if (result)
return result;
/* get current speed setting */ /* get current speed setting */
speed = _speedstep_get(policy->cpus); speed = speedstep_get(policy_cpu);
if (!speed) if (!speed)
return -EIO; return -EIO;
......
...@@ -226,6 +226,7 @@ static unsigned int pentium4_get_frequency(void) ...@@ -226,6 +226,7 @@ static unsigned int pentium4_get_frequency(void)
} }
/* Warning: may get called from smp_call_function_single. */
unsigned int speedstep_get_frequency(unsigned int processor) unsigned int speedstep_get_frequency(unsigned int processor)
{ {
switch (processor) { switch (processor) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment