Thanks, I'll merge this to android-3.4 soon. Todd
On Thu, Dec 13, 2012 at 9:47 AM, Viresh Kumar <viresh.ku...@linaro.org> wrote: > From: Nicolas Pitre <nicolas.pi...@linaro.org> > > Relying on pcpu->governor_enabled alone to ensure proper shutdown > of the governor activities is flawed. The following race is perfectly > possible: > > CPU0 |CPU2 > ---- +---- > |cpufreq_interactive_timer() > cpufreq_governor_interactive(GOV_STOP) |if (!pcpu->governor_enabled) > pcpu->governor_enabled = 0; | return; [untaken] > smp_wmb(); |[...] > del_timer_sync(&pcpu->cpu_timer); |cpufreq_interactive_timer_resched() > |mod_timer_pinned(&pcpu->cpu_timer) > > Result: timer is pending again. It is also possible for > del_timer_sync() to race against the invocation of the idle notifier > callback which also has the potential to reactivate the timer. > > To fix this issue, a read-write semaphore is used. Multiple readers are > allowed as long as pcpu->governor_enabled is true. However it can be > moved to false only after taking a write semaphore which would wait for > any on-going asynchronous activities to complete and prevent any more of > those activities to be initiated. > > Signed-off-by: Nicolas Pitre <nicolas.pi...@linaro.org> > --- > drivers/cpufreq/cpufreq_interactive.c | 38 > ++++++++++++++++++++++++++--------- > 1 file changed, 28 insertions(+), 10 deletions(-) > > diff --git a/drivers/cpufreq/cpufreq_interactive.c > b/drivers/cpufreq/cpufreq_interactive.c > index c82d9fe..061af7f 100644 > --- a/drivers/cpufreq/cpufreq_interactive.c > +++ b/drivers/cpufreq/cpufreq_interactive.c > @@ -21,14 +21,13 @@ > #include <linux/cpufreq.h> > #include <linux/module.h> > #include <linux/moduleparam.h> > -#include <linux/mutex.h> > +#include <linux/rwsem.h> > #include <linux/sched.h> > #include <linux/tick.h> > #include <linux/time.h> > #include <linux/timer.h> > #include <linux/workqueue.h> > #include <linux/kthread.h> > -#include <linux/mutex.h> > #include <linux/slab.h> > #include <asm/cputime.h> > > @@ -50,6 +49,7 @@ struct cpufreq_interactive_cpuinfo { > unsigned int floor_freq; > u64 floor_validate_time; > u64 hispeed_validate_time; > + struct rw_semaphore mutex; > int governor_enabled; > }; > > @@ -138,8 +138,8 @@ static void cpufreq_interactive_timer(unsigned long data) > unsigned int index; > unsigned long flags; > > - smp_rmb(); > - > + if (!down_read_trylock(&pcpu->mutex)) > + return; > if (!pcpu->governor_enabled) > goto exit; > > @@ -258,6 +258,7 @@ rearm: > } > > exit: > + up_read(&pcpu->mutex); > return; > } > > @@ -267,8 +268,12 @@ static void cpufreq_interactive_idle_start(void) > &per_cpu(cpuinfo, smp_processor_id()); > int pending; > > - if (!pcpu->governor_enabled) > + if (!down_read_trylock(&pcpu->mutex)) > + return; > + if (!pcpu->governor_enabled) { > + up_read(&pcpu->mutex); > return; > + } > > pending = timer_pending(&pcpu->cpu_timer); > > @@ -298,6 +303,7 @@ static void cpufreq_interactive_idle_start(void) > } > } > > + up_read(&pcpu->mutex); > } > > static void cpufreq_interactive_idle_end(void) > @@ -305,8 +311,12 @@ static void cpufreq_interactive_idle_end(void) > struct cpufreq_interactive_cpuinfo *pcpu = > &per_cpu(cpuinfo, smp_processor_id()); > > - if (!pcpu->governor_enabled) > + if (!down_read_trylock(&pcpu->mutex)) > + return; > + if (!pcpu->governor_enabled) { > + up_read(&pcpu->mutex); > return; > + } > > /* Arm the timer for 1-2 ticks later if not already. */ > if (!timer_pending(&pcpu->cpu_timer)) { > @@ -317,6 +327,8 @@ static void cpufreq_interactive_idle_end(void) > del_timer(&pcpu->cpu_timer); > cpufreq_interactive_timer(smp_processor_id()); > } > + > + up_read(&pcpu->mutex); > } > > static int cpufreq_interactive_speedchange_task(void *data) > @@ -351,10 +363,12 @@ static int cpufreq_interactive_speedchange_task(void > *data) > unsigned int max_freq = 0; > > pcpu = &per_cpu(cpuinfo, cpu); > - smp_rmb(); > - > - if (!pcpu->governor_enabled) > + if (!down_read_trylock(&pcpu->mutex)) > + continue; > + if (!pcpu->governor_enabled) { > + up_read(&pcpu->mutex); > continue; > + } > > for_each_cpu(j, pcpu->policy->cpus) { > struct cpufreq_interactive_cpuinfo *pjcpu = > @@ -371,6 +385,8 @@ static int cpufreq_interactive_speedchange_task(void > *data) > trace_cpufreq_interactive_setspeed(cpu, > pcpu->target_freq, > pcpu->policy->cur); > + > + up_read(&pcpu->mutex); > } > } > > @@ -690,9 +706,10 @@ static int cpufreq_governor_interactive(struct > cpufreq_policy *policy, > case CPUFREQ_GOV_STOP: > for_each_cpu(j, policy->cpus) { > pcpu = &per_cpu(cpuinfo, j); > + down_write(&pcpu->mutex); > pcpu->governor_enabled = 0; > - smp_wmb(); > del_timer_sync(&pcpu->cpu_timer); > + up_write(&pcpu->mutex); > } > > if (atomic_dec_return(&active_count) > 0) > @@ -736,6 +753,7 @@ static int __init cpufreq_interactive_init(void) > init_timer_deferrable(&pcpu->cpu_timer); > pcpu->cpu_timer.function = cpufreq_interactive_timer; > pcpu->cpu_timer.data = i; > + init_rwsem(&pcpu->mutex); > } > > spin_lock_init(&speedchange_cpumask_lock); > -- > 1.7.12.rc2.18.g61b472e > _______________________________________________ linaro-dev mailing list linaro-dev@lists.linaro.org http://lists.linaro.org/mailman/listinfo/linaro-dev