From 42851e3c2fc4a8e704683c309c03d5af42a8f398 Mon Sep 17 00:00:00 2001
Message-Id: <42851e3c2fc4a8e704683c309c03d5af42a8f398.1378885668.git.viresh.kumar@linaro.org>
In-Reply-To: <a153752719774763f9edd8f46291f1bab722f7d6.1378885668.git.viresh.kumar@linaro.org>
References: <a153752719774763f9edd8f46291f1bab722f7d6.1378885668.git.viresh.kumar@linaro.org>
From: Viresh Kumar <viresh.kumar@linaro.org>
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 <viresh.kumar@linaro.org>
---
 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

