From 42851e3c2fc4a8e704683c309c03d5af42a8f398 Mon Sep 17 00:00:00 2001 Message-Id: <42851e3c2fc4a8e704683c309c03d5af42a8f398.1378885668.git.viresh.kumar@linaro.org> In-Reply-To: References: From: Viresh Kumar Date: Wed, 11 Sep 2013 11:42:18 +0530 Subject: [PATCH 2/2] cpufreq: fix notification serialization issues Sometimes ->target() returns earlier than expected, even without doing any PRECHANGE notifications, in such cases we aren't decrementing our transition_ongoing counter and that makes it go crazy for those cases. This patch fixes this issue by taking care of transition_ongoing count at several places. Now we simply decrement refcount at the end of cpufreq_out_of_sync() & __cpufreq_driver_target() for drivers that don't do ASYNC notifications, for others we do that from POSTCHANGE notifier. These drivers also return -EINPROGRESS from their target() routines to mark that transtion will be completed later. Signed-off-by: Viresh Kumar --- drivers/cpufreq/cpufreq.c | 42 ++++++++++++++++++++++++++++-------- drivers/cpufreq/exynos5440-cpufreq.c | 3 +++ 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 9cc5609..9644150 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -348,7 +348,8 @@ void cpufreq_notify_transition(struct cpufreq_policy *policy, for_each_cpu(freqs->cpu, policy->cpus) __cpufreq_notify_transition(policy, freqs, state); - if (state == CPUFREQ_POSTCHANGE) { + if ((cpufreq_driver->flags & CPUFREQ_ASYNC_NOTIFICATION) + && (state == CPUFREQ_POSTCHANGE)) { unsigned long flags; /* @@ -1406,6 +1407,17 @@ static void cpufreq_out_of_sync(unsigned int cpu, unsigned int old_freq, cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE); cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE); + + /* + * For drivers with CPUFREQ_ASYNC_NOTIFICATION flag set, we decrement + * transition_ongoing from POSTCHANGE notifiers. + */ + if (cpufreq_driver->flags & CPUFREQ_ASYNC_NOTIFICATION) + return; + + write_lock_irqsave(&cpufreq_driver_lock, flags); + policy->transition_ongoing--; + write_unlock_irqrestore(&cpufreq_driver_lock, flags); } /** @@ -1696,14 +1708,6 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy, if (cpufreq_disabled()) return -ENODEV; - write_lock_irqsave(&cpufreq_driver_lock, flags); - if (policy->transition_ongoing) { - write_unlock_irqrestore(&cpufreq_driver_lock, flags); - return -EBUSY; - } - policy->transition_ongoing++; - write_unlock_irqrestore(&cpufreq_driver_lock, flags); - /* Make sure that target_freq is within supported range */ if (target_freq > policy->max) target_freq = policy->max; @@ -1716,9 +1720,29 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy, if (target_freq == policy->cur) return 0; + write_lock_irqsave(&cpufreq_driver_lock, flags); + if (policy->transition_ongoing) { + write_unlock_irqrestore(&cpufreq_driver_lock, flags); + return -EBUSY; + } + policy->transition_ongoing++; + write_unlock_irqrestore(&cpufreq_driver_lock, flags); + if (cpufreq_driver->target) retval = cpufreq_driver->target(policy, target_freq, relation); + /* + * For drivers with CPUFREQ_ASYNC_NOTIFICATION flag set, we decrement + * transition_ongoing from POSTCHANGE notifiers. + */ + if ((cpufreq_driver->flags & CPUFREQ_ASYNC_NOTIFICATION) + && (retval == -EINPROGRESS)) + return retval; + + write_lock_irqsave(&cpufreq_driver_lock, flags); + policy->transition_ongoing--; + write_unlock_irqrestore(&cpufreq_driver_lock, flags); + return retval; } EXPORT_SYMBOL_GPL(__cpufreq_driver_target); diff --git a/drivers/cpufreq/exynos5440-cpufreq.c b/drivers/cpufreq/exynos5440-cpufreq.c index f44664a..1e391ac 100644 --- a/drivers/cpufreq/exynos5440-cpufreq.c +++ b/drivers/cpufreq/exynos5440-cpufreq.c @@ -251,6 +251,9 @@ static int exynos_target(struct cpufreq_policy *policy, __raw_writel(tmp, dvfs_info->base + XMU_C0_3_PSTATE + i * 4); } + + /* Mark transition as In-progress */ + ret = -EINPROGRESS; out: mutex_unlock(&cpufreq_lock); return ret; -- 1.7.12.rc2.18.g61b472e