]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
ASoC: qcom: sdw: fix memory leak for sdw_stream_runtime
authorSrinivas Kandagatla <srinivas.kandagatla@oss.qualcomm.com>
Mon, 5 Jan 2026 15:10:08 +0000 (10:10 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 8 Jan 2026 09:15:04 +0000 (10:15 +0100)
[ Upstream commit bcba17279327c6e85dee6a97014dc642e2dc93cc ]

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>
Signed-off-by: Sasha Levin <sashal@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.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 230af8d7b205d418e6d278b4222278a3e6c0c595..bc12aa5064ceaaf647c2cc66baaffb0f7c0efacf 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 916a156f991fa7a44fccf04562fd2ef1f0f2c338..1c482a012920a1c419ba5704a9048c8cdab9ae8c 100644 (file)
@@ -69,7 +69,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 875da4adb1d7b4efdee47baa4fa8a3c1305e6c04..52e9ad24b5eeb075faf8170eca3685ec1c2ff2e2 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()
@@ -27,6 +58,9 @@ int qcom_snd_sdw_startup(struct snd_pcm_substream *substream)
        struct snd_soc_dai *codec_dai;
        int ret, i;
 
+       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;
@@ -61,19 +95,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;
@@ -101,9 +124,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;
@@ -111,21 +132,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;
 
@@ -138,23 +161,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 1001fd32138037a8fd569f3ab046b0ddd0e0b3c6..ff1ef5363983f047dddddf34656e6b4ff8acc8d4 100644 (file)
@@ -86,7 +86,7 @@ static void sm2450_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 898b5c26bf1ee6bfa6a7a296f90257c6b31851fb..3645fafcd45a5ed0fdbe953b3ff9ab656625c41c 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);