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>
This commit is contained in:
Cezary Rojewski 2026-03-09 10:16:01 +01:00 committed by Mark Brown
parent 1f318b96cc
commit b0b49c77bd
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0
5 changed files with 22 additions and 12 deletions

View File

@ -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;

View File

@ -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);
/*

View File

@ -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",

View File

@ -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;

View File

@ -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) {