ALSA: pcm - Simplify snd_pcm_drain() implementation
authorTakashi Iwai <tiwai@suse.de>
Thu, 17 Sep 2009 16:46:26 +0000 (18:46 +0200)
committerTakashi Iwai <tiwai@suse.de>
Mon, 21 Sep 2009 13:13:09 +0000 (15:13 +0200)
Simplify snd_pcm_drain() implementation and avoid unneeded array-
allocation for waitqueues.  Instead, one waitqueue is used for the
first draining stream, and wait until all streams finished.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/core/pcm_native.c

index 59e5fbe6af51eeb2edf002adff4761ea07387ba7..561d6d95a2d33fef48b98ccfc29c32571cb9dddd 100644 (file)
@@ -1387,11 +1387,6 @@ static struct action_ops snd_pcm_action_drain_init = {
        .post_action = snd_pcm_post_drain_init
 };
 
-struct drain_rec {
-       struct snd_pcm_substream *substream;
-       wait_queue_t wait;
-};
-
 static int snd_pcm_drop(struct snd_pcm_substream *substream);
 
 /*
@@ -1407,10 +1402,9 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream,
        struct snd_card *card;
        struct snd_pcm_runtime *runtime;
        struct snd_pcm_substream *s;
+       wait_queue_t wait;
        int result = 0;
-       int i, num_drecs;
        int nonblock = 0;
-       struct drain_rec *drec, drec_tmp, *d;
 
        card = substream->pcm->card;
        runtime = substream->runtime;
@@ -1433,38 +1427,10 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream,
        } else if (substream->f_flags & O_NONBLOCK)
                nonblock = 1;
 
-       if (nonblock)
-               goto lock; /* no need to allocate waitqueues */
-
-       /* allocate temporary record for drain sync */
        down_read(&snd_pcm_link_rwsem);
-       if (snd_pcm_stream_linked(substream)) {
-               drec = kmalloc(substream->group->count * sizeof(*drec), GFP_KERNEL);
-               if (! drec) {
-                       up_read(&snd_pcm_link_rwsem);
-                       snd_power_unlock(card);
-                       return -ENOMEM;
-               }
-       } else
-               drec = &drec_tmp;
-
-       /* count only playback streams */
-       num_drecs = 0;
-       snd_pcm_group_for_each_entry(s, substream) {
-               runtime = s->runtime;
-               if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-                       d = &drec[num_drecs++];
-                       d->substream = s;
-                       init_waitqueue_entry(&d->wait, current);
-                       add_wait_queue(&runtime->sleep, &d->wait);
-               }
-       }
-       up_read(&snd_pcm_link_rwsem);
-
- lock:
        snd_pcm_stream_lock_irq(substream);
        /* resume pause */
-       if (substream->runtime->status->state == SNDRV_PCM_STATE_PAUSED)
+       if (runtime->status->state == SNDRV_PCM_STATE_PAUSED)
                snd_pcm_pause(substream, 0);
 
        /* pre-start/stop - all running streams are changed to DRAINING state */
@@ -1479,25 +1445,35 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream,
 
        for (;;) {
                long tout;
+               struct snd_pcm_runtime *to_check;
                if (signal_pending(current)) {
                        result = -ERESTARTSYS;
                        break;
                }
-               /* all finished? */
-               for (i = 0; i < num_drecs; i++) {
-                       runtime = drec[i].substream->runtime;
-                       if (runtime->status->state == SNDRV_PCM_STATE_DRAINING)
+               /* find a substream to drain */
+               to_check = NULL;
+               snd_pcm_group_for_each_entry(s, substream) {
+                       if (s->stream != SNDRV_PCM_STREAM_PLAYBACK)
+                               continue;
+                       runtime = s->runtime;
+                       if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) {
+                               to_check = runtime;
                                break;
+                       }
                }
-               if (i == num_drecs)
-                       break; /* yes, all drained */
-
+               if (!to_check)
+                       break; /* all drained */
+               init_waitqueue_entry(&wait, current);
+               add_wait_queue(&to_check->sleep, &wait);
                set_current_state(TASK_INTERRUPTIBLE);
                snd_pcm_stream_unlock_irq(substream);
+               up_read(&snd_pcm_link_rwsem);
                snd_power_unlock(card);
                tout = schedule_timeout(10 * HZ);
                snd_power_lock(card);
+               down_read(&snd_pcm_link_rwsem);
                snd_pcm_stream_lock_irq(substream);
+               remove_wait_queue(&to_check->sleep, &wait);
                if (tout == 0) {
                        if (substream->runtime->status->state == SNDRV_PCM_STATE_SUSPENDED)
                                result = -ESTRPIPE;
@@ -1512,16 +1488,7 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream,
 
  unlock:
        snd_pcm_stream_unlock_irq(substream);
-
-       if (!nonblock) {
-               for (i = 0; i < num_drecs; i++) {
-                       d = &drec[i];
-                       runtime = d->substream->runtime;
-                       remove_wait_queue(&runtime->sleep, &d->wait);
-               }
-               if (drec != &drec_tmp)
-                       kfree(drec);
-       }
+       up_read(&snd_pcm_link_rwsem);
        snd_power_unlock(card);
 
        return result;