summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNishanth Menon <nm@ti.com>2012-03-15 19:24:31 +0100
committerPaul Walmsley <paul@pwsan.com>2012-04-04 17:23:34 +0200
commit0eb4fd9b3e4d08973f7f086dbdb14a0ad5c3d76d (patch)
tree5e767d3317de04c638eac3adf5578959c8a303b1
parentARM: OMAP3xxx: clock data: fix DPLL4 CLKSEL masks (diff)
downloadlinux-0eb4fd9b3e4d08973f7f086dbdb14a0ad5c3d76d.tar.xz
linux-0eb4fd9b3e4d08973f7f086dbdb14a0ad5c3d76d.zip
ARM: OMAP: clock: fix race in disable all clocks
clk_disable_unused is invoked when CONFIG_OMAP_RESET_CLOCKS=y. Since clk_disable_unused is called as lateinitcall, there can be more than a few workqueues executing off secondary CPU(s). The current code does the following: a) checks if clk is unused b) holds lock c) disables clk d) unlocks Between (a) and (b) being executed on CPU0, It is possible to have a driver executing on CPU1 which could do a get_sync->clk_get (and increase the use_count) of the clock which was just about to be disabled by clk_disable_unused. We ensure instead that the entire list traversal is protected by the lock allowing for parent child clock traversal which could be potentially be done by runtime operations to be safe as well. Reported-by: Todd Poynor <toddpoynor@google.com> Signed-off-by: Nishanth Menon <nm@ti.com> Signed-off-by: Paul Walmsley <paul@pwsan.com>
-rw-r--r--arch/arm/plat-omap/clock.c5
1 files changed, 3 insertions, 2 deletions
diff --git a/arch/arm/plat-omap/clock.c b/arch/arm/plat-omap/clock.c
index 56b6f8b7053e..8506cbb7fea4 100644
--- a/arch/arm/plat-omap/clock.c
+++ b/arch/arm/plat-omap/clock.c
@@ -441,6 +441,8 @@ static int __init clk_disable_unused(void)
return 0;
pr_info("clock: disabling unused clocks to save power\n");
+
+ spin_lock_irqsave(&clockfw_lock, flags);
list_for_each_entry(ck, &clocks, node) {
if (ck->ops == &clkops_null)
continue;
@@ -448,10 +450,9 @@ static int __init clk_disable_unused(void)
if (ck->usecount > 0 || !ck->enable_reg)
continue;
- spin_lock_irqsave(&clockfw_lock, flags);
arch_clock->clk_disable_unused(ck);
- spin_unlock_irqrestore(&clockfw_lock, flags);
}
+ spin_unlock_irqrestore(&clockfw_lock, flags);
return 0;
}