Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save Oberon00/70da36f5df80605d0c52 to your computer and use it in GitHub Desktop.
Save Oberon00/70da36f5df80605d0c52 to your computer and use it in GitHub Desktop.
From b21cd087bf1f9c3e98cccf56b405dcc8ac6b11ed Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Christian=20Neum=C3=BCller?= <[email protected]>
Date: Tue, 15 Dec 2015 11:16:59 +0100
Subject: [PATCH] exynos4210: Fix race condition in idle states.
1. Initially, only CPU0 is online.
2. CPU0 starts to bring CPU1 online.
3. CPU0 is waiting for CPU1 to come online, and meanwhile enters idle. Since it
is the only online CPU, it lowers the CPU speed to save power.
4. CPU1 goes online (NOTE: While CPU0 is still idling!)
The problem definitely affects exynos4_enter_idle, but it is likely that the low
power modes are also affected by this race condition.
Note that local_irq_disable does not help here because CPU1 could
already be starting up (most_likely idle could be entered on CPU0 while in
"wait_for_completion_timeout(&cpu_running, ...)" in arch/arm/kernel/smp.c:126.
Calling get_online_cpus() effectively waits for the hotplug operation to
complete if one is in progress. Note that this function might_sleep() so it
cannot be called with interrupts disabled.
Change-Id: If130c3c5e9098fa6a6b1ae138a0e937541b7292f
---
arch/arm/mach-exynos/cpuidle-exynos4.c | 21 ++++++++++++++++-----
1 file changed, 16 insertions(+), 5 deletions(-)
diff --git a/arch/arm/mach-exynos/cpuidle-exynos4.c b/arch/arm/mach-exynos/cpuidle-exynos4.c
index 88b941f..c67e8b6 100644
--- a/arch/arm/mach-exynos/cpuidle-exynos4.c
+++ b/arch/arm/mach-exynos/cpuidle-exynos4.c
@@ -830,6 +830,9 @@ static int exynos4_enter_idle(struct cpuidle_device *dev,
int cpu;
unsigned int tmp;
+ if (use_clock_down == SW_CLK_DWN)
+ get_online_cpus();
+
local_irq_disable();
do_gettimeofday(&before);
@@ -876,6 +879,8 @@ static int exynos4_enter_idle(struct cpuidle_device *dev,
do_gettimeofday(&after);
local_irq_enable();
+ if (use_clock_down == SW_CLK_DWN)
+ put_online_cpus();
idle_time = (after.tv_sec - before.tv_sec) * USEC_PER_SEC +
(after.tv_usec - before.tv_usec);
@@ -914,6 +919,7 @@ static int exynos4_enter_lowpower(struct cpuidle_device *dev,
int ret;
/* This mode only can be entered when only Core0 is online */
+ get_online_cpus();
if (num_online_cpus() != 1) {
BUG_ON(!dev->safe_state);
new_state = dev->safe_state;
@@ -925,13 +931,16 @@ static int exynos4_enter_lowpower(struct cpuidle_device *dev,
__raw_writel(tmp, S5P_CENTRAL_SEQ_OPTION);
}
- if (new_state == &dev->states[0])
- return exynos4_enter_idle(dev, new_state);
+ if (new_state == &dev->states[0]) {
+ ret = exynos4_enter_idle(dev, new_state);
+ goto out;
+ }
enter_mode = exynos4_check_entermode();
- if (!enter_mode)
- return exynos4_enter_idle(dev, new_state);
- else {
+ if (!enter_mode) {
+ ret = exynos4_enter_idle(dev, new_state);
+ goto out;
+ } else {
#ifdef CONFIG_CORESIGHT_ETM
etm_disable(0);
#endif
@@ -943,6 +952,8 @@ static int exynos4_enter_lowpower(struct cpuidle_device *dev,
etm_enable(0);
#endif
}
+out:
+ put_online_cpus();
return ret;
}
--
1.9.1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment