[alsa-devel] Clean up of PCM spinlocks
Jaroslav Kysela
perex at perex.cz
Tue Mar 13 15:24:11 CET 2012
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
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)
So we use only one lock per stream.
Jaroslav
>
> Note that the group lock is identical with the substream's lock
> initially before the substream is linked with others. So, in the case
> of non-linked PCM, the new code behaves exactly same as before.
>
> Signed-off-by: Takashi Iwai <tiwai at suse.de>
> ---
> include/sound/pcm.h | 12 ++++++------
> sound/core/pcm_native.c | 36 ++++++++++++++----------------------
> 2 files changed, 20 insertions(+), 28 deletions(-)
>
> diff --git a/include/sound/pcm.h b/include/sound/pcm.h
> index 0cf91b2..d634dcc 100644
> --- a/include/sound/pcm.h
> +++ b/include/sound/pcm.h
> @@ -531,36 +531,36 @@ static inline int snd_pcm_stream_linked(struct snd_pcm_substream *substream)
> static inline void snd_pcm_stream_lock(struct snd_pcm_substream *substream)
> {
> read_lock(&snd_pcm_link_rwlock);
> - spin_lock(&substream->self_group.lock);
> + spin_lock(&substream->group->lock);
> }
>
> static inline void snd_pcm_stream_unlock(struct snd_pcm_substream *substream)
> {
> - spin_unlock(&substream->self_group.lock);
> + spin_unlock(&substream->group->lock);
> read_unlock(&snd_pcm_link_rwlock);
> }
>
> static inline void snd_pcm_stream_lock_irq(struct snd_pcm_substream *substream)
> {
> read_lock_irq(&snd_pcm_link_rwlock);
> - spin_lock(&substream->self_group.lock);
> + spin_lock(&substream->group->lock);
> }
>
> static inline void snd_pcm_stream_unlock_irq(struct snd_pcm_substream *substream)
> {
> - spin_unlock(&substream->self_group.lock);
> + spin_unlock(&substream->group->lock);
> read_unlock_irq(&snd_pcm_link_rwlock);
> }
>
> #define snd_pcm_stream_lock_irqsave(substream, flags) \
> do { \
> read_lock_irqsave(&snd_pcm_link_rwlock, (flags)); \
> - spin_lock(&substream->self_group.lock); \
> + spin_lock(&substream->group->lock); \
> } while (0)
>
> #define snd_pcm_stream_unlock_irqrestore(substream, flags) \
> do { \
> - spin_unlock(&substream->self_group.lock); \
> + spin_unlock(&substream->group->lock); \
> read_unlock_irqrestore(&snd_pcm_link_rwlock, (flags)); \
> } while (0)
>
> diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
> index 25ed9fe..c5f28ed 100644
> --- a/sound/core/pcm_native.c
> +++ b/sound/core/pcm_native.c
> @@ -712,7 +712,7 @@ static int snd_pcm_action_group(struct action_ops *ops,
> int res = 0;
>
> snd_pcm_group_for_each_entry(s, substream) {
> - if (do_lock && s != substream)
> + if (do_lock)
> spin_lock_nested(&s->self_group.lock,
> SINGLE_DEPTH_NESTING);
> res = ops->pre_action(s, state);
> @@ -740,8 +740,7 @@ static int snd_pcm_action_group(struct action_ops *ops,
> if (do_lock) {
> /* unlock streams */
> snd_pcm_group_for_each_entry(s1, substream) {
> - if (s1 != substream)
> - spin_unlock(&s1->self_group.lock);
> + spin_unlock(&s1->self_group.lock);
> if (s1 == s) /* end */
> break;
> }
> @@ -779,13 +778,7 @@ static int snd_pcm_action(struct action_ops *ops,
> int res;
>
> if (snd_pcm_stream_linked(substream)) {
> - if (!spin_trylock(&substream->group->lock)) {
> - spin_unlock(&substream->self_group.lock);
> - spin_lock(&substream->group->lock);
> - spin_lock(&substream->self_group.lock);
> - }
> res = snd_pcm_action_group(ops, substream, state, 1);
> - spin_unlock(&substream->group->lock);
> } else {
> res = snd_pcm_action_single(ops, substream, state);
> }
> @@ -801,19 +794,13 @@ static int snd_pcm_action_lock_irq(struct action_ops *ops,
> {
> int res;
>
> - read_lock_irq(&snd_pcm_link_rwlock);
> + snd_pcm_stream_lock_irq(substream);
> if (snd_pcm_stream_linked(substream)) {
> - spin_lock(&substream->group->lock);
> - spin_lock(&substream->self_group.lock);
> res = snd_pcm_action_group(ops, substream, state, 1);
> - spin_unlock(&substream->self_group.lock);
> - spin_unlock(&substream->group->lock);
> } else {
> - spin_lock(&substream->self_group.lock);
> res = snd_pcm_action_single(ops, substream, state);
> - spin_unlock(&substream->self_group.lock);
> }
> - read_unlock_irq(&snd_pcm_link_rwlock);
> + snd_pcm_stream_unlock_irq(substream);
> return res;
> }
>
> @@ -1586,12 +1573,18 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
> struct file *file;
> struct snd_pcm_file *pcm_file;
> struct snd_pcm_substream *substream1;
> + struct snd_pcm_group *group;
>
> file = snd_pcm_file_fd(fd);
> if (!file)
> return -EBADFD;
> pcm_file = file->private_data;
> substream1 = pcm_file->substream;
> + group = kmalloc(sizeof(*group), GFP_KERNEL);
> + if (!group) {
> + res = -ENOMEM;
> + goto _nolock;
> + }
> down_write(&snd_pcm_link_rwsem);
> write_lock_irq(&snd_pcm_link_rwlock);
> if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN ||
> @@ -1604,11 +1597,7 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
> goto _end;
> }
> if (!snd_pcm_stream_linked(substream)) {
> - substream->group = kmalloc(sizeof(struct snd_pcm_group), GFP_ATOMIC);
> - if (substream->group == NULL) {
> - res = -ENOMEM;
> - goto _end;
> - }
> + substream->group = group;
> spin_lock_init(&substream->group->lock);
> INIT_LIST_HEAD(&substream->group->substreams);
> list_add_tail(&substream->link_list, &substream->group->substreams);
> @@ -1620,7 +1609,10 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
> _end:
> write_unlock_irq(&snd_pcm_link_rwlock);
> up_write(&snd_pcm_link_rwsem);
> + _nolock:
> fput(file);
> + if (res < 0)
> + kfree(group);
> return res;
> }
>
--
Jaroslav Kysela <perex at perex.cz>
Linux Kernel Sound Maintainer
ALSA Project; Red Hat, Inc.
More information about the Alsa-devel
mailing list