summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCezary Rojewski <cezary.rojewski@intel.com>2026-03-09 10:16:01 +0100
committerMark Brown <broonie@kernel.org>2026-03-11 13:34:09 +0000
commitb0b49c77bddac75db79f7c2c6ec0b07d61864f2f (patch)
tree525a93ad854a1b85152afa6fe4c0f5bcc0dd027c
parent1f318b96cc84d7c2ab792fcc0bfd42a7ca890681 (diff)
downloadlinux-b0b49c77bddac75db79f7c2c6ec0b07d61864f2f.tar.gz
linux-b0b49c77bddac75db79f7c2c6ec0b07d61864f2f.zip
ASoC: Intel: catpt: Synchronize stream access
Streams may have individual controls assigned to them e.g.: volume control in case of offload streams. If such a stream is running and simultaneously its controls are being manipulated, both processes are touching the exact same descriptors - access to these must be synchronized. Replace spinlock with mutex as IPCs are non-atomic operations and add proper locking for all ->stream_list users. Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com> Link: https://patch.msgid.link/20260309091605.896307-2-cezary.rojewski@intel.com Signed-off-by: Mark Brown <broonie@kernel.org>
-rw-r--r--sound/soc/intel/catpt/core.h2
-rw-r--r--sound/soc/intel/catpt/device.c2
-rw-r--r--sound/soc/intel/catpt/ipc.c3
-rw-r--r--sound/soc/intel/catpt/loader.c2
-rw-r--r--sound/soc/intel/catpt/pcm.c25
5 files changed, 22 insertions, 12 deletions
diff --git a/sound/soc/intel/catpt/core.h b/sound/soc/intel/catpt/core.h
index df8a5fd95e13..7e479ef89ad0 100644
--- a/sound/soc/intel/catpt/core.h
+++ b/sound/soc/intel/catpt/core.h
@@ -96,7 +96,7 @@ struct catpt_dev {
struct catpt_module_type modules[CATPT_MODULE_COUNT];
struct catpt_ssp_device_format devfmt[CATPT_SSP_COUNT];
struct list_head stream_list;
- spinlock_t list_lock;
+ struct mutex stream_mutex;
struct mutex clk_mutex;
struct catpt_dx_context dx_ctx;
diff --git a/sound/soc/intel/catpt/device.c b/sound/soc/intel/catpt/device.c
index 0638aecba40d..b5f4361d4465 100644
--- a/sound/soc/intel/catpt/device.c
+++ b/sound/soc/intel/catpt/device.c
@@ -226,7 +226,7 @@ static void catpt_dev_init(struct catpt_dev *cdev, struct device *dev,
cdev->spec = spec;
init_completion(&cdev->fw_ready);
INIT_LIST_HEAD(&cdev->stream_list);
- spin_lock_init(&cdev->list_lock);
+ mutex_init(&cdev->stream_mutex);
mutex_init(&cdev->clk_mutex);
/*
diff --git a/sound/soc/intel/catpt/ipc.c b/sound/soc/intel/catpt/ipc.c
index 5a01a9afb26e..2e3b7a5cbb9b 100644
--- a/sound/soc/intel/catpt/ipc.c
+++ b/sound/soc/intel/catpt/ipc.c
@@ -5,6 +5,7 @@
// Author: Cezary Rojewski <cezary.rojewski@intel.com>
//
+#include <linux/cleanup.h>
#include <linux/irqreturn.h>
#include "core.h"
#include "messages.h"
@@ -151,6 +152,8 @@ catpt_dsp_notify_stream(struct catpt_dev *cdev, union catpt_notify_msg msg)
struct catpt_notify_position pos;
struct catpt_notify_glitch glitch;
+ guard(mutex)(&cdev->stream_mutex);
+
stream = catpt_stream_find(cdev, msg.stream_hw_id);
if (!stream) {
dev_warn(cdev->dev, "notify %d for non-existent stream %d\n",
diff --git a/sound/soc/intel/catpt/loader.c b/sound/soc/intel/catpt/loader.c
index dc7afe587e6f..432cb1f0ab4e 100644
--- a/sound/soc/intel/catpt/loader.c
+++ b/sound/soc/intel/catpt/loader.c
@@ -90,6 +90,7 @@ int catpt_store_streams_context(struct catpt_dev *cdev, struct dma_chan *chan)
{
struct catpt_stream_runtime *stream;
+ /* Lockless as no streams can be added or removed during D3 -> D0 transition. */
list_for_each_entry(stream, &cdev->stream_list, node) {
u32 off, size;
int ret;
@@ -180,6 +181,7 @@ catpt_restore_streams_context(struct catpt_dev *cdev, struct dma_chan *chan)
{
struct catpt_stream_runtime *stream;
+ /* Lockless as no streams can be added or removed during D3 -> D0 transition. */
list_for_each_entry(stream, &cdev->stream_list, node) {
u32 off, size;
int ret;
diff --git a/sound/soc/intel/catpt/pcm.c b/sound/soc/intel/catpt/pcm.c
index 2c5ea4e0ff3d..fbe4821755bd 100644
--- a/sound/soc/intel/catpt/pcm.c
+++ b/sound/soc/intel/catpt/pcm.c
@@ -5,6 +5,7 @@
// Author: Cezary Rojewski <cezary.rojewski@intel.com>
//
+#include <linux/cleanup.h>
#include <linux/pm_runtime.h>
#include <sound/soc.h>
#include <sound/pcm_params.h>
@@ -97,12 +98,12 @@ catpt_get_stream_template(struct snd_pcm_substream *substream)
return catpt_topology[type];
}
+/* Caller responsible for holding ->stream_mutex. */
struct catpt_stream_runtime *
catpt_stream_find(struct catpt_dev *cdev, u8 stream_hw_id)
{
struct catpt_stream_runtime *pos, *result = NULL;
- spin_lock(&cdev->list_lock);
list_for_each_entry(pos, &cdev->stream_list, node) {
if (pos->info.stream_hw_id == stream_hw_id) {
result = pos;
@@ -110,7 +111,6 @@ catpt_stream_find(struct catpt_dev *cdev, u8 stream_hw_id)
}
}
- spin_unlock(&cdev->list_lock);
return result;
}
@@ -286,10 +286,6 @@ static int catpt_dai_startup(struct snd_pcm_substream *substream,
INIT_LIST_HEAD(&stream->node);
snd_soc_dai_set_dma_data(dai, substream, stream);
- spin_lock(&cdev->list_lock);
- list_add_tail(&stream->node, &cdev->stream_list);
- spin_unlock(&cdev->list_lock);
-
return 0;
err_request:
@@ -307,10 +303,6 @@ static void catpt_dai_shutdown(struct snd_pcm_substream *substream,
stream = snd_soc_dai_get_dma_data(dai, substream);
- spin_lock(&cdev->list_lock);
- list_del(&stream->node);
- spin_unlock(&cdev->list_lock);
-
release_resource(stream->persistent);
kfree(stream->persistent);
catpt_dsp_update_srampge(cdev, &cdev->dram, cdev->spec->dram_mask);
@@ -410,12 +402,15 @@ static int catpt_dai_hw_params(struct snd_pcm_substream *substream,
if (ret)
return CATPT_IPC_RET(ret);
+ guard(mutex)(&cdev->stream_mutex);
+
ret = catpt_dai_apply_usettings(dai, stream);
if (ret) {
catpt_ipc_free_stream(cdev, stream->info.stream_hw_id);
return ret;
}
+ list_add_tail(&stream->node, &cdev->stream_list);
stream->allocated = true;
return 0;
}
@@ -430,6 +425,10 @@ static int catpt_dai_hw_free(struct snd_pcm_substream *substream,
if (!stream->allocated)
return 0;
+ mutex_lock(&cdev->stream_mutex);
+ list_del(&stream->node);
+ mutex_unlock(&cdev->stream_mutex);
+
catpt_ipc_reset_stream(cdev, stream->info.stream_hw_id);
catpt_ipc_free_stream(cdev, stream->info.stream_hw_id);
@@ -910,6 +909,8 @@ static int catpt_stream_volume_get(struct snd_kcontrol *kcontrol,
int ret;
int i;
+ guard(mutex)(&cdev->stream_mutex);
+
stream = catpt_stream_find(cdev, pin_id);
if (!stream) {
for (i = 0; i < CATPT_CHANNELS_MAX; i++)
@@ -941,6 +942,8 @@ static int catpt_stream_volume_put(struct snd_kcontrol *kcontrol,
long *ctlvol = (long *)kcontrol->private_value;
int ret, i;
+ guard(mutex)(&cdev->stream_mutex);
+
stream = catpt_stream_find(cdev, pin_id);
if (!stream) {
for (i = 0; i < CATPT_CHANNELS_MAX; i++)
@@ -1017,6 +1020,8 @@ static int catpt_loopback_switch_put(struct snd_kcontrol *kcontrol,
bool mute;
int ret;
+ guard(mutex)(&cdev->stream_mutex);
+
mute = (bool)ucontrol->value.integer.value[0];
stream = catpt_stream_find(cdev, CATPT_PIN_ID_REFERENCE);
if (!stream) {