summaryrefslogtreecommitdiffstats
path: root/sound/core/pcm_native.c
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2005-05-31 14:35:31 +0200
committerJaroslav Kysela <perex@suse.cz>2005-06-22 12:27:53 +0200
commit21cb2a2ec5818cbba01bcb7f24388670322c77f9 (patch)
treeabd491e1b613f200970359546185619228d3ec02 /sound/core/pcm_native.c
parentce43fbaececc82196d321671159483b3287de128 (diff)
[ALSA] Fix races between PCM drain and other ops
PCM Midlevel Fix semaphore races between PCM drain and other ops. Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/core/pcm_native.c')
-rw-r--r--sound/core/pcm_native.c70
1 files changed, 36 insertions, 34 deletions
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 4e582415a08..10c2c983264 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -1368,43 +1368,32 @@ static int snd_pcm_drain(snd_pcm_substream_t *substream)
if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
return -EBADFD;
- down_read(&snd_pcm_link_rwsem);
snd_power_lock(card);
if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
result = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile);
- if (result < 0)
- goto _unlock;
+ if (result < 0) {
+ snd_power_unlock(card);
+ return result;
+ }
}
/* 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) {
- result = -ENOMEM;
- goto _unlock;
+ up_read(&snd_pcm_link_rwsem);
+ snd_power_unlock(card);
+ return -ENOMEM;
}
} else
drec = &drec_tmp;
- snd_pcm_stream_lock_irq(substream);
- /* resume pause */
- if (runtime->status->state == SNDRV_PCM_STATE_PAUSED)
- snd_pcm_pause(substream, 0);
-
- /* pre-start/stop - all running streams are changed to DRAINING state */
- result = snd_pcm_action(&snd_pcm_action_drain_init, substream, 0);
- if (result < 0)
- goto _end;
-
- /* check streams with PLAYBACK & DRAINING */
+ /* count only playback streams */
num_drecs = 0;
snd_pcm_group_for_each(pos, substream) {
snd_pcm_substream_t *s = snd_pcm_group_substream_entry(pos);
runtime = s->runtime;
- if (runtime->status->state != SNDRV_PCM_STATE_DRAINING) {
- runtime->status->state = SNDRV_PCM_STATE_SETUP;
- continue;
- }
if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) {
d = &drec[num_drecs++];
d->substream = s;
@@ -1418,9 +1407,21 @@ static int snd_pcm_drain(snd_pcm_substream_t *substream)
runtime->stop_threshold = runtime->buffer_size;
}
}
-
+ up_read(&snd_pcm_link_rwsem);
if (! num_drecs)
- goto _end;
+ goto _error;
+
+ snd_pcm_stream_lock_irq(substream);
+ /* resume pause */
+ if (runtime->status->state == SNDRV_PCM_STATE_PAUSED)
+ snd_pcm_pause(substream, 0);
+
+ /* pre-start/stop - all running streams are changed to DRAINING state */
+ result = snd_pcm_action(&snd_pcm_action_drain_init, substream, 0);
+ if (result < 0) {
+ snd_pcm_stream_unlock_irq(substream);
+ goto _error;
+ }
for (;;) {
long tout;
@@ -1428,6 +1429,15 @@ static int snd_pcm_drain(snd_pcm_substream_t *substream)
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)
+ break;
+ }
+ if (i == num_drecs)
+ break; /* yes, all drained */
+
set_current_state(TASK_INTERRUPTIBLE);
snd_pcm_stream_unlock_irq(substream);
snd_power_unlock(card);
@@ -1444,15 +1454,11 @@ static int snd_pcm_drain(snd_pcm_substream_t *substream)
}
break;
}
- /* all finished? */
- for (i = 0; i < num_drecs; i++) {
- runtime = drec[i].substream->runtime;
- if (runtime->status->state == SNDRV_PCM_STATE_DRAINING)
- break;
- }
- if (i == num_drecs)
- break;
}
+
+ snd_pcm_stream_unlock_irq(substream);
+
+ _error:
for (i = 0; i < num_drecs; i++) {
d = &drec[i];
runtime = d->substream->runtime;
@@ -1460,13 +1466,9 @@ static int snd_pcm_drain(snd_pcm_substream_t *substream)
runtime->stop_threshold = d->stop_threshold;
}
- _end:
- snd_pcm_stream_unlock_irq(substream);
if (drec != &drec_tmp)
kfree(drec);
- _unlock:
snd_power_unlock(card);
- up_read(&snd_pcm_link_rwsem);
return result;
}