diff options
author | Peter Ujfalusi <peter.ujfalusi@linux.intel.com> | 2023-01-27 14:00:30 +0200 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2023-01-27 12:14:12 +0000 |
commit | f94f3915274d22d5cd8b253e0533822b934f5f41 (patch) | |
tree | be75eab49be989a4ededc1869035fa38eb3ee6a5 /sound/soc/sof/sof-audio.c | |
parent | 955a6f131a50c3685c560ef7b75880d272337b33 (diff) | |
download | lwn-f94f3915274d22d5cd8b253e0533822b934f5f41.tar.gz lwn-f94f3915274d22d5cd8b253e0533822b934f5f41.zip |
ASoC: SOF: Protect swidget->use_count with mutex for kcontrol access race
The use_count of the swidget is protect by ALSA core PCM locking with the
exception when an associated kcontrol is changed.
It has been observed that a rightly timed kcontrol access during stream
stop can result of an attempt to send a control update to a widget which
has been freed up between the check of the use_count and the message
sending.
We need to protect the entire sof_widget_setup() and sof_widget_free()
execution to make it safe to rely on the use_count.
Move the code under an _unlocked() function and use a mutex to protect
the execution of the functions for concurrency.
On the control path we need to use the lock only for the kcontrol access,
the widget_kcontrol_setup() op is called with the lock already held.
Reported-by: Guennadi Liakhovetski <guennadi.liakhovetski@linux.intel.com>
Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Reviewed-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Link: https://lore.kernel.org/r/20230127120031.10709-18-peter.ujfalusi@linux.intel.com
Signed-off-by: Mark Brown <broonie@kernel.org>
Diffstat (limited to 'sound/soc/sof/sof-audio.c')
-rw-r--r-- | sound/soc/sof/sof-audio.c | 36 |
1 files changed, 30 insertions, 6 deletions
diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 1e785e713bb2..d350fa4a3614 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -28,7 +28,8 @@ static void sof_reset_route_setup_status(struct snd_sof_dev *sdev, struct snd_so } } -int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) +static int sof_widget_free_unlocked(struct snd_sof_dev *sdev, + struct snd_sof_widget *swidget) { const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); struct snd_sof_widget *pipe_widget; @@ -70,7 +71,7 @@ int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) * skip for static pipelines */ if (swidget->dynamic_pipeline_widget && swidget->id != snd_soc_dapm_scheduler) { - ret = sof_widget_free(sdev, pipe_widget); + ret = sof_widget_free_unlocked(sdev, pipe_widget); if (ret < 0 && !err) err = ret; } @@ -84,9 +85,21 @@ int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) return err; } + +int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) +{ + int ret; + + mutex_lock(&swidget->setup_mutex); + ret = sof_widget_free_unlocked(sdev, swidget); + mutex_unlock(&swidget->setup_mutex); + + return ret; +} EXPORT_SYMBOL(sof_widget_free); -int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) +static int sof_widget_setup_unlocked(struct snd_sof_dev *sdev, + struct snd_sof_widget *swidget) { const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); bool use_count_decremented = false; @@ -116,7 +129,7 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) goto use_count_dec; } - ret = sof_widget_setup(sdev, swidget->spipe->pipe_widget); + ret = sof_widget_setup_unlocked(sdev, swidget->spipe->pipe_widget); if (ret < 0) goto use_count_dec; } @@ -160,19 +173,30 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) widget_free: /* widget use_count and core ref_count will both be decremented by sof_widget_free() */ - sof_widget_free(sdev, swidget); + sof_widget_free_unlocked(sdev, swidget); use_count_decremented = true; core_put: snd_sof_dsp_core_put(sdev, swidget->core); pipe_widget_free: if (swidget->id != snd_soc_dapm_scheduler) - sof_widget_free(sdev, swidget->spipe->pipe_widget); + sof_widget_free_unlocked(sdev, swidget->spipe->pipe_widget); use_count_dec: if (!use_count_decremented) swidget->use_count--; return ret; } + +int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) +{ + int ret; + + mutex_lock(&swidget->setup_mutex); + ret = sof_widget_setup_unlocked(sdev, swidget); + mutex_unlock(&swidget->setup_mutex); + + return ret; +} EXPORT_SYMBOL(sof_widget_setup); int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsource, |