]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ASoC: qcom: sdw: fix memory leak for sdw_stream_runtime
authorSrinivas Kandagatla <srinivas.kandagatla@oss.qualcomm.com>
Wed, 22 Oct 2025 14:33:46 +0000 (15:33 +0100)
committerMark Brown <broonie@kernel.org>
Wed, 29 Oct 2025 14:54:38 +0000 (14:54 +0000)
For some reason we endedup allocating sdw_stream_runtime for every cpu dai,
this has two issues.
1. we never set snd_soc_dai_set_stream for non soundwire dai, which
   means there is no way that we can free this, resulting in memory leak
2. startup and shutdown callbacks can be called without
   hw_params callback called. This combination results in memory leak
because machine driver sruntime array pointer is only set in hw_params
callback.

Fix this by
 1. adding a helper function to get sdw_runtime for substream
which can be used by shutdown callback to get hold of sruntime to free.
 2. only allocate sdw_runtime for soundwire dais.

Fixes: d32bac9cb09c ("ASoC: qcom: Add helper for allocating Soundwire stream runtime")
Cc: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Cc: Stable@vger.kernel.org
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@oss.qualcomm.com>
Tested-by: Steev Klimaszewski <threeway@gmail.com> # Thinkpad X13s
Link: https://patch.msgid.link/20251022143349.1081513-2-srinivas.kandagatla@oss.qualcomm.com
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/qcom/sc7280.c
sound/soc/qcom/sc8280xp.c
sound/soc/qcom/sdw.c
sound/soc/qcom/sdw.h
sound/soc/qcom/sm8250.c
sound/soc/qcom/x1e80100.c

index af412bd0c89ff5a475251b1f4371d14f8efe8063..c444dae563c75dbf662e9c3af9ecb0c963c247c5 100644 (file)
@@ -317,7 +317,7 @@ static void sc7280_snd_shutdown(struct snd_pcm_substream *substream)
        struct snd_soc_card *card = rtd->card;
        struct sc7280_snd_data *data = snd_soc_card_get_drvdata(card);
        struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
