Commit 9b0573c0 authored by Takashi Iwai's avatar Takashi Iwai

ALSA: PCM: Fix some races at disconnection

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: default avatarMatthieu CASTET <matthieu.castet@parrot.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 1693849f
...@@ -1086,11 +1086,15 @@ static int snd_pcm_dev_disconnect(struct snd_device *device) ...@@ -1086,11 +1086,15 @@ static int snd_pcm_dev_disconnect(struct snd_device *device)
if (list_empty(&pcm->list)) if (list_empty(&pcm->list))
goto unlock; goto unlock;
mutex_lock(&pcm->open_mutex);
list_del_init(&pcm->list); list_del_init(&pcm->list);
for (cidx = 0; cidx < 2; cidx++) 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) if (substream->runtime)
substream->runtime->status->state = SNDRV_PCM_STATE_DISCONNECTED; substream->runtime->status->state = SNDRV_PCM_STATE_DISCONNECTED;
snd_pcm_stream_unlock_irq(substream);
}
list_for_each_entry(notify, &snd_pcm_notify_list, list) { list_for_each_entry(notify, &snd_pcm_notify_list, list) {
notify->n_disconnect(pcm); notify->n_disconnect(pcm);
} }
...@@ -1110,6 +1114,7 @@ static int snd_pcm_dev_disconnect(struct snd_device *device) ...@@ -1110,6 +1114,7 @@ static int snd_pcm_dev_disconnect(struct snd_device *device)
pcm->streams[cidx].chmap_kctl = NULL; pcm->streams[cidx].chmap_kctl = NULL;
} }
} }
mutex_unlock(&pcm->open_mutex);
unlock: unlock:
mutex_unlock(&register_mutex); mutex_unlock(&register_mutex);
return 0; return 0;
......
...@@ -369,6 +369,14 @@ static int period_to_usecs(struct snd_pcm_runtime *runtime) ...@@ -369,6 +369,14 @@ static int period_to_usecs(struct snd_pcm_runtime *runtime)
return usecs; 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, static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params) struct snd_pcm_hw_params *params)
{ {
...@@ -452,7 +460,7 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream, ...@@ -452,7 +460,7 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
runtime->boundary *= 2; runtime->boundary *= 2;
snd_pcm_timer_resolution_change(substream); 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)) if (pm_qos_request_active(&substream->latency_pm_qos_req))
pm_qos_remove_request(&substream->latency_pm_qos_req); pm_qos_remove_request(&substream->latency_pm_qos_req);
...@@ -464,7 +472,7 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream, ...@@ -464,7 +472,7 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
/* hardware might be unusable from this time, /* hardware might be unusable from this time,
so we force application to retry to set so we force application to retry to set
the correct hardware parameter settings */ 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) if (substream->ops->hw_free != NULL)
substream->ops->hw_free(substream); substream->ops->hw_free(substream);
return err; return err;
...@@ -512,7 +520,7 @@ static int snd_pcm_hw_free(struct snd_pcm_substream *substream) ...@@ -512,7 +520,7 @@ static int snd_pcm_hw_free(struct snd_pcm_substream *substream)
return -EBADFD; return -EBADFD;
if (substream->ops->hw_free) if (substream->ops->hw_free)
result = substream->ops->hw_free(substream); 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); pm_qos_remove_request(&substream->latency_pm_qos_req);
return result; return result;
} }
...@@ -1320,7 +1328,7 @@ static void snd_pcm_post_prepare(struct snd_pcm_substream *substream, int state) ...@@ -1320,7 +1328,7 @@ static void snd_pcm_post_prepare(struct snd_pcm_substream *substream, int state)
{ {
struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_runtime *runtime = substream->runtime;
runtime->control->appl_ptr = runtime->status->hw_ptr; 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 = { static struct action_ops snd_pcm_action_prepare = {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment