ARM: OMAP: clockdomain: Fix locking on _clkdm_clk_hwmod_enable / disable
authorTero Kristo <t-kristo@ti.com>
Tue, 25 Sep 2012 16:05:32 +0000 (19:05 +0300)
committerGrazvydas Ignotas <notasas@gmail.com>
Mon, 14 Apr 2014 20:09:24 +0000 (23:09 +0300)
Previously the code only acquired spinlock after increasing / decreasing
the usecount value, which is wrong. This leaves a small window where
a task switch may occur between the check of the usecount and the actual
wakeup / sleep of the domain. Fixed by moving the spinlock locking before
the usecount access. Left the usecount as atomic_t if someone wants an
easy access to the parameter through atomic_read.

Signed-off-by: Tero Kristo <t-kristo@ti.com>
Acked-by: Paul Walmsley <paul@pwsan.com>
Signed-off-by: Tony Lindgren <tony@atomide.com>
arch/arm/mach-omap2/clockdomain.c

index 527378f..7153a7d 100644 (file)
@@ -915,15 +915,18 @@ static int _clkdm_clk_hwmod_enable(struct clockdomain *clkdm)
        if (!clkdm || !arch_clkdm || !arch_clkdm->clkdm_clk_enable)
                return -EINVAL;
 
+       spin_lock_irqsave(&clkdm->lock, flags);
+
        /*
         * For arch's with no autodeps, clkcm_clk_enable
         * should be called for every clock instance or hwmod that is
         * enabled, so the clkdm can be force woken up.
         */
-       if ((atomic_inc_return(&clkdm->usecount) > 1) && autodeps)
+       if ((atomic_inc_return(&clkdm->usecount) > 1) && autodeps) {
+               spin_unlock_irqrestore(&clkdm->lock, flags);
                return 0;
+       }
 
-       spin_lock_irqsave(&clkdm->lock, flags);
        arch_clkdm->clkdm_clk_enable(clkdm);
        pwrdm_wait_transition(clkdm->pwrdm.ptr);
        pwrdm_clkdm_state_switch(clkdm);
@@ -941,15 +944,19 @@ static int _clkdm_clk_hwmod_disable(struct clockdomain *clkdm)
        if (!clkdm || !arch_clkdm || !arch_clkdm->clkdm_clk_disable)
                return -EINVAL;
 
+       spin_lock_irqsave(&clkdm->lock, flags);
+
        if (atomic_read(&clkdm->usecount) == 0) {
+               spin_unlock_irqrestore(&clkdm->lock, flags);
                WARN_ON(1); /* underflow */
                return -ERANGE;
        }
 
-       if (atomic_dec_return(&clkdm->usecount) > 0)
+       if (atomic_dec_return(&clkdm->usecount) > 0) {
+               spin_unlock_irqrestore(&clkdm->lock, flags);
                return 0;
+       }
 
-       spin_lock_irqsave(&clkdm->lock, flags);
        arch_clkdm->clkdm_clk_disable(clkdm);
        pwrdm_clkdm_state_switch(clkdm);
        spin_unlock_irqrestore(&clkdm->lock, flags);