diff options
author | Takashi Iwai <tiwai@suse.de> | 2012-11-07 12:39:51 +0100 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-11-17 13:14:22 -0800 |
commit | 61bc2855cf2e28f7ef3928c04453daa07dfffd9b (patch) | |
tree | 279016d1e07b24d3681285315cef2f3acf0d8ce7 /sound | |
parent | ec1f5bf6b3a9f42284371d41a7c998c05c49abcb (diff) |
ALSA: PCM: Fix some races at disconnection
commit 9b0573c07f278e9888c352aa9724035c75784ea0 upstream.
Fix races at PCM disconnection:
- while a PCM device is being opened or closed
- while the PCM state is being changed without lock in prepare,
hw_params, hw_free ops
Reported-by: Matthieu CASTET <matthieu.castet@parrot.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'sound')
-rw-r--r-- | sound/core/pcm.c | 7 | ||||
-rw-r--r-- | sound/core/pcm_native.c | 16 |
2 files changed, 18 insertions, 5 deletions
diff --git a/sound/core/pcm.c b/sound/core/pcm.c index ee9abb2d9001..1d2561234fa3 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -1045,11 +1045,15 @@ static int snd_pcm_dev_disconnect(struct snd_device *device) if (list_empty(&pcm->list)) goto unlock; + mutex_lock(&pcm->open_mutex); list_del_init(&pcm->list); for (cidx = 0; cidx < 2; cidx++) - for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) + for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) { + snd_pcm_stream_lock_irq(substream); if (substream->runtime) substream->runtime->status->state = SNDRV_PCM_STATE_DISCONNECTED; + snd_pcm_stream_unlock_irq(substream); + } list_for_each_entry(notify, &snd_pcm_notify_list, list) { notify->n_disconnect(pcm); } @@ -1065,6 +1069,7 @@ static int snd_pcm_dev_disconnect(struct snd_device *device) } snd_unregister_device(devtype, pcm->card, pcm->device); } + mutex_unlock(&pcm->open_mutex); unlock: mutex_unlock(®ister_mutex); return 0; diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 1c6be91dfb98..a6380787b7f9 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -368,6 +368,14 @@ static int period_to_usecs(struct snd_pcm_runtime *runtime) return usecs; } +static void snd_pcm_set_state(struct snd_pcm_substream *substream, int state) +{ + snd_pcm_stream_lock_irq(substream); + if (substream->runtime->status->state != SNDRV_PCM_STATE_DISCONNECTED) + substream->runtime->status->state = state; + snd_pcm_stream_unlock_irq(substream); +} + static int snd_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -451,7 +459,7 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream, runtime->boundary *= 2; snd_pcm_timer_resolution_change(substream); - runtime->status->state = SNDRV_PCM_STATE_SETUP; + snd_pcm_set_state(substream, SNDRV_PCM_STATE_SETUP); if (pm_qos_request_active(&substream->latency_pm_qos_req)) pm_qos_remove_request(&substream->latency_pm_qos_req); @@ -463,7 +471,7 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream, /* hardware might be unusable from this time, so we force application to retry to set the correct hardware parameter settings */ - runtime->status->state = SNDRV_PCM_STATE_OPEN; + snd_pcm_set_state(substream, SNDRV_PCM_STATE_OPEN); if (substream->ops->hw_free != NULL) substream->ops->hw_free(substream); return err; @@ -511,7 +519,7 @@ static int snd_pcm_hw_free(struct snd_pcm_substream *substream) return -EBADFD; if (substream->ops->hw_free) result = substream->ops->hw_free(substream); - runtime->status->state = SNDRV_PCM_STATE_OPEN; + snd_pcm_set_state(substream, SNDRV_PCM_STATE_OPEN); pm_qos_remove_request(&substream->latency_pm_qos_req); return result; } @@ -1319,7 +1327,7 @@ static void snd_pcm_post_prepare(struct snd_pcm_substream *substream, int state) { struct snd_pcm_runtime *runtime = substream->runtime; runtime->control->appl_ptr = runtime->status->hw_ptr; - runtime->status->state = SNDRV_PCM_STATE_PREPARED; + snd_pcm_set_state(substream, SNDRV_PCM_STATE_PREPARED); } static struct action_ops snd_pcm_action_prepare = { |