summaryrefslogtreecommitdiff
path: root/sound/soc/sof/ipc4-control.c
diff options
context:
space:
mode:
authorPeter Ujfalusi <peter.ujfalusi@linux.intel.com>2023-01-27 15:00:30 +0300
committerMark Brown <broonie@kernel.org>2023-01-27 15:14:12 +0300
commitf94f3915274d22d5cd8b253e0533822b934f5f41 (patch)
treebe75eab49be989a4ededc1869035fa38eb3ee6a5 /sound/soc/sof/ipc4-control.c
parent955a6f131a50c3685c560ef7b75880d272337b33 (diff)
downloadlinux-f94f3915274d22d5cd8b253e0533822b934f5f41.tar.xz
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/ipc4-control.c')
-rw-r--r--sound/soc/sof/ipc4-control.c33
1 files changed, 23 insertions, 10 deletions
diff --git a/sound/soc/sof/ipc4-control.c b/sound/soc/sof/ipc4-control.c
index 0d5a578c3496..67bd2233fd9a 100644
--- a/sound/soc/sof/ipc4-control.c
+++ b/sound/soc/sof/ipc4-control.c
@@ -12,7 +12,8 @@
#include "ipc4-priv.h"
#include "ipc4-topology.h"
-static int sof_ipc4_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool set)
+static int sof_ipc4_set_get_kcontrol_data(struct snd_sof_control *scontrol,
+ bool set, bool lock)
{
struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
struct snd_soc_component *scomp = scontrol->scomp;
@@ -21,6 +22,7 @@ static int sof_ipc4_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool
struct sof_ipc4_msg *msg = &cdata->msg;
struct snd_sof_widget *swidget;
bool widget_found = false;
+ int ret = 0;
/* find widget associated with the control */
list_for_each_entry(swidget, &sdev->widget_list, list) {
@@ -35,23 +37,34 @@ static int sof_ipc4_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool
return -ENOENT;
}
+ if (lock)
+ mutex_lock(&swidget->setup_mutex);
+ else
+ lockdep_assert_held(&swidget->setup_mutex);
+
/*
- * Volatile controls should always be part of static pipelines and the widget use_count
- * would always be > 0 in this case. For the others, just return the cached value if the
- * widget is not set up.
+ * Volatile controls should always be part of static pipelines and the
+ * widget use_count would always be > 0 in this case. For the others,
+ * just return the cached value if the widget is not set up.
*/
if (!swidget->use_count)
- return 0;
+ goto unlock;
msg->primary &= ~SOF_IPC4_MOD_INSTANCE_MASK;
msg->primary |= SOF_IPC4_MOD_INSTANCE(swidget->instance_id);
- return iops->set_get_data(sdev, msg, msg->data_size, set);
+ ret = iops->set_get_data(sdev, msg, msg->data_size, set);
+
+unlock:
+ if (lock)
+ mutex_unlock(&swidget->setup_mutex);
+
+ return ret;
}
static int
sof_ipc4_set_volume_data(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget,
- struct snd_sof_control *scontrol)
+ struct snd_sof_control *scontrol, bool lock)
{
struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
struct sof_ipc4_gain *gain = swidget->private;
@@ -90,7 +103,7 @@ sof_ipc4_set_volume_data(struct snd_sof_dev *sdev, struct snd_sof_widget *swidge
msg->data_ptr = &data;
msg->data_size = sizeof(data);
- ret = sof_ipc4_set_get_kcontrol_data(scontrol, true);
+ ret = sof_ipc4_set_get_kcontrol_data(scontrol, true, lock);
msg->data_ptr = NULL;
msg->data_size = 0;
if (ret < 0) {
@@ -145,7 +158,7 @@ static bool sof_ipc4_volume_put(struct snd_sof_control *scontrol,
return false;
}
- ret = sof_ipc4_set_volume_data(sdev, swidget, scontrol);
+ ret = sof_ipc4_set_volume_data(sdev, swidget, scontrol, true);
if (ret < 0)
return false;
@@ -175,7 +188,7 @@ static int sof_ipc4_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_s
list_for_each_entry(scontrol, &sdev->kcontrol_list, list)
if (scontrol->comp_id == swidget->comp_id) {
- ret = sof_ipc4_set_volume_data(sdev, swidget, scontrol);
+ ret = sof_ipc4_set_volume_data(sdev, swidget, scontrol, false);
if (ret < 0) {
dev_err(sdev->dev, "%s: kcontrol %d set up failed for widget %s\n",
__func__, scontrol->comp_id, swidget->widget->name);