ALSA: pcm - Simplify snd_pcm_drain() implementation

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>
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 59e5fbe..561d6d9 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -1387,11 +1387,6 @@
 	.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 @@
 	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 @@
 	} 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 @@
 
 	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 @@
 
  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;