[alsa-devel] Clean up of PCM spinlocks

Jaroslav Kysela perex at perex.cz
Tue Mar 13 16:46:39 CET 2012


Date 13.3.2012 16:23, Takashi Iwai wrote:
> At Tue, 13 Mar 2012 16:16:36 +0100,
> Jaroslav Kysela wrote:
>>
>> Date 13.3.2012 16:00, Takashi Iwai wrote:
>>> At Tue, 13 Mar 2012 15:24:11 +0100,
>>> Jaroslav Kysela wrote:
>>>>
>>>> Date 13.3.2012 11:57, Takashi Iwai wrote:
>>>>> Hi,
>>>>>
>>>>> below is a patch I posted to LKML as the follow up for the lockdep
>>>>> report from Dave Jones.  It's a resurrected patch from what I worked
>>>>> on quite ago.  Although this fix is suboptimal, I see no better way.
>>>>>
>>>>> If this looks OK, I'm going to queue it as a 3.4 material.
>>>>>
>>>>>
>>>>> thanks,
>>>>>
>>>>> Takashi
>>>>>
>>>>> ===
>>>>>
>>>>> From: Takashi Iwai <tiwai at suse.de>
>>>>> Subject: [PATCH] ALSA: pcm - Simplify linked PCM substream spinlocks
>>>>>
>>>>> The spinlocks held for PCM substreams have been always messy.  For the
>>>>> better concurrent accessibility, we took always the substream's lock
>>>>> first.  When substreams are linked, we need a group-wide lock, but
>>>>> this should be applied outside substream's lock.  So, we unlock the
>>>>> substream's lock first, then do locks twice.
>>>>>
>>>>> This scheme is known to be problematic with lockdep.  Maybe because
>>>>> the nested lock isn't marked properly, and partly because the lock and
>>>>> unlock sequences are different (A/B -> A/B).
>>>>>
>>>>> This patch tries to simplify the scheme.  Instead of holding the
>>>>> substream's lock, we take the group lock always at first.  A drawback
>>>>> of this is that the access to the individual substream in a same group
>>>>> won't be allowed any longer.  But, the code looks much easier, and
>>>>> likely less buggy.
>>>>
>>>> What do you mean by this? I noticed that you switched the behaviour for
>>>> the stream_lock():
>>>>
>>>> 1) self_group spinlock is used for single (non-linked) stream
>>>>    - same as before
>>>> 2) group.lock spinlock is used for the linked streams
>>>>    - the self_group spinlock is used only in the group action
>>>>      - the question is why? it seems useless, the
>>>>        stream_lock() functions use the allocated group.lock
>>>
>>> Oh yeah, right, this is utterly useless!
>>> I just thought of the mutex case, but in this case, the lock is
>>> avoided anyway.
>>>
>>>> I don't think that this patch will work in the way we expect.
>>>>
>>>> I believe we should use only group.lock spinlock for the linked streams
>>>> without using the self_group spinlock to make locking work like this:
>>>>
>>>> stream_lock(current)
>>>> group_action(current) {
>>>>   for s from all_group_substreams {
>>>>     if (s != current)
>>>>       stream_lock(s);
>>>>   }
>>>>   .... job ....
>>>>   .... group substreams unlock ....
>>>> }
>>>> stream_unlock(current)
>>>
>>> Well, if we think of a single lock, the code would be much
>>> simplified than above.
>>>
>>> The reviewsed patch is below.  I left the single-stream version as is
>>> since it's used explicitly in the drain() -- the drain action is
>>> applied to each substream although it's linked.
>>
>> I meant use group->lock in the group_action, see patch bellow.
> 
> Hmm, I don't think this works.  From linked stream members,
> group->lock points to the very same lock instance.  So,
> snd_pcm_action_group() would dead-lock the same lock with your code.

I see. The problem is that the concurrent but linked streams can take
different first locks for the group locking:

time 0		 time 1
--------------------------------
lock stream 0 -> group lock
lock stream 1 -> group lock

In this case the condition current != s is not enough. I think that we
have to strictly separate situations when the group lock can be
activated and use different locking scheme there - for example using a
master lock per linked substreams.

It seems to me that your proposed patch just removes the spinlocks for
linked substreams which can cause inconsistencies for these streams.

Or just a quick idea - what about to track the the group action
collisions? We can use a spinlock protected counter and serialize these
requests in another way (if counter is above one - the CPU should wait
until it becomes one).

>>> Also, the changes in snd_pcm_link() to use GFP_KERNEL is splitted out
>>> of this patch.
>>
>> Maybe, we can reuse the self_group container for the linked streams, too?
>> In this case, the dynamic allocation of new group structure can be removed.
> 
> A substream can be unlinked at any time, thus it can't belong to
> the substream instance.  The point of substream->group is to detach
> the instance from the substream.  self_group is just an optimization
> for unlinked streams.

I see.

> The GFP_KERNEL thing is a thing of optimization, too: whether to call
> kmalloc() superfluously in the case of errors or not.  As seen in the
> patch below, it's no big matter...

I agree.

					Jaroslav

-- 
Jaroslav Kysela <perex at perex.cz>
Linux Kernel Sound Maintainer
ALSA Project; Red Hat, Inc.


More information about the Alsa-devel mailing list