Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+6 −1
Original line number Diff line number Diff line
@@ -1086,11 +1086,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);
	}
@@ -1110,6 +1114,7 @@ static int snd_pcm_dev_disconnect(struct snd_device *device)
			pcm->streams[cidx].chmap_kctl = NULL;
		}
	}
	mutex_unlock(&pcm->open_mutex);
 unlock:
	mutex_unlock(&register_mutex);
	return 0;
+12 −4
Original line number Diff line number Diff line
@@ -369,6 +369,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)
{
@@ -452,7 +460,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);
@@ -464,7 +472,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;
@@ -512,7 +520,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;
}
@@ -1320,7 +1328,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 = {