mirror of
https://github.com/torvalds/linux.git
synced 2024-12-28 13:51:44 +00:00
[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>
This commit is contained in:
parent
ce43fbaece
commit
21cb2a2ec5
@ -1368,43 +1368,32 @@ static int snd_pcm_drain(snd_pcm_substream_t *substream)
|
|||||||
if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
|
if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
|
||||||
return -EBADFD;
|
return -EBADFD;
|
||||||
|
|
||||||
down_read(&snd_pcm_link_rwsem);
|
|
||||||
snd_power_lock(card);
|
snd_power_lock(card);
|
||||||
if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
|
if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
|
||||||
result = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile);
|
result = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile);
|
||||||
if (result < 0)
|
if (result < 0) {
|
||||||
goto _unlock;
|
snd_power_unlock(card);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* allocate temporary record for drain sync */
|
/* allocate temporary record for drain sync */
|
||||||
|
down_read(&snd_pcm_link_rwsem);
|
||||||
if (snd_pcm_stream_linked(substream)) {
|
if (snd_pcm_stream_linked(substream)) {
|
||||||
drec = kmalloc(substream->group->count * sizeof(*drec), GFP_KERNEL);
|
drec = kmalloc(substream->group->count * sizeof(*drec), GFP_KERNEL);
|
||||||
if (! drec) {
|
if (! drec) {
|
||||||
result = -ENOMEM;
|
up_read(&snd_pcm_link_rwsem);
|
||||||
goto _unlock;
|
snd_power_unlock(card);
|
||||||
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
drec = &drec_tmp;
|
drec = &drec_tmp;
|
||||||
|
|
||||||
snd_pcm_stream_lock_irq(substream);
|
/* count only playback streams */
|
||||||
/* 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 */
|
|
||||||
num_drecs = 0;
|
num_drecs = 0;
|
||||||
snd_pcm_group_for_each(pos, substream) {
|
snd_pcm_group_for_each(pos, substream) {
|
||||||
snd_pcm_substream_t *s = snd_pcm_group_substream_entry(pos);
|
snd_pcm_substream_t *s = snd_pcm_group_substream_entry(pos);
|
||||||
runtime = s->runtime;
|
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) {
|
if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||||
d = &drec[num_drecs++];
|
d = &drec[num_drecs++];
|
||||||
d->substream = s;
|
d->substream = s;
|
||||||
@ -1418,9 +1407,21 @@ static int snd_pcm_drain(snd_pcm_substream_t *substream)
|
|||||||
runtime->stop_threshold = runtime->buffer_size;
|
runtime->stop_threshold = runtime->buffer_size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
up_read(&snd_pcm_link_rwsem);
|
||||||
if (! num_drecs)
|
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 (;;) {
|
for (;;) {
|
||||||
long tout;
|
long tout;
|
||||||
@ -1428,6 +1429,15 @@ static int snd_pcm_drain(snd_pcm_substream_t *substream)
|
|||||||
result = -ERESTARTSYS;
|
result = -ERESTARTSYS;
|
||||||
break;
|
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);
|
set_current_state(TASK_INTERRUPTIBLE);
|
||||||
snd_pcm_stream_unlock_irq(substream);
|
snd_pcm_stream_unlock_irq(substream);
|
||||||
snd_power_unlock(card);
|
snd_power_unlock(card);
|
||||||
@ -1444,15 +1454,11 @@ static int snd_pcm_drain(snd_pcm_substream_t *substream)
|
|||||||
}
|
}
|
||||||
break;
|
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++) {
|
for (i = 0; i < num_drecs; i++) {
|
||||||
d = &drec[i];
|
d = &drec[i];
|
||||||
runtime = d->substream->runtime;
|
runtime = d->substream->runtime;
|
||||||
@ -1460,13 +1466,9 @@ static int snd_pcm_drain(snd_pcm_substream_t *substream)
|
|||||||
runtime->stop_threshold = d->stop_threshold;
|
runtime->stop_threshold = d->stop_threshold;
|
||||||
}
|
}
|
||||||
|
|
||||||
_end:
|
|
||||||
snd_pcm_stream_unlock_irq(substream);
|
|
||||||
if (drec != &drec_tmp)
|
if (drec != &drec_tmp)
|
||||||
kfree(drec);
|
kfree(drec);
|
||||||
_unlock:
|
|
||||||
snd_power_unlock(card);
|
snd_power_unlock(card);
|
||||||
up_read(&snd_pcm_link_rwsem);
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user