-       struct sdw_stream_runtime *sruntime = data->sruntime[cpu_dai->id];
+       struct sdw_stream_runtime *sruntime = qcom_snd_sdw_get_stream(substream);
 
        switch (cpu_dai->id) {
        case MI2S_PRIMARY:
index 187f37ffe3283713a2496e16ee58e91d58800f28..ed8b04c6022e9f6c277236294c8db32e0ec92b70 100644 (file)
@@ -73,7 +73,7 @@ static void sc8280xp_snd_shutdown(struct snd_pcm_substream *substream)
        struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
        struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
        struct sc8280xp_snd_data *pdata = snd_soc_card_get_drvdata(rtd->card);
-       struct sdw_stream_runtime *sruntime = pdata->sruntime[cpu_dai->id];
+       struct sdw_stream_runtime *sruntime = qcom_snd_sdw_get_stream(substream);
 
        pdata->sruntime[cpu_dai->id] = NULL;
        sdw_release_stream(sruntime);
index 7d7981d4295b482a8b8e0b65100af2e6bb716f7c..7b2cae92c812969c886e6b48d1193f47ef5f7069 100644 (file)
@@ -7,6 +7,37 @@
 #include <sound/soc.h>
 #include "sdw.h"
 
+static bool qcom_snd_is_sdw_dai(int id)
+{
+       switch (id) {
+       case WSA_CODEC_DMA_RX_0:
+       case WSA_CODEC_DMA_TX_0:
+       case WSA_CODEC_DMA_RX_1:
+       case WSA_CODEC_DMA_TX_1:
+       case WSA_CODEC_DMA_TX_2:
+       case RX_CODEC_DMA_RX_0:
+       case TX_CODEC_DMA_TX_0:
+       case RX_CODEC_DMA_RX_1:
+       case TX_CODEC_DMA_TX_1:
+       case RX_CODEC_DMA_RX_2:
+       case TX_CODEC_DMA_TX_2:
+       case RX_CODEC_DMA_RX_3:
+       case TX_CODEC_DMA_TX_3:
+       case RX_CODEC_DMA_RX_4:
+       case TX_CODEC_DMA_TX_4:
+       case RX_CODEC_DMA_RX_5:
+       case TX_CODEC_DMA_TX_5:
+       case RX_CODEC_DMA_RX_6:
+       case RX_CODEC_DMA_RX_7:
+       case SLIMBUS_0_RX...SLIMBUS_6_TX:
+               return true;
+       default:
+               break;
+       }
+
+       return false;
+}
+
 /**
  * qcom_snd_sdw_startup() - Helper to start Soundwire stream for SoC audio card
  * @substream: The PCM substream from audio, as passed to snd_soc_ops->startup()
@@ -29,6 +60,9 @@ int qcom_snd_sdw_startup(struct snd_pcm_substream *substream)
        u32 rx_ch_cnt = 0, tx_ch_cnt = 0;
        int ret, i, j;
 
+       if (!qcom_snd_is_sdw_dai(cpu_dai->id))
+               return 0;
+
        sruntime = sdw_alloc_stream(cpu_dai->name, SDW_STREAM_PCM);
        if (!sruntime)
                return -ENOMEM;
@@ -89,19 +123,8 @@ int qcom_snd_sdw_prepare(struct snd_pcm_substream *substream,
        if (!sruntime)
                return 0;
 
-       switch (cpu_dai->id) {
-       case WSA_CODEC_DMA_RX_0:
-       case WSA_CODEC_DMA_RX_1:
-       case RX_CODEC_DMA_RX_0:
-       case RX_CODEC_DMA_RX_1:
-       case TX_CODEC_DMA_TX_0:
-       case TX_CODEC_DMA_TX_1:
-       case TX_CODEC_DMA_TX_2:
-       case TX_CODEC_DMA_TX_3:
-               break;
-       default:
+       if (!qcom_snd_is_sdw_dai(cpu_dai->id))
                return 0;
-       }
 
        if (*stream_prepared)
                return 0;
@@ -129,9 +152,7 @@ int qcom_snd_sdw_prepare(struct snd_pcm_substream *substream,
 }
 EXPORT_SYMBOL_GPL(qcom_snd_sdw_prepare);
 
-int qcom_snd_sdw_hw_params(struct snd_pcm_substream *substream,
-                          struct snd_pcm_hw_params *params,
-                          struct sdw_stream_runtime **psruntime)
+struct sdw_stream_runtime *qcom_snd_sdw_get_stream(struct snd_pcm_substream *substream)
 {
        struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
        struct snd_soc_dai *codec_dai;
@@ -139,21 +160,23 @@ int qcom_snd_sdw_hw_params(struct snd_pcm_substream *substream,
        struct sdw_stream_runtime *sruntime;
        int i;
 
-       switch (cpu_dai->id) {
-       case WSA_CODEC_DMA_RX_0:
-       case RX_CODEC_DMA_RX_0:
-       case RX_CODEC_DMA_RX_1:
-       case TX_CODEC_DMA_TX_0:
-       case TX_CODEC_DMA_TX_1:
-       case TX_CODEC_DMA_TX_2:
-       case TX_CODEC_DMA_TX_3:
-               for_each_rtd_codec_dais(rtd, i, codec_dai) {
-                       sruntime = snd_soc_dai_get_stream(codec_dai, substream->stream);
-                       if (sruntime != ERR_PTR(-ENOTSUPP))
-                               *psruntime = sruntime;
-               }
-               break;
+       if (!qcom_snd_is_sdw_dai(cpu_dai->id))
+               return NULL;
+
+       for_each_rtd_codec_dais(rtd, i, codec_dai) {
+               sruntime = snd_soc_dai_get_stream(codec_dai, substream->stream);
+               if (sruntime != ERR_PTR(-ENOTSUPP))
+                       return sruntime;
        }
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(qcom_snd_sdw_get_stream);
+
+int qcom_snd_sdw_hw_params(struct snd_pcm_substream *substream,
+                          struct snd_pcm_hw_params *params,
+                          struct sdw_stream_runtime **psruntime)
+{
+       *psruntime = qcom_snd_sdw_get_stream(substream);
 
        return 0;
 
@@ -166,23 +189,13 @@ int qcom_snd_sdw_hw_free(struct snd_pcm_substream *substream,
        struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
        struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
 
-       switch (cpu_dai->id) {
-       case WSA_CODEC_DMA_RX_0:
-       case WSA_CODEC_DMA_RX_1:
-       case RX_CODEC_DMA_RX_0:
-       case RX_CODEC_DMA_RX_1:
-       case TX_CODEC_DMA_TX_0:
-       case TX_CODEC_DMA_TX_1:
-       case TX_CODEC_DMA_TX_2:
-       case TX_CODEC_DMA_TX_3:
-               if (sruntime && *stream_prepared) {
-                       sdw_disable_stream(sruntime);
-                       sdw_deprepare_stream(sruntime);
-                       *stream_prepared = false;
-               }
-               break;
-       default:
-               break;
+       if (!qcom_snd_is_sdw_dai(cpu_dai->id))
+               return 0;
+
+       if (sruntime && *stream_prepared) {
+               sdw_disable_stream(sruntime);
+               sdw_deprepare_stream(sruntime);
+               *stream_prepared = false;
        }
 
        return 0;
index 392e3455f1b198bb3cba61dfbd3b70eef60f85b2..b8bc5beb052230aceb8f0591e22df838ec54020a 100644 (file)
@@ -10,6 +10,7 @@ int qcom_snd_sdw_startup(struct snd_pcm_substream *substream);
 int qcom_snd_sdw_prepare(struct snd_pcm_substream *substream,
                         struct sdw_stream_runtime *runtime,
                         bool *stream_prepared);
+struct sdw_stream_runtime *qcom_snd_sdw_get_stream(struct snd_pcm_substream *stream);
 int qcom_snd_sdw_hw_params(struct snd_pcm_substream *substream,
                           struct snd_pcm_hw_params *params,
                           struct sdw_stream_runtime **psruntime);
index f5b75a06e5bd20e00874f4cd29d1b947ee89d79f..ce5b0059207f1ccc6b1297527abdffb92187adbf 100644 (file)
@@ -117,7 +117,7 @@ static void sm8250_snd_shutdown(struct snd_pcm_substream *substream)
        struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
        struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
        struct sm8250_snd_data *data = snd_soc_card_get_drvdata(rtd->card);
-       struct sdw_stream_runtime *sruntime = data->sruntime[cpu_dai->id];
+       struct sdw_stream_runtime *sruntime = qcom_snd_sdw_get_stream(substream);
 
        data->sruntime[cpu_dai->id] = NULL;
        sdw_release_stream(sruntime);
index 444f2162889f7d9d4b6b06bddc980d8a15dd988b..2e3599516aa2ae3c3ffb2ca1f82ca1e38b569250 100644 (file)
@@ -55,7 +55,7 @@ static void x1e80100_snd_shutdown(struct snd_pcm_substream *substream)
        struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
        struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
        struct x1e80100_snd_data *data = snd_soc_card_get_drvdata(rtd->card);
-       struct sdw_stream_runtime *sruntime = data->sruntime[cpu_dai->id];
+       struct sdw_stream_runtime *sruntime = qcom_snd_sdw_get_stream(substream);
 
        data->sruntime[cpu_dai->id] = NULL;
        sdw_release_stream(sruntime);