| From 3c3201f8c7bb77eb53b08a3ca8d9a4ddc500b4c0 Mon Sep 17 00:00:00 2001 |
| From: Takashi Iwai <tiwai@suse.de> |
| Date: Tue, 22 Mar 2022 18:07:19 +0100 |
| Subject: ALSA: pcm: Fix races among concurrent prepare and hw_params/hw_free calls |
| |
| From: Takashi Iwai <tiwai@suse.de> |
| |
| commit 3c3201f8c7bb77eb53b08a3ca8d9a4ddc500b4c0 upstream. |
| |
| Like the previous fixes to hw_params and hw_free ioctl races, we need |
| to paper over the concurrent prepare ioctl calls against hw_params and |
| hw_free, too. |
| |
| This patch implements the locking with the existing |
| runtime->buffer_mutex for prepare ioctls. Unlike the previous case |
| for snd_pcm_hw_hw_params() and snd_pcm_hw_free(), snd_pcm_prepare() is |
| performed to the linked streams, hence the lock can't be applied |
| simply on the top. For tracking the lock in each linked substream, we |
| modify snd_pcm_action_group() slightly and apply the buffer_mutex for |
| the case stream_lock=false (formerly there was no lock applied) |
| there. |
| |
| Cc: <stable@vger.kernel.org> |
| Reviewed-by: Jaroslav Kysela <perex@perex.cz> |
| Link: https://lore.kernel.org/r/20220322170720.3529-4-tiwai@suse.de |
| Signed-off-by: Takashi Iwai <tiwai@suse.de> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| sound/core/pcm_native.c | 32 ++++++++++++++++++-------------- |
| 1 file changed, 18 insertions(+), 14 deletions(-) |
| |
| --- a/sound/core/pcm_native.c |
| +++ b/sound/core/pcm_native.c |
| @@ -1177,15 +1177,17 @@ struct action_ops { |
| static int snd_pcm_action_group(const struct action_ops *ops, |
| struct snd_pcm_substream *substream, |
| snd_pcm_state_t state, |
| - bool do_lock) |
| + bool stream_lock) |
| { |
| struct snd_pcm_substream *s = NULL; |
| struct snd_pcm_substream *s1; |
| int res = 0, depth = 1; |
| |
| snd_pcm_group_for_each_entry(s, substream) { |
| - if (do_lock && s != substream) { |
| - if (s->pcm->nonatomic) |
| + if (s != substream) { |
| + if (!stream_lock) |
| + mutex_lock_nested(&s->runtime->buffer_mutex, depth); |
| + else if (s->pcm->nonatomic) |
| mutex_lock_nested(&s->self_group.mutex, depth); |
| else |
| spin_lock_nested(&s->self_group.lock, depth); |
| @@ -1213,18 +1215,18 @@ static int snd_pcm_action_group(const st |
| ops->post_action(s, state); |
| } |
| _unlock: |
| - if (do_lock) { |
| - /* unlock streams */ |
| - snd_pcm_group_for_each_entry(s1, substream) { |
| - if (s1 != substream) { |
| - if (s1->pcm->nonatomic) |
| - mutex_unlock(&s1->self_group.mutex); |
| - else |
| - spin_unlock(&s1->self_group.lock); |
| - } |
| - if (s1 == s) /* end */ |
| - break; |
| + /* unlock streams */ |
| + snd_pcm_group_for_each_entry(s1, substream) { |
| + if (s1 != substream) { |
| + if (!stream_lock) |
| + mutex_unlock(&s1->runtime->buffer_mutex); |
| + else if (s1->pcm->nonatomic) |
| + mutex_unlock(&s1->self_group.mutex); |
| + else |
| + spin_unlock(&s1->self_group.lock); |
| } |
| + if (s1 == s) /* end */ |
| + break; |
| } |
| return res; |
| } |
| @@ -1354,10 +1356,12 @@ static int snd_pcm_action_nonatomic(cons |
| |
| /* Guarantee the group members won't change during non-atomic action */ |
| down_read(&snd_pcm_link_rwsem); |
| + mutex_lock(&substream->runtime->buffer_mutex); |
| if (snd_pcm_stream_linked(substream)) |
| res = snd_pcm_action_group(ops, substream, state, false); |
| else |
| res = snd_pcm_action_single(ops, substream, state); |
| + mutex_unlock(&substream->runtime->buffer_mutex); |
| up_read(&snd_pcm_link_rwsem); |
| return res; |
| } |