[PATCH 12/35] davinci: clock framework: remove spinlock usage
Kevin Hilman
khilman at deeprootsystems.com
Mon Jan 11 10:27:01 EST 2010
Russell King - ARM Linux <linux at arm.linux.org.uk> writes:
> On Wed, Jan 06, 2010 at 10:31:54AM -0800, Kevin Hilman wrote:
>> From: Sekhar Nori <nsekhar at ti.com>
>>
>> Currently, the spinlock in DaVinci clock framework is being
>> used to:
>>
>> 1) Protect clock structure variables usecount and rate against
>> concurrent modification.
>>
>> 2) Protect against simultaneous PSC enables/disables ie.
>> serialize davinci_psc_config().
>>
>> 3) Serialize clk_set_rate():
>> i. Prevent simultaneous setting of clock rates
>> ii. Ensure clock list remains sane during rate
>> propagation (also in clk_set_parent).
>>
>> Remove the spinlock usage in clock framework by:
>>
>> 1) Making clock rate and usecount as atomic variables.
>>
>> 2) Making davinci_psc_config() protect itself instead of
>> relying on serialization by caller.
>>
>> 3) (i) Allowing the clk->set_rate to serialize itself. There
>> should be no need to serialize all clock rate settings.
>> Currently the only user of rate setting is cpufreq driver on
>> DA850. cpufreq naturally serializes the calls to set CPU rate.
>> Also, there appears no need to lock IRQs during CPU rate
>> transtitions. If required, IRQs can be locked in the actual
>> set_rate function.
>>
>> 3) (ii) Use the mutex already in place for serialzing clock list
>> manipulation for serializing clock rate propagation as well.
>>
>> Apart from the general benefit of reducing locking granurlarity,
>> the main motivation behind this change is to enable usage of
>> clock framework for off-chip clock synthesizers. One such
>> synthesizer, CDCE949, is present on DM6467 EVM. Access to the
>> synthesizer happens through I2C bus - accessing which can lead to
>> CPU sleep. Having IRQs locked in clk_set_rate prevents the
>> clk->set_rate function from sleeping.
>
> This patch is extremely bogus. Just because something is called 'atomic'
> does not make it so.
Russell,
Thanks for the feedback. I'll drop this patch while we rework this in
a different way.
Thanks,
Kevin
> atomic_set()..atomic_read() are NOT atomic. They are just plain write
> and read of the underlying atomic value - the former is intended for
> one-time initialization, and the latter is intended for debugging.
>
>> @@ -41,15 +40,16 @@ static void __clk_enable(struct clk *clk)
>> {
>> if (clk->parent)
>> __clk_enable(clk->parent);
>> - if (clk->usecount++ == 0 && (clk->flags & CLK_PSC))
>> + if (atomic_read(&clk->usecount) == 0 && (clk->flags & CLK_PSC))
>> davinci_psc_config(psc_domain(clk), clk->gpsc, clk->lpsc, 1);
>> + atomic_inc(&clk->usecount);
>
> This is wrong on two levels. Firstly, consider a simultaneous clk_enable
> and clk_disable.
>
> CPU0 CPU1
> __clk_disable
>
> atomic_dec_and_test(&clk->usecount) -> true
>
> __clk_enable
> atomic_read(&clk->usecount) returns zero
> davinci_psc_config(..., 1);
> atomic_inc(&clk->usecount);
> davinci_psc_config(..., 0);
>
> The result is that we now have the situation where the usecount is nonzero
> but the clock is disabled.
>
> Similar bogosities can arise when an already enabled clock tries to be
> simultaneously enabled for a second time and disabled.
>
> Secondly, davinci_psc_config() does a lot of read-modify-writing. With
> the spinlock gone, what's the protection against simultaneous r-m-w on
> these registers?
>
>> @@ -88,7 +80,7 @@ unsigned long clk_get_rate(struct clk *clk)
>> if (clk == NULL || IS_ERR(clk))
>> return -EINVAL;
>>
>> - return clk->rate;
>> + return atomic_read(&clk->rate);
>
> atomic here doesn't make clk->rate magically right. This is precisely
> equivalent to reading clk->rate directly.
>
>> int clk_set_rate(struct clk *clk, unsigned long rate)
>> {
>> - unsigned long flags;
>> int ret = -EINVAL;
>>
>> if (clk == NULL || IS_ERR(clk))
>> return ret;
>>
>> - spin_lock_irqsave(&clockfw_lock, flags);
>> if (clk->set_rate)
>> ret = clk->set_rate(clk, rate);
>> if (ret == 0) {
>> if (clk->recalc)
>> - clk->rate = clk->recalc(clk);
>> + atomic_set(&clk->rate, clk->recalc(clk));
>
> This atomic_set does nothing to protect you from races. You might as
> well be writing directly to clk->rate.
More information about the linux-arm-kernel
mailing list