Skip to content

Commit 9b0573c

Browse files
committed
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: Matthieu CASTET <[email protected]> Cc: <[email protected]> Signed-off-by: Takashi Iwai <[email protected]>
1 parent 1693849 commit 9b0573c

File tree

2 files changed

+18
-5
lines changed

2 files changed

+18
-5
lines changed

sound/core/pcm.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1086,11 +1086,15 @@ static int snd_pcm_dev_disconnect(struct snd_device *device)
10861086
if (list_empty(&pcm->list))
10871087
goto unlock;
10881088

1089+
mutex_lock(&pcm->open_mutex);
10891090
list_del_init(&pcm->list);
10901091
for (cidx = 0; cidx < 2; cidx++)
1091-
for (substream = pcm->streams[cidx].substream; substream; substream = substream->next)
1092+
for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) {
1093+
snd_pcm_stream_lock_irq(substream);
10921094
if (substream->runtime)
10931095
substream->runtime->status->state = SNDRV_PCM_STATE_DISCONNECTED;
1096+
snd_pcm_stream_unlock_irq(substream);
1097+
}
10941098
list_for_each_entry(notify, &snd_pcm_notify_list, list) {
10951099
notify->n_disconnect(pcm);
10961100
}
@@ -1110,6 +1114,7 @@ static int snd_pcm_dev_disconnect(struct snd_device *device)
11101114
pcm->streams[cidx].chmap_kctl = NULL;
11111115
}
11121116
}
1117+
mutex_unlock(&pcm->open_mutex);
11131118
unlock:
11141119
mutex_unlock(&register_mutex);
11151120
return 0;

sound/core/pcm_native.c

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,14 @@ static int period_to_usecs(struct snd_pcm_runtime *runtime)
369369
return usecs;
370370
}
371371

372+
static void snd_pcm_set_state(struct snd_pcm_substream *substream, int state)
373+
{
374+
snd_pcm_stream_lock_irq(substream);
375+
if (substream->runtime->status->state != SNDRV_PCM_STATE_DISCONNECTED)
376+
substream->runtime->status->state = state;
377+
snd_pcm_stream_unlock_irq(substream);
378+
}
379+
372380
static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
373381
struct snd_pcm_hw_params *params)
374382
{
@@ -452,7 +460,7 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
452460
runtime->boundary *= 2;
453461

454462
snd_pcm_timer_resolution_change(substream);
455-
runtime->status->state = SNDRV_PCM_STATE_SETUP;
463+
snd_pcm_set_state(substream, SNDRV_PCM_STATE_SETUP);
456464

457465
if (pm_qos_request_active(&substream->latency_pm_qos_req))
458466
pm_qos_remove_request(&substream->latency_pm_qos_req);
@@ -464,7 +472,7 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
464472
/* hardware might be unusable from this time,
465473
so we force application to retry to set
466474
the correct hardware parameter settings */
467-
runtime->status->state = SNDRV_PCM_STATE_OPEN;
475+
snd_pcm_set_state(substream, SNDRV_PCM_STATE_OPEN);
468476
if (substream->ops->hw_free != NULL)
469477
substream->ops->hw_free(substream);
470478
return err;
@@ -512,7 +520,7 @@ static int snd_pcm_hw_free(struct snd_pcm_substream *substream)
512520
return -EBADFD;
513521
if (substream->ops->hw_free)
514522
result = substream->ops->hw_free(substream);
515-
runtime->status->state = SNDRV_PCM_STATE_OPEN;
523+
snd_pcm_set_state(substream, SNDRV_PCM_STATE_OPEN);
516524
pm_qos_remove_request(&substream->latency_pm_qos_req);
517525
return result;
518526
}
@@ -1320,7 +1328,7 @@ static void snd_pcm_post_prepare(struct snd_pcm_substream *substream, int state)
13201328
{
13211329
struct snd_pcm_runtime *runtime = substream->runtime;
13221330
runtime->control->appl_ptr = runtime->status->hw_ptr;
1323-
runtime->status->state = SNDRV_PCM_STATE_PREPARED;
1331+
snd_pcm_set_state(substream, SNDRV_PCM_STATE_PREPARED);
13241332
}
13251333

13261334
static struct action_ops snd_pcm_action_prepare = {

0 commit comments

Comments
 (0)