ALSA: pcm: Fix races among concurrent prepare and hw_params/hw_free calls
[sfrench/cifs-2.6.git] / sound / core / pcm_native.c
index 266895374b8343cd77976e7b3007f380bbfc0ad2..0e4fbf5fd87b40dd30123f8457d515dd1295df06 100644 (file)
@@ -1190,15 +1190,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);
@@ -1226,18 +1228,18 @@ static int snd_pcm_action_group(const struct action_ops *ops,
                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;
 }
@@ -1367,10 +1369,12 @@ static int snd_pcm_action_nonatomic(const struct action_ops *ops,
 
        /* 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;
 }