aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2012-11-07 12:39:51 +0100
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-11-17 13:14:22 -0800
commit61bc2855cf2e28f7ef3928c04453daa07dfffd9b (patch)
tree279016d1e07b24d3681285315cef2f3acf0d8ce7
parentec1f5bf6b3a9f42284371d41a7c998c05c49abcb (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>
-rw-r--r--sound/core/pcm.c7
-rw-r--r--sound/core/pcm_native.c16
2 files changed, 18 insertions, 5 deletions
diff --git a/sound/core/pcm.c b/sound/core/pcm.c
index ee9abb2d900..1d2561234fa 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(&register_mutex);
return 0;
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 1c6be91dfb9..a6380787b7f 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 = {