--- /dev/null
+From 3fe9f5882cf71573516749b0bb687ef88f470d1d Mon Sep 17 00:00:00 2001
+From: Benjamin Bara <benjamin.bara@skidata.com>
+Date: Tue, 8 Oct 2024 13:36:14 +0200
+Subject: ASoC: dapm: avoid container_of() to get component
+
+From: Benjamin Bara <benjamin.bara@skidata.com>
+
+commit 3fe9f5882cf71573516749b0bb687ef88f470d1d upstream.
+
+The current implementation does not work for widgets of DAPMs without
+component, as snd_soc_dapm_to_component() requires it. If the widget is
+directly owned by the card, e.g. as it is the case for the tegra
+implementation, the call leads to UB. Therefore directly access the
+component of the widget's DAPM to be able to check if a component is
+available.
+
+Fixes: f82eb06a40c8 ("ASoC: tegra: machine: Handle component name prefix")
+Cc: stable@vger.kernel.org # v6.7+
+Signed-off-by: Benjamin Bara <benjamin.bara@skidata.com>
+Link: https://patch.msgid.link/20241008-tegra-dapm-v2-1-5e999cb5f0e7@skidata.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ sound/soc/soc-dapm.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/sound/soc/soc-dapm.c
++++ b/sound/soc/soc-dapm.c
+@@ -2786,10 +2786,10 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_update_da
+
+ int snd_soc_dapm_widget_name_cmp(struct snd_soc_dapm_widget *widget, const char *s)
+ {
+- struct snd_soc_component *component = snd_soc_dapm_to_component(widget->dapm);
++ struct snd_soc_component *component = widget->dapm->component;
+ const char *wname = widget->name;
+
+- if (component->name_prefix)
++ if (component && component->name_prefix)
+ wname += strlen(component->name_prefix) + 1; /* plus space */
+
+ return strcmp(wname, s);
--- /dev/null
+From 49da1463c9e3d2082276c3e0e2a8b65a88711cd2 Mon Sep 17 00:00:00 2001
+From: Zichen Xie <zichenxie0106@gmail.com>
+Date: Sun, 6 Oct 2024 15:57:37 -0500
+Subject: ASoC: qcom: Fix NULL Dereference in asoc_qcom_lpass_cpu_platform_probe()
+
+From: Zichen Xie <zichenxie0106@gmail.com>
+
+commit 49da1463c9e3d2082276c3e0e2a8b65a88711cd2 upstream.
+
+A devm_kzalloc() in asoc_qcom_lpass_cpu_platform_probe() could
+possibly return NULL pointer. NULL Pointer Dereference may be
+triggerred without addtional check.
+Add a NULL check for the returned pointer.
+
+Fixes: b5022a36d28f ("ASoC: qcom: lpass: Use regmap_field for i2sctl and dmactl registers")
+Cc: stable@vger.kernel.org
+Signed-off-by: Zichen Xie <zichenxie0106@gmail.com>
+Link: https://patch.msgid.link/20241006205737.8829-1-zichenxie0106@gmail.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ sound/soc/qcom/lpass-cpu.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/sound/soc/qcom/lpass-cpu.c
++++ b/sound/soc/qcom/lpass-cpu.c
+@@ -1242,6 +1242,8 @@ int asoc_qcom_lpass_cpu_platform_probe(s
+ /* Allocation for i2sctl regmap fields */
+ drvdata->i2sctl = devm_kzalloc(&pdev->dev, sizeof(struct lpaif_i2sctl),
+ GFP_KERNEL);
++ if (!drvdata->i2sctl)
++ return -ENOMEM;
+
+ /* Initialize bitfields for dai I2SCTL register */
+ ret = lpass_cpu_init_i2sctl_bitfields(dev, drvdata->i2sctl,
--- /dev/null
+From db7e59e6a39a4d3d54ca8197c796557e6d480b0d Mon Sep 17 00:00:00 2001
+From: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+Date: Sat, 12 Oct 2024 12:11:08 +0200
+Subject: ASoC: qcom: sc7280: Fix missing Soundwire runtime stream alloc
+
+From: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+
+commit db7e59e6a39a4d3d54ca8197c796557e6d480b0d upstream.
+
+Commit 15c7fab0e047 ("ASoC: qcom: Move Soundwire runtime stream alloc to
+soundcards") moved the allocation of Soundwire stream runtime from the
+Qualcomm Soundwire driver to each individual machine sound card driver,
+except that it forgot to update SC7280 card.
+
+Just like for other Qualcomm sound cards using Soundwire, the card
+driver should allocate and release the runtime. Otherwise sound
+playback will result in a NULL pointer dereference or other effect of
+uninitialized memory accesses (which was confirmed on SDM845 having
+similar issue).
+
+Cc: stable@vger.kernel.org
+Cc: Alexey Klimov <alexey.klimov@linaro.org>
+Cc: Steev Klimaszewski <steev@kali.org>
+Fixes: 15c7fab0e047 ("ASoC: qcom: Move Soundwire runtime stream alloc to soundcards")
+Link: https://lore.kernel.org/r/20241010054109.16938-1-krzysztof.kozlowski@linaro.org
+Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+Link: https://patch.msgid.link/20241012101108.129476-1-krzysztof.kozlowski@linaro.org
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ sound/soc/qcom/Kconfig | 1 +
+ sound/soc/qcom/sc7280.c | 10 +++++++++-
+ 2 files changed, 10 insertions(+), 1 deletion(-)
+
+--- a/sound/soc/qcom/Kconfig
++++ b/sound/soc/qcom/Kconfig
+@@ -208,6 +208,7 @@ config SND_SOC_SC7280
+ tristate "SoC Machine driver for SC7280 boards"
+ depends on I2C && SOUNDWIRE
+ select SND_SOC_QCOM_COMMON
++ select SND_SOC_QCOM_SDW
+ select SND_SOC_LPASS_SC7280
+ select SND_SOC_MAX98357A
+ select SND_SOC_WCD938X_SDW
+--- a/sound/soc/qcom/sc7280.c
++++ b/sound/soc/qcom/sc7280.c
+@@ -23,6 +23,7 @@
+ #include "common.h"
+ #include "lpass.h"
+ #include "qdsp6/q6afe.h"
++#include "sdw.h"
+
+ #define DEFAULT_MCLK_RATE 19200000
+ #define RT5682_PLL_FREQ (48000 * 512)
+@@ -316,6 +317,7 @@ static void sc7280_snd_shutdown(struct s
+ 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];
+
+ switch (cpu_dai->id) {
+ case MI2S_PRIMARY:
+@@ -333,6 +335,9 @@ static void sc7280_snd_shutdown(struct s
+ default:
+ break;
+ }
++
++ data->sruntime[cpu_dai->id] = NULL;
++ sdw_release_stream(sruntime);
+ }
+
+ static int sc7280_snd_startup(struct snd_pcm_substream *substream)
+@@ -347,6 +352,8 @@ static int sc7280_snd_startup(struct snd
+ switch (cpu_dai->id) {
+ case MI2S_PRIMARY:
+ ret = sc7280_rt5682_init(rtd);
++ if (ret)
++ return ret;
+ break;
+ case SECONDARY_MI2S_RX:
+ codec_dai_fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_I2S;
+@@ -360,7 +367,8 @@ static int sc7280_snd_startup(struct snd
+ default:
+ break;
+ }
+- return ret;
++
++ return qcom_snd_sdw_startup(substream);
+ }
+
+ static const struct snd_soc_ops sc7280_ops = {
--- /dev/null
+From d0e806b0cc6260b59c65e606034a63145169c04c Mon Sep 17 00:00:00 2001
+From: Alexey Klimov <alexey.klimov@linaro.org>
+Date: Wed, 9 Oct 2024 22:39:22 +0100
+Subject: ASoC: qcom: sdm845: add missing soundwire runtime stream alloc
+
+From: Alexey Klimov <alexey.klimov@linaro.org>
+
+commit d0e806b0cc6260b59c65e606034a63145169c04c upstream.
+
+During the migration of Soundwire runtime stream allocation from
+the Qualcomm Soundwire controller to SoC's soundcard drivers the sdm845
+soundcard was forgotten.
+
+At this point any playback attempt or audio daemon startup, for instance
+on sdm845-db845c (Qualcomm RB3 board), will result in stream pointer
+NULL dereference:
+
+ Unable to handle kernel NULL pointer dereference at virtual
+ address 0000000000000020
+ Mem abort info:
+ ESR = 0x0000000096000004
+ EC = 0x25: DABT (current EL), IL = 32 bits
+ SET = 0, FnV = 0
+ EA = 0, S1PTW = 0
+ FSC = 0x04: level 0 translation fault
+ Data abort info:
+ ISV = 0, ISS = 0x00000004, ISS2 = 0x00000000
+ CM = 0, WnR = 0, TnD = 0, TagAccess = 0
+ GCS = 0, Overlay = 0, DirtyBit = 0, Xs = 0
+ user pgtable: 4k pages, 48-bit VAs, pgdp=0000000101ecf000
+ [0000000000000020] pgd=0000000000000000, p4d=0000000000000000
+ Internal error: Oops: 0000000096000004 [#1] PREEMPT SMP
+ Modules linked in: ...
+ CPU: 5 UID: 0 PID: 1198 Comm: aplay
+ Not tainted 6.12.0-rc2-qcomlt-arm64-00059-g9d78f315a362-dirty #18
+ Hardware name: Thundercomm Dragonboard 845c (DT)
+ pstate: 60400005 (nZCv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--)
+ pc : sdw_stream_add_slave+0x44/0x380 [soundwire_bus]
+ lr : sdw_stream_add_slave+0x44/0x380 [soundwire_bus]
+ sp : ffff80008a2035c0
+ x29: ffff80008a2035c0 x28: ffff80008a203978 x27: 0000000000000000
+ x26: 00000000000000c0 x25: 0000000000000000 x24: ffff1676025f4800
+ x23: ffff167600ff1cb8 x22: ffff167600ff1c98 x21: 0000000000000003
+ x20: ffff167607316000 x19: ffff167604e64e80 x18: 0000000000000000
+ x17: 0000000000000000 x16: ffffcec265074160 x15: 0000000000000000
+ x14: 0000000000000000 x13: 0000000000000000 x12: 0000000000000000
+ x11: 0000000000000000 x10: 0000000000000000 x9 : 0000000000000000
+ x8 : 0000000000000000 x7 : 0000000000000000 x6 : ffff167600ff1cec
+ x5 : ffffcec22cfa2010 x4 : 0000000000000000 x3 : 0000000000000003
+ x2 : ffff167613f836c0 x1 : 0000000000000000 x0 : ffff16761feb60b8
+ Call trace:
+ sdw_stream_add_slave+0x44/0x380 [soundwire_bus]
+ wsa881x_hw_params+0x68/0x80 [snd_soc_wsa881x]
+ snd_soc_dai_hw_params+0x3c/0xa4
+ __soc_pcm_hw_params+0x230/0x660
+ dpcm_be_dai_hw_params+0x1d0/0x3f8
+ dpcm_fe_dai_hw_params+0x98/0x268
+ snd_pcm_hw_params+0x124/0x460
+ snd_pcm_common_ioctl+0x998/0x16e8
+ snd_pcm_ioctl+0x34/0x58
+ __arm64_sys_ioctl+0xac/0xf8
+ invoke_syscall+0x48/0x104
+ el0_svc_common.constprop.0+0x40/0xe0
+ do_el0_svc+0x1c/0x28
+ el0_svc+0x34/0xe0
+ el0t_64_sync_handler+0x120/0x12c
+ el0t_64_sync+0x190/0x194
+ Code: aa0403fb f9418400 9100e000 9400102f (f8420f22)
+ ---[ end trace 0000000000000000 ]---
+
+0000000000006108 <sdw_stream_add_slave>:
+ 6108: d503233f paciasp
+ 610c: a9b97bfd stp x29, x30, [sp, #-112]!
+ 6110: 910003fd mov x29, sp
+ 6114: a90153f3 stp x19, x20, [sp, #16]
+ 6118: a9025bf5 stp x21, x22, [sp, #32]
+ 611c: aa0103f6 mov x22, x1
+ 6120: 2a0303f5 mov w21, w3
+ 6124: a90363f7 stp x23, x24, [sp, #48]
+ 6128: aa0003f8 mov x24, x0
+ 612c: aa0203f7 mov x23, x2
+ 6130: a9046bf9 stp x25, x26, [sp, #64]
+ 6134: aa0403f9 mov x25, x4 <-- x4 copied to x25
+ 6138: a90573fb stp x27, x28, [sp, #80]
+ 613c: aa0403fb mov x27, x4
+ 6140: f9418400 ldr x0, [x0, #776]
+ 6144: 9100e000 add x0, x0, #0x38
+ 6148: 94000000 bl 0 <mutex_lock>
+ 614c: f8420f22 ldr x2, [x25, #32]! <-- offset 0x44
+ ^^^
+This is 0x6108 + offset 0x44 from the beginning of sdw_stream_add_slave()
+where data abort happens.
+wsa881x_hw_params() is called with stream = NULL and passes it further
+in register x4 (5th argument) to sdw_stream_add_slave() without any checks.
+Value from x4 is copied to x25 and finally it aborts on trying to load
+a value from address in x25 plus offset 32 (in dec) which corresponds
+to master_list member in struct sdw_stream_runtime:
+
+struct sdw_stream_runtime {
+ const char * name; /* 0 8 */
+ struct sdw_stream_params params; /* 8 12 */
+ enum sdw_stream_state state; /* 20 4 */
+ enum sdw_stream_type type; /* 24 4 */
+ /* XXX 4 bytes hole, try to pack */
+ here-> struct list_head master_list; /* 32 16 */
+ int m_rt_count; /* 48 4 */
+ /* size: 56, cachelines: 1, members: 6 */
+ /* sum members: 48, holes: 1, sum holes: 4 */
+ /* padding: 4 */
+ /* last cacheline: 56 bytes */
+
+Fix this by adding required calls to qcom_snd_sdw_startup() and
+sdw_release_stream() to startup and shutdown routines which restores
+the previous correct behaviour when ->set_stream() method is called to
+set a valid stream runtime pointer on playback startup.
+
+Reproduced and then fix was tested on db845c RB3 board.
+
+Reported-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
+Cc: stable@vger.kernel.org
+Fixes: 15c7fab0e047 ("ASoC: qcom: Move Soundwire runtime stream alloc to soundcards")
+Cc: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Cc: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
+Cc: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+Cc: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
+Signed-off-by: Alexey Klimov <alexey.klimov@linaro.org>
+Tested-by: Steev Klimaszewski <steev@kali.org> # Lenovo Yoga C630
+Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+Reviewed-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://patch.msgid.link/20241009213922.999355-1-alexey.klimov@linaro.org
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ sound/soc/qcom/sdm845.c | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+--- a/sound/soc/qcom/sdm845.c
++++ b/sound/soc/qcom/sdm845.c
+@@ -15,6 +15,7 @@
+ #include <uapi/linux/input-event-codes.h>
+ #include "common.h"
+ #include "qdsp6/q6afe.h"
++#include "sdw.h"
+ #include "../codecs/rt5663.h"
+
+ #define DRIVER_NAME "sdm845"
+@@ -416,7 +417,7 @@ static int sdm845_snd_startup(struct snd
+ pr_err("%s: invalid dai id 0x%x\n", __func__, cpu_dai->id);
+ break;
+ }
+- return 0;
++ return qcom_snd_sdw_startup(substream);
+ }
+
+ static void sdm845_snd_shutdown(struct snd_pcm_substream *substream)
+@@ -425,6 +426,7 @@ static void sdm845_snd_shutdown(struct
+ struct snd_soc_card *card = rtd->card;
+ struct sdm845_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];
+
+ switch (cpu_dai->id) {
+ case PRIMARY_MI2S_RX:
+@@ -463,6 +465,9 @@ static void sdm845_snd_shutdown(struct
+ pr_err("%s: invalid dai id 0x%x\n", __func__, cpu_dai->id);
+ break;
+ }
++
++ data->sruntime[cpu_dai->id] = NULL;
++ sdw_release_stream(sruntime);
+ }
+
+ static int sdm845_snd_prepare(struct snd_pcm_substream *substream)
--- /dev/null
+From ab5593793e9088abcddce30ba8e376e31b7285fd Mon Sep 17 00:00:00 2001
+From: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
+Date: Wed, 16 Oct 2024 11:29:10 +0800
+Subject: ASoC: SOF: Intel: hda: Always clean up link DMA during stop
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
+
+commit ab5593793e9088abcddce30ba8e376e31b7285fd upstream.
+
+This is required to reset the DMA read/write pointers when the stream is
+prepared and restarted after a call to snd_pcm_drain()/snd_pcm_drop().
+Also, now that the stream is reset during stop, do not save LLP registers
+in the case of STOP/suspend to avoid erroneous delay reporting.
+
+Link: https://github.com/thesofproject/sof/issues/9502
+Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
+Reviewed-by: Péter Ujfalusi <peter.ujfalusi@linux.intel.com>
+Reviewed-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
+Signed-off-by: Bard Liao <yung-chuan.liao@linux.intel.com>
+All: stable@vger.kernel.org # 6.10.x 6.11.x
+Link: https://patch.msgid.link/20241016032910.14601-5-yung-chuan.liao@linux.intel.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ sound/soc/sof/intel/hda-dai-ops.c | 23 ++++++++++-------------
+ sound/soc/sof/intel/hda-dai.c | 1 +
+ 2 files changed, 11 insertions(+), 13 deletions(-)
+
+--- a/sound/soc/sof/intel/hda-dai-ops.c
++++ b/sound/soc/sof/intel/hda-dai-ops.c
+@@ -346,20 +346,21 @@ static int hda_trigger(struct snd_sof_de
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ snd_hdac_ext_stream_start(hext_stream);
+ break;
+- case SNDRV_PCM_TRIGGER_SUSPEND:
+- case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+- snd_hdac_ext_stream_clear(hext_stream);
+-
+ /*
+- * Save the LLP registers in case the stream is
+- * restarting due PAUSE_RELEASE, or START without a pcm
+- * close/open since in this case the LLP register is not reset
+- * to 0 and the delay calculation will return with invalid
+- * results.
++ * Save the LLP registers since in case of PAUSE the LLP
++ * register are not reset to 0, the delay calculation will use
++ * the saved offsets for compensating the delay calculation.
+ */
+ hext_stream->pplcllpl = readl(hext_stream->pplc_addr + AZX_REG_PPLCLLPL);
+ hext_stream->pplcllpu = readl(hext_stream->pplc_addr + AZX_REG_PPLCLLPU);
++ snd_hdac_ext_stream_clear(hext_stream);
++ break;
++ case SNDRV_PCM_TRIGGER_SUSPEND:
++ case SNDRV_PCM_TRIGGER_STOP:
++ hext_stream->pplcllpl = 0;
++ hext_stream->pplcllpu = 0;
++ snd_hdac_ext_stream_clear(hext_stream);
+ break;
+ default:
+ dev_err(sdev->dev, "unknown trigger command %d\n", cmd);
+@@ -512,7 +513,6 @@ static const struct hda_dai_widget_dma_o
+ static int hda_ipc3_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
+ struct snd_pcm_substream *substream, int cmd)
+ {
+- struct hdac_ext_stream *hext_stream = hda_get_hext_stream(sdev, cpu_dai, substream);
+ struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
+
+ switch (cmd) {
+@@ -527,9 +527,6 @@ static int hda_ipc3_post_trigger(struct
+ if (ret < 0)
+ return ret;
+
+- if (cmd == SNDRV_PCM_TRIGGER_STOP)
+- return hda_link_dma_cleanup(substream, hext_stream, cpu_dai);
+-
+ break;
+ }
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+--- a/sound/soc/sof/intel/hda-dai.c
++++ b/sound/soc/sof/intel/hda-dai.c
+@@ -302,6 +302,7 @@ static int __maybe_unused hda_dai_trigge
+ }
+
+ switch (cmd) {
++ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ ret = hda_link_dma_cleanup(substream, hext_stream, dai);
+ if (ret < 0) {
--- /dev/null
+From 6e38a7e098d32d128b00b42a536151de9ea1340b Mon Sep 17 00:00:00 2001
+From: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
+Date: Wed, 16 Oct 2024 11:29:08 +0800
+Subject: ASoC: SOF: Intel: hda: Handle prepare without close for non-HDA DAI's
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
+
+commit 6e38a7e098d32d128b00b42a536151de9ea1340b upstream.
+
+When a PCM is restarted after a snd_pcm_drain/snd_pcm_drop(), the prepare
+callback will be invoked and the hw_params will be set again. For the
+HDA DAI's, the hw_params function handles this case already but not for
+the non-HDA DAI's. So, add the check for link_prepared to verify if the
+hw_params should be done again or not. Additionally, for SDW DAI's reset
+the PCMSyCM registers as would be done in the case of a start after a
+hw_free.
+
+Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
+Reviewed-by: Péter Ujfalusi <peter.ujfalusi@linux.intel.com>
+Reviewed-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
+Signed-off-by: Bard Liao <yung-chuan.liao@linux.intel.com>
+All: stable@vger.kernel.org # 6.10.x 6.11.x
+Link: https://patch.msgid.link/20241016032910.14601-3-yung-chuan.liao@linux.intel.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ sound/soc/sof/intel/hda-dai.c | 36 +++++++++++++++++++++++++++++++----
+ 1 file changed, 32 insertions(+), 4 deletions(-)
+
+diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c
+index 1c823f9eea57..8cccf38967e7 100644
+--- a/sound/soc/sof/intel/hda-dai.c
++++ b/sound/soc/sof/intel/hda-dai.c
+@@ -370,6 +370,13 @@ static int non_hda_dai_hw_params_data(struct snd_pcm_substream *substream,
+ return -EINVAL;
+ }
+
++ sdev = widget_to_sdev(w);
++ hext_stream = ops->get_hext_stream(sdev, cpu_dai, substream);
++
++ /* nothing more to do if the link is already prepared */
++ if (hext_stream && hext_stream->link_prepared)
++ return 0;
++
+ /* use HDaudio stream handling */
+ ret = hda_dai_hw_params_data(substream, params, cpu_dai, data, flags);
+ if (ret < 0) {
+@@ -377,7 +384,6 @@ static int non_hda_dai_hw_params_data(struct snd_pcm_substream *substream,
+ return ret;
+ }
+
+- sdev = widget_to_sdev(w);
+ if (sdev->dspless_mode_selected)
+ return 0;
+
+@@ -482,6 +488,31 @@ int sdw_hda_dai_hw_params(struct snd_pcm_substream *substream,
+ int ret;
+ int i;
+
++ ops = hda_dai_get_ops(substream, cpu_dai);
++ if (!ops) {
++ dev_err(cpu_dai->dev, "DAI widget ops not set\n");
++ return -EINVAL;
++ }
++
++ sdev = widget_to_sdev(w);
++ hext_stream = ops->get_hext_stream(sdev, cpu_dai, substream);
++
++ /* nothing more to do if the link is already prepared */
++ if (hext_stream && hext_stream->link_prepared)
++ return 0;
++
++ /*
++ * reset the PCMSyCM registers to handle a prepare callback when the PCM is restarted
++ * due to xruns or after a call to snd_pcm_drain/drop()
++ */
++ ret = hdac_bus_eml_sdw_map_stream_ch(sof_to_bus(sdev), link_id, cpu_dai->id,
++ 0, 0, substream->stream);
++ if (ret < 0) {
++ dev_err(cpu_dai->dev, "%s: hdac_bus_eml_sdw_map_stream_ch failed %d\n",
++ __func__, ret);
++ return ret;
++ }
++
+ data.dai_index = (link_id << 8) | cpu_dai->id;
+ data.dai_node_id = intel_alh_id;
+ ret = non_hda_dai_hw_params_data(substream, params, cpu_dai, &data, flags);
+@@ -490,10 +521,7 @@ int sdw_hda_dai_hw_params(struct snd_pcm_substream *substream,
+ return ret;
+ }
+
+- ops = hda_dai_get_ops(substream, cpu_dai);
+- sdev = widget_to_sdev(w);
+ hext_stream = ops->get_hext_stream(sdev, cpu_dai, substream);
+-
+ if (!hext_stream)
+ return -ENODEV;
+
+--
+2.47.0
+
--- /dev/null
+From 9814c1447f9cc67c9e88e0a4423de3a496078360 Mon Sep 17 00:00:00 2001
+From: Kai Vehmanen <kai.vehmanen@linux.intel.com>
+Date: Tue, 8 Oct 2024 09:07:10 +0300
+Subject: ASoC: SOF: Intel: hda-loader: do not wait for HDaudio IOC
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Kai Vehmanen <kai.vehmanen@linux.intel.com>
+
+commit 9814c1447f9cc67c9e88e0a4423de3a496078360 upstream.
+
+Commit 9ee3f0d8c999 ("ASOC: SOF: Intel: hda-loader: only wait for
+HDaudio IOC for IPC4 devices") removed DMA wait for IPC3 case.
+Proceed and remove the wait for IPC4 devices as well.
+
+There is no dependency to IPC version in the load logic and
+checking the firmware status is a sufficient check in case of
+errors.
+
+The removed code also had a bug in that -ETIMEDOUT is returned
+without stopping the DMA transfer.
+
+Cc: stable@vger.kernel.org
+Link: https://github.com/thesofproject/linux/issues/5135
+Fixes: 9ee3f0d8c999 ("ASOC: SOF: Intel: hda-loader: only wait for HDaudio IOC for IPC4 devices")
+Suggested-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
+Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
+Reviewed-by: Péter 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>
+Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
+Link: https://patch.msgid.link/20241008060710.15409-1-peter.ujfalusi@linux.intel.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ sound/soc/sof/intel/hda-loader.c | 17 -----------------
+ 1 file changed, 17 deletions(-)
+
+--- a/sound/soc/sof/intel/hda-loader.c
++++ b/sound/soc/sof/intel/hda-loader.c
+@@ -294,14 +294,9 @@ int hda_cl_copy_fw(struct snd_sof_dev *s
+ {
+ struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
+ const struct sof_intel_dsp_desc *chip = hda->desc;
+- struct sof_intel_hda_stream *hda_stream;
+- unsigned long time_left;
+ unsigned int reg;
+ int ret, status;
+
+- hda_stream = container_of(hext_stream, struct sof_intel_hda_stream,
+- hext_stream);
+-
+ dev_dbg(sdev->dev, "Code loader DMA starting\n");
+
+ ret = hda_cl_trigger(sdev->dev, hext_stream, SNDRV_PCM_TRIGGER_START);
+@@ -310,18 +305,6 @@ int hda_cl_copy_fw(struct snd_sof_dev *s
+ return ret;
+ }
+
+- if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) {
+- /* Wait for completion of transfer */
+- time_left = wait_for_completion_timeout(&hda_stream->ioc,
+- msecs_to_jiffies(HDA_CL_DMA_IOC_TIMEOUT_MS));
+-
+- if (!time_left) {
+- dev_err(sdev->dev, "Code loader DMA did not complete\n");
+- return -ETIMEDOUT;
+- }
+- dev_dbg(sdev->dev, "Code loader DMA done\n");
+- }
+-
+ dev_dbg(sdev->dev, "waiting for FW_ENTERED status\n");
+
+ status = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR,
--- /dev/null
+From 9822b4c90d77e3c6555fb21c459c4a61c6a8619f Mon Sep 17 00:00:00 2001
+From: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
+Date: Wed, 16 Oct 2024 11:29:07 +0800
+Subject: ASoC: SOF: ipc4-topology: Do not set ALH node_id for aggregated DAIs
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
+
+commit 9822b4c90d77e3c6555fb21c459c4a61c6a8619f upstream.
+
+For aggregated DAIs, the node ID is set to the group_id during the DAI
+widget's ipc_prepare op. With the current logic, setting the dai_index
+for node_id in the dai_config is redundant as it will be overwritten
+with the group_id anyway. Removing it will also prevent any accidental
+clearing/resetting of the group_id for aggregated DAIs due to the
+dai_config calls could that happen before the allocated group_id is
+freed.
+
+Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
+Reviewed-by: Péter Ujfalusi <peter.ujfalusi@linux.intel.com>
+Reviewed-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
+Signed-off-by: Bard Liao <yung-chuan.liao@linux.intel.com>
+All: stable@vger.kernel.org # 6.10.x 6.11.x
+Link: https://patch.msgid.link/20241016032910.14601-2-yung-chuan.liao@linux.intel.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ sound/soc/sof/ipc4-topology.c | 15 +++++++++++++--
+ 1 file changed, 13 insertions(+), 2 deletions(-)
+
+diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c
+index 87be7f16e8c2..240fee2166d1 100644
+--- a/sound/soc/sof/ipc4-topology.c
++++ b/sound/soc/sof/ipc4-topology.c
+@@ -3129,9 +3129,20 @@ static int sof_ipc4_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget *
+ * group_id during copier's ipc_prepare op.
+ */
+ if (flags & SOF_DAI_CONFIG_FLAGS_HW_PARAMS) {
++ struct sof_ipc4_alh_configuration_blob *blob;
++
++ blob = (struct sof_ipc4_alh_configuration_blob *)ipc4_copier->copier_config;
+ ipc4_copier->dai_index = data->dai_node_id;
+- copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK;
+- copier_data->gtw_cfg.node_id |= SOF_IPC4_NODE_INDEX(data->dai_node_id);
++
++ /*
++ * no need to set the node_id for aggregated DAI's. These will be assigned
++ * a group_id during widget ipc_prepare
++ */
++ if (blob->alh_cfg.device_count == 1) {
++ copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK;
++ copier_data->gtw_cfg.node_id |=
++ SOF_IPC4_NODE_INDEX(data->dai_node_id);
++ }
+ }
+
+ break;
+--
+2.47.0
+
--- /dev/null
+From 8e59a2a5459fd9840dbe2cbde85fe154b11e1727 Mon Sep 17 00:00:00 2001
+From: Niklas Cassel <cassel@kernel.org>
+Date: Wed, 23 Oct 2024 12:55:41 +0200
+Subject: ata: libata: Set DID_TIME_OUT for commands that actually timed out
+
+From: Niklas Cassel <cassel@kernel.org>
+
+commit 8e59a2a5459fd9840dbe2cbde85fe154b11e1727 upstream.
+
+When ata_qc_complete() schedules a command for EH using
+ata_qc_schedule_eh(), blk_abort_request() will be called, which leads to
+req->q->mq_ops->timeout() / scsi_timeout() being called.
+
+scsi_timeout(), if the LLDD has no abort handler (libata has no abort
+handler), will set host byte to DID_TIME_OUT, and then call
+scsi_eh_scmd_add() to add the command to EH.
+
+Thus, when commands first enter libata's EH strategy_handler, all the
+commands that have been added to EH will have DID_TIME_OUT set.
+
+Commit e5dd410acb34 ("ata: libata: Clear DID_TIME_OUT for ATA PT commands
+with sense data") clears this bogus DID_TIME_OUT flag for all commands
+that reached libata's EH strategy_handler.
+
+libata has its own flag (AC_ERR_TIMEOUT), that it sets for commands that
+have not received a completion at the time of entering EH.
+
+ata_eh_worth_retry() has no special handling for AC_ERR_TIMEOUT, so by
+default timed out commands will get flag ATA_QCFLAG_RETRY set, and will be
+retried after the port has been reset (ata_eh_link_autopsy() always
+triggers a port reset if any command has AC_ERR_TIMEOUT set).
+
+For a command that has ATA_QCFLAG_RETRY set, while also having an error
+flag set (e.g. AC_ERR_TIMEOUT), ata_eh_finish() will not increment
+scmd->allowed, so the command will at most be retried scmd->allowed number
+of times (which by default is set to 3).
+
+However, scsi_eh_flush_done_q() will only retry commands for which
+scsi_noretry_cmd() returns false.
+
+For a command that has DID_TIME_OUT set, while also having either the
+FAILFAST flag set, or the command being a passthrough command,
+scsi_noretry_cmd() will return true. Thus, such a command will never be
+retried.
+
+Thus, make sure that libata sets SCSI's DID_TIME_OUT flag for commands that
+actually timed out (libata's AC_ERR_TIMEOUT flag), such that timed out
+commands will once again not be retried if they are also a FAILFAST or
+passthrough command.
+
+Cc: stable@vger.kernel.org
+Fixes: e5dd410acb34 ("ata: libata: Clear DID_TIME_OUT for ATA PT commands with sense data")
+Reported-by: Lai, Yi <yi1.lai@linux.intel.com>
+Closes: https://lore.kernel.org/linux-ide/ZxYz871I3Blsi30F@ly-workstation/
+Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
+Link: https://lore.kernel.org/r/20241023105540.1070012-2-cassel@kernel.org
+Signed-off-by: Niklas Cassel <cassel@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/ata/libata-eh.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/ata/libata-eh.c
++++ b/drivers/ata/libata-eh.c
+@@ -648,6 +648,7 @@ void ata_scsi_cmd_error_handler(struct S
+ /* the scmd has an associated qc */
+ if (!(qc->flags & ATA_QCFLAG_EH)) {
+ /* which hasn't failed yet, timeout */
++ set_host_byte(scmd, DID_TIME_OUT);
+ qc->err_mask |= AC_ERR_TIMEOUT;
+ qc->flags |= ATA_QCFLAG_EH;
+ nr_timedout++;
--- /dev/null
+From 2ff949441802a8d076d9013c7761f63e8ae5a9bd Mon Sep 17 00:00:00 2001
+From: Xinyu Zhang <xizhang@purestorage.com>
+Date: Wed, 23 Oct 2024 15:15:19 -0600
+Subject: block: fix sanity checks in blk_rq_map_user_bvec
+
+From: Xinyu Zhang <xizhang@purestorage.com>
+
+commit 2ff949441802a8d076d9013c7761f63e8ae5a9bd upstream.
+
+blk_rq_map_user_bvec contains a check bytes + bv->bv_len > nr_iter which
+causes unnecessary failures in NVMe passthrough I/O, reproducible as
+follows:
+
+- register a 2 page, page-aligned buffer against a ring
+- use that buffer to do a 1 page io_uring NVMe passthrough read
+
+The second (i = 1) iteration of the loop in blk_rq_map_user_bvec will
+then have nr_iter == 1 page, bytes == 1 page, bv->bv_len == 1 page, so
+the check bytes + bv->bv_len > nr_iter will succeed, causing the I/O to
+fail. This failure is unnecessary, as when the check succeeds, it means
+we've checked the entire buffer that will be used by the request - i.e.
+blk_rq_map_user_bvec should complete successfully. Therefore, terminate
+the loop early and return successfully when the check bytes + bv->bv_len
+> nr_iter succeeds.
+
+While we're at it, also remove the check that all segments in the bvec
+are single-page. While this seems to be true for all users of the
+function, it doesn't appear to be required anywhere downstream.
+
+CC: stable@vger.kernel.org
+Signed-off-by: Xinyu Zhang <xizhang@purestorage.com>
+Co-developed-by: Uday Shankar <ushankar@purestorage.com>
+Signed-off-by: Uday Shankar <ushankar@purestorage.com>
+Fixes: 37987547932c ("block: extend functionality to map bvec iterator")
+Link: https://lore.kernel.org/r/20241023211519.4177873-1-ushankar@purestorage.com
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ block/blk-map.c | 4 +---
+ 1 file changed, 1 insertion(+), 3 deletions(-)
+
+--- a/block/blk-map.c
++++ b/block/blk-map.c
+@@ -600,9 +600,7 @@ static int blk_rq_map_user_bvec(struct r
+ if (nsegs >= nr_segs || bytes > UINT_MAX - bv->bv_len)
+ goto put_bio;
+ if (bytes + bv->bv_len > nr_iter)
+- goto put_bio;
+- if (bv->bv_offset + bv->bv_len > PAGE_SIZE)
+- goto put_bio;
++ break;
+
+ nsegs++;
+ bytes += bv->bv_len;
--- /dev/null
+From 63feb35cd26557572ad95fc062ede344bb61d9ad Mon Sep 17 00:00:00 2001
+From: Aurabindo Pillai <aurabindo.pillai@amd.com>
+Date: Mon, 7 Oct 2024 14:19:32 -0400
+Subject: drm/amd/display: temp w/a for DP Link Layer compliance
+
+From: Aurabindo Pillai <aurabindo.pillai@amd.com>
+
+commit 63feb35cd26557572ad95fc062ede344bb61d9ad upstream.
+
+[Why&How]
+Disabling P-State support on full updates for DCN401 results in
+introducing additional communication with SMU. A UCLK hard min message
+to SMU takes 4 seconds to go through, which was due to DCN not allowing
+pstate switch, which was caused by incorrect value for TTU watermark
+before blanking the HUBP prior to DPG on for servicing the test request.
+
+Fix the issue temporarily by disallowing pstate changes for compliance
+test while test request handler is reworked for a proper fix.
+
+Fixes: 67ea53a4bd9d ("drm/amd/display: Disable DCN401 UCLK P-State support on full updates")
+Cc: Mario Limonciello <mario.limonciello@amd.com>
+Cc: Alex Deucher <alexander.deucher@amd.com>
+Reviewed-by: Dillon Varone <dillon.varone@amd.com>
+Signed-off-by: Aurabindo Pillai <aurabindo.pillai@amd.com>
+Signed-off-by: Wayne Lin <wayne.lin@amd.com>
+Tested-by: Daniel Wheeler <daniel.wheeler@amd.com>
+Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
+(cherry picked from commit 8a79f7cdbb41bb0ddfd4d7662b4428d4a9d5306d)
+Cc: stable@vger.kernel.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c | 13 +++++++++++++
+ 1 file changed, 13 insertions(+)
+
+--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c
++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c
+@@ -44,6 +44,7 @@
+
+ #include "dm_helpers.h"
+ #include "ddc_service_types.h"
++#include "clk_mgr.h"
+
+ static u32 edid_extract_panel_id(struct edid *edid)
+ {
+@@ -1121,6 +1122,8 @@ bool dm_helpers_dp_handle_test_pattern_r
+ struct pipe_ctx *pipe_ctx = NULL;
+ struct amdgpu_dm_connector *aconnector = link->priv;
+ struct drm_device *dev = aconnector->base.dev;
++ struct dc_state *dc_state = ctx->dc->current_state;
++ struct clk_mgr *clk_mgr = ctx->dc->clk_mgr;
+ int i;
+
+ for (i = 0; i < MAX_PIPES; i++) {
+@@ -1221,6 +1224,16 @@ bool dm_helpers_dp_handle_test_pattern_r
+ pipe_ctx->stream->test_pattern.type = test_pattern;
+ pipe_ctx->stream->test_pattern.color_space = test_pattern_color_space;
+
++ /* Temp W/A for compliance test failure */
++ dc_state->bw_ctx.bw.dcn.clk.p_state_change_support = false;
++ dc_state->bw_ctx.bw.dcn.clk.dramclk_khz = clk_mgr->dc_mode_softmax_enabled ?
++ clk_mgr->bw_params->dc_mode_softmax_memclk : clk_mgr->bw_params->max_memclk_mhz;
++ dc_state->bw_ctx.bw.dcn.clk.idle_dramclk_khz = dc_state->bw_ctx.bw.dcn.clk.dramclk_khz;
++ ctx->dc->clk_mgr->funcs->update_clocks(
++ ctx->dc->clk_mgr,
++ dc_state,
++ false);
++
+ dc_link_dp_set_test_pattern(
+ (struct dc_link *) link,
+ test_pattern,
--- /dev/null
+From de96f6a3003513c796bbe4e23210a446913f5c00 Mon Sep 17 00:00:00 2001
+From: Michel Alex <Alex.Michel@wiedemann-group.com>
+Date: Wed, 16 Oct 2024 12:11:15 +0000
+Subject: net: phy: dp83822: Fix reset pin definitions
+
+From: Michel Alex <Alex.Michel@wiedemann-group.com>
+
+commit de96f6a3003513c796bbe4e23210a446913f5c00 upstream.
+
+This change fixes a rare issue where the PHY fails to detect a link
+due to incorrect reset behavior.
+
+The SW_RESET definition was incorrectly assigned to bit 14, which is the
+Digital Restart bit according to the datasheet. This commit corrects
+SW_RESET to bit 15 and assigns DIG_RESTART to bit 14 as per the
+datasheet specifications.
+
+The SW_RESET define is only used in the phy_reset function, which fully
+re-initializes the PHY after the reset is performed. The change in the
+bit definitions should not have any negative impact on the functionality
+of the PHY.
+
+v2:
+- added Fixes tag
+- improved commit message
+
+Cc: stable@vger.kernel.org
+Fixes: 5dc39fd5ef35 ("net: phy: DP83822: Add ability to advertise Fiber connection")
+Signed-off-by: Alex Michel <alex.michel@wiedemann-group.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Message-ID: <AS1P250MB0608A798661549BF83C4B43EA9462@AS1P250MB0608.EURP250.PROD.OUTLOOK.COM>
+Signed-off-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/phy/dp83822.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/phy/dp83822.c
++++ b/drivers/net/phy/dp83822.c
+@@ -45,8 +45,8 @@
+ /* Control Register 2 bits */
+ #define DP83822_FX_ENABLE BIT(14)
+
+-#define DP83822_HW_RESET BIT(15)
+-#define DP83822_SW_RESET BIT(14)
++#define DP83822_SW_RESET BIT(15)
++#define DP83822_DIG_RESTART BIT(14)
+
+ /* PHY STS bits */
+ #define DP83822_PHYSTS_DUPLEX BIT(2)
--- /dev/null
+From 8dd91e8d31febf4d9cca3ae1bb4771d33ae7ee5a Mon Sep 17 00:00:00 2001
+From: Olga Kornievskaia <okorniev@redhat.com>
+Date: Fri, 18 Oct 2024 15:24:58 -0400
+Subject: nfsd: fix race between laundromat and free_stateid
+
+From: Olga Kornievskaia <okorniev@redhat.com>
+
+commit 8dd91e8d31febf4d9cca3ae1bb4771d33ae7ee5a upstream.
+
+There is a race between laundromat handling of revoked delegations
+and a client sending free_stateid operation. Laundromat thread
+finds that delegation has expired and needs to be revoked so it
+marks the delegation stid revoked and it puts it on a reaper list
+but then it unlock the state lock and the actual delegation revocation
+happens without the lock. Once the stid is marked revoked a racing
+free_stateid processing thread does the following (1) it calls
+list_del_init() which removes it from the reaper list and (2) frees
+the delegation stid structure. The laundromat thread ends up not
+calling the revoke_delegation() function for this particular delegation
+but that means it will no release the lock lease that exists on
+the file.
+
+Now, a new open for this file comes in and ends up finding that
+lease list isn't empty and calls nfsd_breaker_owns_lease() which ends
+up trying to derefence a freed delegation stateid. Leading to the
+followint use-after-free KASAN warning:
+
+kernel: ==================================================================
+kernel: BUG: KASAN: slab-use-after-free in nfsd_breaker_owns_lease+0x140/0x160 [nfsd]
+kernel: Read of size 8 at addr ffff0000e73cd0c8 by task nfsd/6205
+kernel:
+kernel: CPU: 2 UID: 0 PID: 6205 Comm: nfsd Kdump: loaded Not tainted 6.11.0-rc7+ #9
+kernel: Hardware name: Apple Inc. Apple Virtualization Generic Platform, BIOS 2069.0.0.0.0 08/03/2024
+kernel: Call trace:
+kernel: dump_backtrace+0x98/0x120
+kernel: show_stack+0x1c/0x30
+kernel: dump_stack_lvl+0x80/0xe8
+kernel: print_address_description.constprop.0+0x84/0x390
+kernel: print_report+0xa4/0x268
+kernel: kasan_report+0xb4/0xf8
+kernel: __asan_report_load8_noabort+0x1c/0x28
+kernel: nfsd_breaker_owns_lease+0x140/0x160 [nfsd]
+kernel: nfsd_file_do_acquire+0xb3c/0x11d0 [nfsd]
+kernel: nfsd_file_acquire_opened+0x84/0x110 [nfsd]
+kernel: nfs4_get_vfs_file+0x634/0x958 [nfsd]
+kernel: nfsd4_process_open2+0xa40/0x1a40 [nfsd]
+kernel: nfsd4_open+0xa08/0xe80 [nfsd]
+kernel: nfsd4_proc_compound+0xb8c/0x2130 [nfsd]
+kernel: nfsd_dispatch+0x22c/0x718 [nfsd]
+kernel: svc_process_common+0x8e8/0x1960 [sunrpc]
+kernel: svc_process+0x3d4/0x7e0 [sunrpc]
+kernel: svc_handle_xprt+0x828/0xe10 [sunrpc]
+kernel: svc_recv+0x2cc/0x6a8 [sunrpc]
+kernel: nfsd+0x270/0x400 [nfsd]
+kernel: kthread+0x288/0x310
+kernel: ret_from_fork+0x10/0x20
+
+This patch proposes a fixed that's based on adding 2 new additional
+stid's sc_status values that help coordinate between the laundromat
+and other operations (nfsd4_free_stateid() and nfsd4_delegreturn()).
+
+First to make sure, that once the stid is marked revoked, it is not
+removed by the nfsd4_free_stateid(), the laundromat take a reference
+on the stateid. Then, coordinating whether the stid has been put
+on the cl_revoked list or we are processing FREE_STATEID and need to
+make sure to remove it from the list, each check that state and act
+accordingly. If laundromat has added to the cl_revoke list before
+the arrival of FREE_STATEID, then nfsd4_free_stateid() knows to remove
+it from the list. If nfsd4_free_stateid() finds that operations arrived
+before laundromat has placed it on cl_revoke list, it marks the state
+freed and then laundromat will no longer add it to the list.
+
+Also, for nfsd4_delegreturn() when looking for the specified stid,
+we need to access stid that are marked removed or freeable, it means
+the laundromat has started processing it but hasn't finished and this
+delegreturn needs to return nfserr_deleg_revoked and not
+nfserr_bad_stateid. The latter will not trigger a FREE_STATEID and the
+lack of it will leave this stid on the cl_revoked list indefinitely.
+
+Fixes: 2d4a532d385f ("nfsd: ensure that clp->cl_revoked list is protected by clp->cl_lock")
+CC: stable@vger.kernel.org
+Signed-off-by: Olga Kornievskaia <okorniev@redhat.com>
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/nfsd/nfs4state.c | 48 ++++++++++++++++++++++++++++++++++++++++--------
+ fs/nfsd/state.h | 2 ++
+ 2 files changed, 42 insertions(+), 8 deletions(-)
+
+--- a/fs/nfsd/nfs4state.c
++++ b/fs/nfsd/nfs4state.c
+@@ -1358,21 +1358,47 @@ static void destroy_delegation(struct nf
+ destroy_unhashed_deleg(dp);
+ }
+
++/**
++ * revoke_delegation - perform nfs4 delegation structure cleanup
++ * @dp: pointer to the delegation
++ *
++ * This function assumes that it's called either from the administrative
++ * interface (nfsd4_revoke_states()) that's revoking a specific delegation
++ * stateid or it's called from a laundromat thread (nfsd4_landromat()) that
++ * determined that this specific state has expired and needs to be revoked
++ * (both mark state with the appropriate stid sc_status mode). It is also
++ * assumed that a reference was taken on the @dp state.
++ *
++ * If this function finds that the @dp state is SC_STATUS_FREED it means
++ * that a FREE_STATEID operation for this stateid has been processed and
++ * we can proceed to removing it from recalled list. However, if @dp state
++ * isn't marked SC_STATUS_FREED, it means we need place it on the cl_revoked
++ * list and wait for the FREE_STATEID to arrive from the client. At the same
++ * time, we need to mark it as SC_STATUS_FREEABLE to indicate to the
++ * nfsd4_free_stateid() function that this stateid has already been added
++ * to the cl_revoked list and that nfsd4_free_stateid() is now responsible
++ * for removing it from the list. Inspection of where the delegation state
++ * in the revocation process is protected by the clp->cl_lock.
++ */
+ static void revoke_delegation(struct nfs4_delegation *dp)
+ {
+ struct nfs4_client *clp = dp->dl_stid.sc_client;
+
+ WARN_ON(!list_empty(&dp->dl_recall_lru));
++ WARN_ON_ONCE(!(dp->dl_stid.sc_status &
++ (SC_STATUS_REVOKED | SC_STATUS_ADMIN_REVOKED)));
+
+ trace_nfsd_stid_revoke(&dp->dl_stid);
+
+- if (dp->dl_stid.sc_status &
+- (SC_STATUS_REVOKED | SC_STATUS_ADMIN_REVOKED)) {
+- spin_lock(&clp->cl_lock);
+- refcount_inc(&dp->dl_stid.sc_count);
+- list_add(&dp->dl_recall_lru, &clp->cl_revoked);
+- spin_unlock(&clp->cl_lock);
++ spin_lock(&clp->cl_lock);
++ if (dp->dl_stid.sc_status & SC_STATUS_FREED) {
++ list_del_init(&dp->dl_recall_lru);
++ goto out;
+ }
++ list_add(&dp->dl_recall_lru, &clp->cl_revoked);
++ dp->dl_stid.sc_status |= SC_STATUS_FREEABLE;
++out:
++ spin_unlock(&clp->cl_lock);
+ destroy_unhashed_deleg(dp);
+ }
+
+@@ -1781,6 +1807,7 @@ void nfsd4_revoke_states(struct net *net
+ mutex_unlock(&stp->st_mutex);
+ break;
+ case SC_TYPE_DELEG:
++ refcount_inc(&stid->sc_count);
+ dp = delegstateid(stid);
+ spin_lock(&state_lock);
+ if (!unhash_delegation_locked(
+@@ -6544,6 +6571,7 @@ nfs4_laundromat(struct nfsd_net *nn)
+ dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru);
+ if (!state_expired(<, dp->dl_time))
+ break;
++ refcount_inc(&dp->dl_stid.sc_count);
+ unhash_delegation_locked(dp, SC_STATUS_REVOKED);
+ list_add(&dp->dl_recall_lru, &reaplist);
+ }
+@@ -7161,7 +7189,9 @@ nfsd4_free_stateid(struct svc_rqst *rqst
+ s->sc_status |= SC_STATUS_CLOSED;
+ spin_unlock(&s->sc_lock);
+ dp = delegstateid(s);
+- list_del_init(&dp->dl_recall_lru);
++ if (s->sc_status & SC_STATUS_FREEABLE)
++ list_del_init(&dp->dl_recall_lru);
++ s->sc_status |= SC_STATUS_FREED;
+ spin_unlock(&cl->cl_lock);
+ nfs4_put_stid(s);
+ ret = nfs_ok;
+@@ -7491,7 +7521,9 @@ nfsd4_delegreturn(struct svc_rqst *rqstp
+ if ((status = fh_verify(rqstp, &cstate->current_fh, S_IFREG, 0)))
+ return status;
+
+- status = nfsd4_lookup_stateid(cstate, stateid, SC_TYPE_DELEG, 0, &s, nn);
++ status = nfsd4_lookup_stateid(cstate, stateid, SC_TYPE_DELEG,
++ SC_STATUS_REVOKED | SC_STATUS_FREEABLE,
++ &s, nn);
+ if (status)
+ goto out;
+ dp = delegstateid(s);
+--- a/fs/nfsd/state.h
++++ b/fs/nfsd/state.h
+@@ -113,6 +113,8 @@ struct nfs4_stid {
+ /* For a deleg stateid kept around only to process free_stateid's: */
+ #define SC_STATUS_REVOKED BIT(1)
+ #define SC_STATUS_ADMIN_REVOKED BIT(2)
++#define SC_STATUS_FREEABLE BIT(3)
++#define SC_STATUS_FREED BIT(4)
+ unsigned short sc_status;
+
+ struct list_head sc_cp_list;
--- /dev/null
+From 26f8dd2dde6864558782d91542f89483bd59a3c2 Mon Sep 17 00:00:00 2001
+From: Dominique Martinet <asmadeus@codewreck.org>
+Date: Thu, 24 Oct 2024 08:52:12 +0900
+Subject: Revert "fs/9p: fix uaf in in v9fs_stat2inode_dotl"
+
+From: Dominique Martinet <asmadeus@codewreck.org>
+
+commit 26f8dd2dde6864558782d91542f89483bd59a3c2 upstream.
+
+This reverts commit 11763a8598f888dec631a8a903f7ada32181001f.
+
+This is a requirement to revert commit 724a08450f74 ("fs/9p: simplify
+iget to remove unnecessary paths"), see that revert for details.
+
+Fixes: 724a08450f74 ("fs/9p: simplify iget to remove unnecessary paths")
+Reported-by: Will Deacon <will@kernel.org>
+Link: https://lkml.kernel.org/r/20240923100508.GA32066@willie-the-truck
+Cc: stable@vger.kernel.org # v6.9+
+Message-ID: <20241024-revert_iget-v1-3-4cac63d25f72@codewreck.org>
+Signed-off-by: Dominique Martinet <asmadeus@codewreck.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/9p/vfs_inode_dotl.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/fs/9p/vfs_inode_dotl.c b/fs/9p/vfs_inode_dotl.c
+index 2b313fe7003e..ef9db3e03506 100644
+--- a/fs/9p/vfs_inode_dotl.c
++++ b/fs/9p/vfs_inode_dotl.c
+@@ -78,11 +78,11 @@ struct inode *v9fs_fid_iget_dotl(struct super_block *sb, struct p9_fid *fid)
+
+ retval = v9fs_init_inode(v9ses, inode, &fid->qid,
+ st->st_mode, new_decode_dev(st->st_rdev));
+- v9fs_stat2inode_dotl(st, inode, 0);
+ kfree(st);
+ if (retval)
+ goto error;
+
++ v9fs_stat2inode_dotl(st, inode, 0);
+ v9fs_set_netfs_context(inode);
+ v9fs_cache_inode_get_cookie(inode);
+ retval = v9fs_get_acl(inode, fid);
+--
+2.47.0
+
--- /dev/null
+From f69999b5f9b444a2443ca2b9e5976e78bb5b7c69 Mon Sep 17 00:00:00 2001
+From: Dominique Martinet <asmadeus@codewreck.org>
+Date: Thu, 24 Oct 2024 08:52:10 +0900
+Subject: Revert " fs/9p: mitigate inode collisions"
+
+From: Dominique Martinet <asmadeus@codewreck.org>
+
+commit f69999b5f9b444a2443ca2b9e5976e78bb5b7c69 upstream.
+
+This reverts commit d05dcfdf5e1659b2949d13060284eff3888b644e.
+
+This is a requirement to revert commit 724a08450f74 ("fs/9p: simplify
+iget to remove unnecessary paths"), see that revert for details.
+
+Fixes: 724a08450f74 ("fs/9p: simplify iget to remove unnecessary paths")
+Reported-by: Will Deacon <will@kernel.org>
+Link: https://lkml.kernel.org/r/20240923100508.GA32066@willie-the-truck
+Cc: stable@vger.kernel.org # v6.9+
+Message-ID: <20241024-revert_iget-v1-1-4cac63d25f72@codewreck.org>
+Signed-off-by: Dominique Martinet <asmadeus@codewreck.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/9p/v9fs.h | 11 +++++------
+ fs/9p/vfs_inode.c | 37 ++++++++-----------------------------
+ fs/9p/vfs_inode_dotl.c | 28 ++++++++--------------------
+ fs/9p/vfs_super.c | 2 +-
+ 4 files changed, 22 insertions(+), 56 deletions(-)
+
+--- a/fs/9p/v9fs.h
++++ b/fs/9p/v9fs.h
+@@ -179,14 +179,13 @@ extern int v9fs_vfs_rename(struct mnt_id
+ struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry,
+ unsigned int flags);
+-extern struct inode *v9fs_fid_iget(struct super_block *sb, struct p9_fid *fid,
+- bool new);
++extern struct inode *v9fs_fid_iget(struct super_block *sb, struct p9_fid *fid);
+ extern const struct inode_operations v9fs_dir_inode_operations_dotl;
+ extern const struct inode_operations v9fs_file_inode_operations_dotl;
+ extern const struct inode_operations v9fs_symlink_inode_operations_dotl;
+ extern const struct netfs_request_ops v9fs_req_ops;
+ extern struct inode *v9fs_fid_iget_dotl(struct super_block *sb,
+- struct p9_fid *fid, bool new);
++ struct p9_fid *fid);
+
+ /* other default globals */
+ #define V9FS_PORT 564
+@@ -225,12 +224,12 @@ static inline int v9fs_proto_dotl(struct
+ */
+ static inline struct inode *
+ v9fs_get_inode_from_fid(struct v9fs_session_info *v9ses, struct p9_fid *fid,
+- struct super_block *sb, bool new)
++ struct super_block *sb)
+ {
+ if (v9fs_proto_dotl(v9ses))
+- return v9fs_fid_iget_dotl(sb, fid, new);
++ return v9fs_fid_iget_dotl(sb, fid);
+ else
+- return v9fs_fid_iget(sb, fid, new);
++ return v9fs_fid_iget(sb, fid);
+ }
+
+ #endif
+--- a/fs/9p/vfs_inode.c
++++ b/fs/9p/vfs_inode.c
+@@ -365,8 +365,7 @@ void v9fs_evict_inode(struct inode *inod
+ clear_inode(inode);
+ }
+
+-struct inode *
+-v9fs_fid_iget(struct super_block *sb, struct p9_fid *fid, bool new)
++struct inode *v9fs_fid_iget(struct super_block *sb, struct p9_fid *fid)
+ {
+ dev_t rdev;
+ int retval;
+@@ -378,18 +377,8 @@ v9fs_fid_iget(struct super_block *sb, st
+ inode = iget_locked(sb, QID2INO(&fid->qid));
+ if (unlikely(!inode))
+ return ERR_PTR(-ENOMEM);
+- if (!(inode->i_state & I_NEW)) {
+- if (!new) {
+- goto done;
+- } else {
+- p9_debug(P9_DEBUG_VFS, "WARNING: Inode collision %ld\n",
+- inode->i_ino);
+- iput(inode);
+- remove_inode_hash(inode);
+- inode = iget_locked(sb, QID2INO(&fid->qid));
+- WARN_ON(!(inode->i_state & I_NEW));
+- }
+- }
++ if (!(inode->i_state & I_NEW))
++ return inode;
+
+ /*
+ * initialize the inode with the stat info
+@@ -413,11 +402,11 @@ v9fs_fid_iget(struct super_block *sb, st
+ v9fs_set_netfs_context(inode);
+ v9fs_cache_inode_get_cookie(inode);
+ unlock_new_inode(inode);
+-done:
+ return inode;
+ error:
+ iget_failed(inode);
+ return ERR_PTR(retval);
++
+ }
+
+ /**
+@@ -449,15 +438,8 @@ static int v9fs_at_to_dotl_flags(int fla
+ */
+ static void v9fs_dec_count(struct inode *inode)
+ {
+- if (!S_ISDIR(inode->i_mode) || inode->i_nlink > 2) {
+- if (inode->i_nlink) {
+- drop_nlink(inode);
+- } else {
+- p9_debug(P9_DEBUG_VFS,
+- "WARNING: unexpected i_nlink zero %d inode %ld\n",
+- inode->i_nlink, inode->i_ino);
+- }
+- }
++ if (!S_ISDIR(inode->i_mode) || inode->i_nlink > 2)
++ drop_nlink(inode);
+ }
+
+ /**
+@@ -508,9 +490,6 @@ static int v9fs_remove(struct inode *dir
+ } else
+ v9fs_dec_count(inode);
+
+- if (inode->i_nlink <= 0) /* no more refs unhash it */
+- remove_inode_hash(inode);
+-
+ v9fs_invalidate_inode_attr(inode);
+ v9fs_invalidate_inode_attr(dir);
+
+@@ -576,7 +555,7 @@ v9fs_create(struct v9fs_session_info *v9
+ /*
+ * instantiate inode and assign the unopened fid to the dentry
+ */
+- inode = v9fs_get_inode_from_fid(v9ses, fid, dir->i_sb, true);
++ inode = v9fs_get_inode_from_fid(v9ses, fid, dir->i_sb);
+ if (IS_ERR(inode)) {
+ err = PTR_ERR(inode);
+ p9_debug(P9_DEBUG_VFS,
+@@ -705,7 +684,7 @@ struct dentry *v9fs_vfs_lookup(struct in
+ else if (IS_ERR(fid))
+ inode = ERR_CAST(fid);
+ else
+- inode = v9fs_get_inode_from_fid(v9ses, fid, dir->i_sb, false);
++ inode = v9fs_get_inode_from_fid(v9ses, fid, dir->i_sb);
+ /*
+ * If we had a rename on the server and a parallel lookup
+ * for the new name, then make sure we instantiate with
+--- a/fs/9p/vfs_inode_dotl.c
++++ b/fs/9p/vfs_inode_dotl.c
+@@ -52,10 +52,7 @@ static kgid_t v9fs_get_fsgid_for_create(
+ return current_fsgid();
+ }
+
+-
+-
+-struct inode *
+-v9fs_fid_iget_dotl(struct super_block *sb, struct p9_fid *fid, bool new)
++struct inode *v9fs_fid_iget_dotl(struct super_block *sb, struct p9_fid *fid)
+ {
+ int retval;
+ struct inode *inode;
+@@ -65,18 +62,8 @@ v9fs_fid_iget_dotl(struct super_block *s
+ inode = iget_locked(sb, QID2INO(&fid->qid));
+ if (unlikely(!inode))
+ return ERR_PTR(-ENOMEM);
+- if (!(inode->i_state & I_NEW)) {
+- if (!new) {
+- goto done;
+- } else { /* deal with race condition in inode number reuse */
+- p9_debug(P9_DEBUG_ERROR, "WARNING: Inode collision %lx\n",
+- inode->i_ino);
+- iput(inode);
+- remove_inode_hash(inode);
+- inode = iget_locked(sb, QID2INO(&fid->qid));
+- WARN_ON(!(inode->i_state & I_NEW));
+- }
+- }
++ if (!(inode->i_state & I_NEW))
++ return inode;
+
+ /*
+ * initialize the inode with the stat info
+@@ -103,11 +90,12 @@ v9fs_fid_iget_dotl(struct super_block *s
+ goto error;
+
+ unlock_new_inode(inode);
+-done:
++
+ return inode;
+ error:
+ iget_failed(inode);
+ return ERR_PTR(retval);
++
+ }
+
+ struct dotl_openflag_map {
+@@ -259,7 +247,7 @@ v9fs_vfs_atomic_open_dotl(struct inode *
+ p9_debug(P9_DEBUG_VFS, "p9_client_walk failed %d\n", err);
+ goto out;
+ }
+- inode = v9fs_fid_iget_dotl(dir->i_sb, fid, true);
++ inode = v9fs_fid_iget_dotl(dir->i_sb, fid);
+ if (IS_ERR(inode)) {
+ err = PTR_ERR(inode);
+ p9_debug(P9_DEBUG_VFS, "inode creation failed %d\n", err);
+@@ -352,7 +340,7 @@ static int v9fs_vfs_mkdir_dotl(struct mn
+ }
+
+ /* instantiate inode and assign the unopened fid to the dentry */
+- inode = v9fs_fid_iget_dotl(dir->i_sb, fid, true);
++ inode = v9fs_fid_iget_dotl(dir->i_sb, fid);
+ if (IS_ERR(inode)) {
+ err = PTR_ERR(inode);
+ p9_debug(P9_DEBUG_VFS, "inode creation failed %d\n",
+@@ -788,7 +776,7 @@ v9fs_vfs_mknod_dotl(struct mnt_idmap *id
+ err);
+ goto error;
+ }
+- inode = v9fs_fid_iget_dotl(dir->i_sb, fid, true);
++ inode = v9fs_fid_iget_dotl(dir->i_sb, fid);
+ if (IS_ERR(inode)) {
+ err = PTR_ERR(inode);
+ p9_debug(P9_DEBUG_VFS, "inode creation failed %d\n",
+--- a/fs/9p/vfs_super.c
++++ b/fs/9p/vfs_super.c
+@@ -139,7 +139,7 @@ static struct dentry *v9fs_mount(struct
+ else
+ sb->s_d_op = &v9fs_dentry_operations;
+
+- inode = v9fs_get_inode_from_fid(v9ses, fid, sb, true);
++ inode = v9fs_get_inode_from_fid(v9ses, fid, sb);
+ if (IS_ERR(inode)) {
+ retval = PTR_ERR(inode);
+ goto release_sb;
--- /dev/null
+From fedd06210b14febfa69e09d0721746749ea9ea20 Mon Sep 17 00:00:00 2001
+From: Dominique Martinet <asmadeus@codewreck.org>
+Date: Thu, 24 Oct 2024 08:52:11 +0900
+Subject: Revert "fs/9p: remove redundant pointer v9ses"
+
+From: Dominique Martinet <asmadeus@codewreck.org>
+
+commit fedd06210b14febfa69e09d0721746749ea9ea20 upstream.
+
+This reverts commit 10211b4a23cf4a3df5c11a10e5b3d371f16a906f.
+
+This is a requirement to revert commit 724a08450f74 ("fs/9p: simplify
+iget to remove unnecessary paths"), see that revert for details.
+
+Fixes: 724a08450f74 ("fs/9p: simplify iget to remove unnecessary paths")
+Reported-by: Will Deacon <will@kernel.org>
+Link: https://lkml.kernel.org/r/20240923100508.GA32066@willie-the-truck
+Cc: stable@vger.kernel.org # v6.9+
+Message-ID: <20241024-revert_iget-v1-2-4cac63d25f72@codewreck.org>
+Signed-off-by: Dominique Martinet <asmadeus@codewreck.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/9p/vfs_inode_dotl.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/fs/9p/vfs_inode_dotl.c b/fs/9p/vfs_inode_dotl.c
+index 55dde186041a..2b313fe7003e 100644
+--- a/fs/9p/vfs_inode_dotl.c
++++ b/fs/9p/vfs_inode_dotl.c
+@@ -297,6 +297,7 @@ static int v9fs_vfs_mkdir_dotl(struct mnt_idmap *idmap,
+ umode_t omode)
+ {
+ int err;
++ struct v9fs_session_info *v9ses;
+ struct p9_fid *fid = NULL, *dfid = NULL;
+ kgid_t gid;
+ const unsigned char *name;
+@@ -306,6 +307,7 @@ static int v9fs_vfs_mkdir_dotl(struct mnt_idmap *idmap,
+ struct posix_acl *dacl = NULL, *pacl = NULL;
+
+ p9_debug(P9_DEBUG_VFS, "name %pd\n", dentry);
++ v9ses = v9fs_inode2v9ses(dir);
+
+ omode |= S_IFDIR;
+ if (dir->i_mode & S_ISGID)
+@@ -737,6 +739,7 @@ v9fs_vfs_mknod_dotl(struct mnt_idmap *idmap, struct inode *dir,
+ kgid_t gid;
+ const unsigned char *name;
+ umode_t mode;
++ struct v9fs_session_info *v9ses;
+ struct p9_fid *fid = NULL, *dfid = NULL;
+ struct inode *inode;
+ struct p9_qid qid;
+@@ -746,6 +749,7 @@ v9fs_vfs_mknod_dotl(struct mnt_idmap *idmap, struct inode *dir,
+ dir->i_ino, dentry, omode,
+ MAJOR(rdev), MINOR(rdev));
+
++ v9ses = v9fs_inode2v9ses(dir);
+ dfid = v9fs_parent_fid(dentry);
+ if (IS_ERR(dfid)) {
+ err = PTR_ERR(dfid);
+--
+2.47.0
+
--- /dev/null
+From be2ca3825372085d669d322dccd0542a90e5b434 Mon Sep 17 00:00:00 2001
+From: Dominique Martinet <asmadeus@codewreck.org>
+Date: Thu, 24 Oct 2024 08:52:13 +0900
+Subject: Revert "fs/9p: simplify iget to remove unnecessary paths"
+
+From: Dominique Martinet <asmadeus@codewreck.org>
+
+commit be2ca3825372085d669d322dccd0542a90e5b434 upstream.
+
+This reverts commit 724a08450f74b02bd89078a596fd24857827c012.
+
+This code simplification introduced significant regressions on servers
+that do not remap inode numbers when exporting multiple underlying
+filesystems with colliding inodes, as can be illustrated with simple
+tmpfs exports in qemu with remapping disabled:
+```
+# host side
+cd /tmp/linux-test
+mkdir m1 m2
+mount -t tmpfs tmpfs m1
+mount -t tmpfs tmpfs m2
+mkdir m1/dir m2/dir
+echo foo > m1/dir/foo
+echo bar > m2/dir/bar
+
+# guest side
+# started with -virtfs local,path=/tmp/linux-test,mount_tag=tmp,security_model=mapped-file
+mount -t 9p -o trans=virtio,debug=1 tmp /mnt/t
+
+ls /mnt/t/m1/dir
+# foo
+ls /mnt/t/m2/dir
+# bar (works ok if directry isn't open)
+
+# cd to keep first dir's inode alive
+cd /mnt/t/m1/dir
+ls /mnt/t/m2/dir
+# foo (should be bar)
+```
+Other examples can be crafted with regular files with fscache enabled,
+in which case I/Os just happen to the wrong file leading to
+corruptions, or guest failing to boot with:
+ | VFS: Lookup of 'com.android.runtime' in 9p 9p would have caused loop
+
+In theory, we'd want the servers to be smart enough and ensure they
+never send us two different files with the same 'qid.path', but while
+qemu has an option to remap that is recommended (and qemu prints a
+warning if this case happens), there are many other servers which do
+not (kvmtool, nfs-ganesha, probably diod...), we should at least ensure
+we don't cause regressions on this:
+- assume servers can't be trusted and operations that should get a 'new'
+inode properly do so. commit d05dcfdf5e16 (" fs/9p: mitigate inode
+collisions") attempted to do this, but v9fs_fid_iget_dotl() was not
+called so some higher level of caching got in the way; this needs to be
+fixed properly before we can re-apply the patches.
+- if we ever want to really simplify this code, we will need to add some
+negotiation with the server at mount time where the server could claim
+they handle this properly, at which point we could optimize this out.
+(but that might not be needed at all if we properly handle the 'new'
+check?)
+
+Fixes: 724a08450f74 ("fs/9p: simplify iget to remove unnecessary paths")
+Reported-by: Will Deacon <will@kernel.org>
+Link: https://lore.kernel.org/all/20240408141436.GA17022@redhat.com/
+Link: https://lkml.kernel.org/r/20240923100508.GA32066@willie-the-truck
+Cc: stable@vger.kernel.org # v6.9+
+Message-ID: <20241024-revert_iget-v1-4-4cac63d25f72@codewreck.org>
+Signed-off-by: Dominique Martinet <asmadeus@codewreck.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/9p/v9fs.h | 31 +++++++++++++--
+ fs/9p/v9fs_vfs.h | 2 -
+ fs/9p/vfs_inode.c | 98 ++++++++++++++++++++++++++++++++++++++-----------
+ fs/9p/vfs_inode_dotl.c | 92 +++++++++++++++++++++++++++++++++++++---------
+ fs/9p/vfs_super.c | 2 -
+ 5 files changed, 180 insertions(+), 45 deletions(-)
+
+--- a/fs/9p/v9fs.h
++++ b/fs/9p/v9fs.h
+@@ -179,13 +179,16 @@ extern int v9fs_vfs_rename(struct mnt_id
+ struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry,
+ unsigned int flags);
+-extern struct inode *v9fs_fid_iget(struct super_block *sb, struct p9_fid *fid);
++extern struct inode *v9fs_inode_from_fid(struct v9fs_session_info *v9ses,
++ struct p9_fid *fid,
++ struct super_block *sb, int new);
+ extern const struct inode_operations v9fs_dir_inode_operations_dotl;
+ extern const struct inode_operations v9fs_file_inode_operations_dotl;
+ extern const struct inode_operations v9fs_symlink_inode_operations_dotl;
+ extern const struct netfs_request_ops v9fs_req_ops;
+-extern struct inode *v9fs_fid_iget_dotl(struct super_block *sb,
+- struct p9_fid *fid);
++extern struct inode *v9fs_inode_from_fid_dotl(struct v9fs_session_info *v9ses,
++ struct p9_fid *fid,
++ struct super_block *sb, int new);
+
+ /* other default globals */
+ #define V9FS_PORT 564
+@@ -227,9 +230,27 @@ v9fs_get_inode_from_fid(struct v9fs_sess
+ struct super_block *sb)
+ {
+ if (v9fs_proto_dotl(v9ses))
+- return v9fs_fid_iget_dotl(sb, fid);
++ return v9fs_inode_from_fid_dotl(v9ses, fid, sb, 0);
+ else
+- return v9fs_fid_iget(sb, fid);
++ return v9fs_inode_from_fid(v9ses, fid, sb, 0);
++}
++
++/**
++ * v9fs_get_new_inode_from_fid - Helper routine to populate an inode by
++ * issuing a attribute request
++ * @v9ses: session information
++ * @fid: fid to issue attribute request for
++ * @sb: superblock on which to create inode
++ *
++ */
++static inline struct inode *
++v9fs_get_new_inode_from_fid(struct v9fs_session_info *v9ses, struct p9_fid *fid,
++ struct super_block *sb)
++{
++ if (v9fs_proto_dotl(v9ses))
++ return v9fs_inode_from_fid_dotl(v9ses, fid, sb, 1);
++ else
++ return v9fs_inode_from_fid(v9ses, fid, sb, 1);
+ }
+
+ #endif
+--- a/fs/9p/v9fs_vfs.h
++++ b/fs/9p/v9fs_vfs.h
+@@ -42,7 +42,7 @@ struct inode *v9fs_alloc_inode(struct su
+ void v9fs_free_inode(struct inode *inode);
+ void v9fs_set_netfs_context(struct inode *inode);
+ int v9fs_init_inode(struct v9fs_session_info *v9ses,
+- struct inode *inode, struct p9_qid *qid, umode_t mode, dev_t rdev);
++ struct inode *inode, umode_t mode, dev_t rdev);
+ void v9fs_evict_inode(struct inode *inode);
+ #if (BITS_PER_LONG == 32)
+ #define QID2INO(q) ((ino_t) (((q)->path+2) ^ (((q)->path) >> 32)))
+--- a/fs/9p/vfs_inode.c
++++ b/fs/9p/vfs_inode.c
+@@ -256,12 +256,9 @@ void v9fs_set_netfs_context(struct inode
+ }
+
+ int v9fs_init_inode(struct v9fs_session_info *v9ses,
+- struct inode *inode, struct p9_qid *qid, umode_t mode, dev_t rdev)
++ struct inode *inode, umode_t mode, dev_t rdev)
+ {
+ int err = 0;
+- struct v9fs_inode *v9inode = V9FS_I(inode);
+-
+- memcpy(&v9inode->qid, qid, sizeof(struct p9_qid));
+
+ inode_init_owner(&nop_mnt_idmap, inode, NULL, mode);
+ inode->i_blocks = 0;
+@@ -365,40 +362,80 @@ void v9fs_evict_inode(struct inode *inod
+ clear_inode(inode);
+ }
+
+-struct inode *v9fs_fid_iget(struct super_block *sb, struct p9_fid *fid)
++static int v9fs_test_inode(struct inode *inode, void *data)
++{
++ int umode;
++ dev_t rdev;
++ struct v9fs_inode *v9inode = V9FS_I(inode);
++ struct p9_wstat *st = (struct p9_wstat *)data;
++ struct v9fs_session_info *v9ses = v9fs_inode2v9ses(inode);
++
++ umode = p9mode2unixmode(v9ses, st, &rdev);
++ /* don't match inode of different type */
++ if (inode_wrong_type(inode, umode))
++ return 0;
++
++ /* compare qid details */
++ if (memcmp(&v9inode->qid.version,
++ &st->qid.version, sizeof(v9inode->qid.version)))
++ return 0;
++
++ if (v9inode->qid.type != st->qid.type)
++ return 0;
++
++ if (v9inode->qid.path != st->qid.path)
++ return 0;
++ return 1;
++}
++
++static int v9fs_test_new_inode(struct inode *inode, void *data)
++{
++ return 0;
++}
++
++static int v9fs_set_inode(struct inode *inode, void *data)
++{
++ struct v9fs_inode *v9inode = V9FS_I(inode);
++ struct p9_wstat *st = (struct p9_wstat *)data;
++
++ memcpy(&v9inode->qid, &st->qid, sizeof(st->qid));
++ return 0;
++}
++
++static struct inode *v9fs_qid_iget(struct super_block *sb,
++ struct p9_qid *qid,
++ struct p9_wstat *st,
++ int new)
+ {
+ dev_t rdev;
+ int retval;
+ umode_t umode;
+ struct inode *inode;
+- struct p9_wstat *st;
+ struct v9fs_session_info *v9ses = sb->s_fs_info;
++ int (*test)(struct inode *inode, void *data);
++
++ if (new)
++ test = v9fs_test_new_inode;
++ else
++ test = v9fs_test_inode;
+
+- inode = iget_locked(sb, QID2INO(&fid->qid));
+- if (unlikely(!inode))
++ inode = iget5_locked(sb, QID2INO(qid), test, v9fs_set_inode, st);
++ if (!inode)
+ return ERR_PTR(-ENOMEM);
+ if (!(inode->i_state & I_NEW))
+ return inode;
+-
+ /*
+ * initialize the inode with the stat info
+ * FIXME!! we may need support for stale inodes
+ * later.
+ */
+- st = p9_client_stat(fid);
+- if (IS_ERR(st)) {
+- retval = PTR_ERR(st);
+- goto error;
+- }
+-
++ inode->i_ino = QID2INO(qid);
+ umode = p9mode2unixmode(v9ses, st, &rdev);
+- retval = v9fs_init_inode(v9ses, inode, &fid->qid, umode, rdev);
+- v9fs_stat2inode(st, inode, sb, 0);
+- p9stat_free(st);
+- kfree(st);
++ retval = v9fs_init_inode(v9ses, inode, umode, rdev);
+ if (retval)
+ goto error;
+
++ v9fs_stat2inode(st, inode, sb, 0);
+ v9fs_set_netfs_context(inode);
+ v9fs_cache_inode_get_cookie(inode);
+ unlock_new_inode(inode);
+@@ -409,6 +446,23 @@ error:
+
+ }
+
++struct inode *
++v9fs_inode_from_fid(struct v9fs_session_info *v9ses, struct p9_fid *fid,
++ struct super_block *sb, int new)
++{
++ struct p9_wstat *st;
++ struct inode *inode = NULL;
++
++ st = p9_client_stat(fid);
++ if (IS_ERR(st))
++ return ERR_CAST(st);
++
++ inode = v9fs_qid_iget(sb, &st->qid, st, new);
++ p9stat_free(st);
++ kfree(st);
++ return inode;
++}
++
+ /**
+ * v9fs_at_to_dotl_flags- convert Linux specific AT flags to
+ * plan 9 AT flag.
+@@ -555,7 +609,7 @@ v9fs_create(struct v9fs_session_info *v9
+ /*
+ * instantiate inode and assign the unopened fid to the dentry
+ */
+- inode = v9fs_get_inode_from_fid(v9ses, fid, dir->i_sb);
++ inode = v9fs_get_new_inode_from_fid(v9ses, fid, dir->i_sb);
+ if (IS_ERR(inode)) {
+ err = PTR_ERR(inode);
+ p9_debug(P9_DEBUG_VFS,
+@@ -683,8 +737,10 @@ struct dentry *v9fs_vfs_lookup(struct in
+ inode = NULL;
+ else if (IS_ERR(fid))
+ inode = ERR_CAST(fid);
+- else
++ else if (v9ses->cache & (CACHE_META|CACHE_LOOSE))
+ inode = v9fs_get_inode_from_fid(v9ses, fid, dir->i_sb);
++ else
++ inode = v9fs_get_new_inode_from_fid(v9ses, fid, dir->i_sb);
+ /*
+ * If we had a rename on the server and a parallel lookup
+ * for the new name, then make sure we instantiate with
+--- a/fs/9p/vfs_inode_dotl.c
++++ b/fs/9p/vfs_inode_dotl.c
+@@ -52,33 +52,76 @@ static kgid_t v9fs_get_fsgid_for_create(
+ return current_fsgid();
+ }
+
+-struct inode *v9fs_fid_iget_dotl(struct super_block *sb, struct p9_fid *fid)
++static int v9fs_test_inode_dotl(struct inode *inode, void *data)
++{
++ struct v9fs_inode *v9inode = V9FS_I(inode);
++ struct p9_stat_dotl *st = (struct p9_stat_dotl *)data;
++
++ /* don't match inode of different type */
++ if (inode_wrong_type(inode, st->st_mode))
++ return 0;
++
++ if (inode->i_generation != st->st_gen)
++ return 0;
++
++ /* compare qid details */
++ if (memcmp(&v9inode->qid.version,
++ &st->qid.version, sizeof(v9inode->qid.version)))
++ return 0;
++
++ if (v9inode->qid.type != st->qid.type)
++ return 0;
++
++ if (v9inode->qid.path != st->qid.path)
++ return 0;
++ return 1;
++}
++
++/* Always get a new inode */
++static int v9fs_test_new_inode_dotl(struct inode *inode, void *data)
++{
++ return 0;
++}
++
++static int v9fs_set_inode_dotl(struct inode *inode, void *data)
++{
++ struct v9fs_inode *v9inode = V9FS_I(inode);
++ struct p9_stat_dotl *st = (struct p9_stat_dotl *)data;
++
++ memcpy(&v9inode->qid, &st->qid, sizeof(st->qid));
++ inode->i_generation = st->st_gen;
++ return 0;
++}
++
++static struct inode *v9fs_qid_iget_dotl(struct super_block *sb,
++ struct p9_qid *qid,
++ struct p9_fid *fid,
++ struct p9_stat_dotl *st,
++ int new)
+ {
+ int retval;
+ struct inode *inode;
+- struct p9_stat_dotl *st;
+ struct v9fs_session_info *v9ses = sb->s_fs_info;
++ int (*test)(struct inode *inode, void *data);
+
+- inode = iget_locked(sb, QID2INO(&fid->qid));
+- if (unlikely(!inode))
++ if (new)
++ test = v9fs_test_new_inode_dotl;
++ else
++ test = v9fs_test_inode_dotl;
++
++ inode = iget5_locked(sb, QID2INO(qid), test, v9fs_set_inode_dotl, st);
++ if (!inode)
+ return ERR_PTR(-ENOMEM);
+ if (!(inode->i_state & I_NEW))
+ return inode;
+-
+ /*
+ * initialize the inode with the stat info
+ * FIXME!! we may need support for stale inodes
+ * later.
+ */
+- st = p9_client_getattr_dotl(fid, P9_STATS_BASIC | P9_STATS_GEN);
+- if (IS_ERR(st)) {
+- retval = PTR_ERR(st);
+- goto error;
+- }
+-
+- retval = v9fs_init_inode(v9ses, inode, &fid->qid,
++ inode->i_ino = QID2INO(qid);
++ retval = v9fs_init_inode(v9ses, inode,
+ st->st_mode, new_decode_dev(st->st_rdev));
+- kfree(st);
+ if (retval)
+ goto error;
+
+@@ -90,7 +133,6 @@ struct inode *v9fs_fid_iget_dotl(struct
+ goto error;
+
+ unlock_new_inode(inode);
+-
+ return inode;
+ error:
+ iget_failed(inode);
+@@ -98,6 +140,22 @@ error:
+
+ }
+
++struct inode *
++v9fs_inode_from_fid_dotl(struct v9fs_session_info *v9ses, struct p9_fid *fid,
++ struct super_block *sb, int new)
++{
++ struct p9_stat_dotl *st;
++ struct inode *inode = NULL;
++
++ st = p9_client_getattr_dotl(fid, P9_STATS_BASIC | P9_STATS_GEN);
++ if (IS_ERR(st))
++ return ERR_CAST(st);
++
++ inode = v9fs_qid_iget_dotl(sb, &st->qid, fid, st, new);
++ kfree(st);
++ return inode;
++}
++
+ struct dotl_openflag_map {
+ int open_flag;
+ int dotl_flag;
+@@ -247,7 +305,7 @@ v9fs_vfs_atomic_open_dotl(struct inode *
+ p9_debug(P9_DEBUG_VFS, "p9_client_walk failed %d\n", err);
+ goto out;
+ }
+- inode = v9fs_fid_iget_dotl(dir->i_sb, fid);
++ inode = v9fs_get_new_inode_from_fid(v9ses, fid, dir->i_sb);
+ if (IS_ERR(inode)) {
+ err = PTR_ERR(inode);
+ p9_debug(P9_DEBUG_VFS, "inode creation failed %d\n", err);
+@@ -342,7 +400,7 @@ static int v9fs_vfs_mkdir_dotl(struct mn
+ }
+
+ /* instantiate inode and assign the unopened fid to the dentry */
+- inode = v9fs_fid_iget_dotl(dir->i_sb, fid);
++ inode = v9fs_get_new_inode_from_fid(v9ses, fid, dir->i_sb);
+ if (IS_ERR(inode)) {
+ err = PTR_ERR(inode);
+ p9_debug(P9_DEBUG_VFS, "inode creation failed %d\n",
+@@ -780,7 +838,7 @@ v9fs_vfs_mknod_dotl(struct mnt_idmap *id
+ err);
+ goto error;
+ }
+- inode = v9fs_fid_iget_dotl(dir->i_sb, fid);
++ inode = v9fs_get_new_inode_from_fid(v9ses, fid, dir->i_sb);
+ if (IS_ERR(inode)) {
+ err = PTR_ERR(inode);
+ p9_debug(P9_DEBUG_VFS, "inode creation failed %d\n",
+--- a/fs/9p/vfs_super.c
++++ b/fs/9p/vfs_super.c
+@@ -139,7 +139,7 @@ static struct dentry *v9fs_mount(struct
+ else
+ sb->s_d_op = &v9fs_dentry_operations;
+
+- inode = v9fs_get_inode_from_fid(v9ses, fid, sb);
++ inode = v9fs_get_new_inode_from_fid(v9ses, fid, sb);
+ if (IS_ERR(inode)) {
+ retval = PTR_ERR(inode);
+ goto release_sb;
platform-x86-intel-pmc-fix-pmc_core_iounmap-to-call-.patch
fgraph-fix-missing-unlock-in-register_ftrace_graph.patch
fgraph-change-the-name-of-cpuhp-state-to-fgraph-onli.patch
+net-phy-dp83822-fix-reset-pin-definitions.patch
+nfsd-fix-race-between-laundromat-and-free_stateid.patch
+block-fix-sanity-checks-in-blk_rq_map_user_bvec.patch
+drm-amd-display-temp-w-a-for-dp-link-layer-compliance.patch
+ata-libata-set-did_time_out-for-commands-that-actually-timed-out.patch
+asoc-sof-intel-hda-loader-do-not-wait-for-hdaudio-ioc.patch
+asoc-sof-intel-hda-handle-prepare-without-close-for-non-hda-dai-s.patch
+asoc-sof-intel-hda-always-clean-up-link-dma-during-stop.patch
+asoc-sof-ipc4-topology-do-not-set-alh-node_id-for-aggregated-dais.patch
+asoc-dapm-avoid-container_of-to-get-component.patch
+asoc-qcom-sc7280-fix-missing-soundwire-runtime-stream-alloc.patch
+asoc-qcom-sdm845-add-missing-soundwire-runtime-stream-alloc.patch
+asoc-qcom-fix-null-dereference-in-asoc_qcom_lpass_cpu_platform_probe.patch
+revert-fs-9p-mitigate-inode-collisions.patch
+revert-fs-9p-remove-redundant-pointer-v9ses.patch
+revert-fs-9p-fix-uaf-in-in-v9fs_stat2inode_dotl.patch
+revert-fs-9p-simplify-iget-to-remove-unnecessary-paths.patch
+soundwire-intel_ace2x-send-pdi-stream-number-during-prepare.patch
--- /dev/null
+From c78f1e15e46ac82607eed593b22992fd08644d96 Mon Sep 17 00:00:00 2001
+From: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
+Date: Wed, 16 Oct 2024 11:29:09 +0800
+Subject: soundwire: intel_ace2x: Send PDI stream number during prepare
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
+
+commit c78f1e15e46ac82607eed593b22992fd08644d96 upstream.
+
+In the case of a prepare callback after an xrun or when the PCM is
+restarted after a call to snd_pcm_drain/snd_pcm_drop, avoid
+reprogramming the SHIM registers but send the PDI stream number so that
+the link DMA data can be set. This is needed for the case that the DMA
+data is cleared when the PCM is stopped and restarted without being
+closed.
+
+Link: https://github.com/thesofproject/sof/issues/9502
+Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
+Reviewed-by: Péter Ujfalusi <peter.ujfalusi@linux.intel.com>
+Reviewed-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
+Signed-off-by: Bard Liao <yung-chuan.liao@linux.intel.com>
+Acked-by: Vinod Koul <vkoul@kernel.org>
+All: stable@vger.kernel.org # 6.10.x 6.11.x
+Link: https://patch.msgid.link/20241016032910.14601-4-yung-chuan.liao@linux.intel.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/soundwire/intel_ace2x.c | 19 ++++++-------------
+ 1 file changed, 6 insertions(+), 13 deletions(-)
+
+diff --git a/drivers/soundwire/intel_ace2x.c b/drivers/soundwire/intel_ace2x.c
+index fff312c6968d..4f3dd70d6a1a 100644
+--- a/drivers/soundwire/intel_ace2x.c
++++ b/drivers/soundwire/intel_ace2x.c
+@@ -376,11 +376,12 @@ static int intel_hw_params(struct snd_pcm_substream *substream,
+ static int intel_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+ {
++ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai);
+ struct sdw_intel *sdw = cdns_to_intel(cdns);
+ struct sdw_cdns_dai_runtime *dai_runtime;
++ struct snd_pcm_hw_params *hw_params;
+ int ch, dir;
+- int ret = 0;
+
+ dai_runtime = cdns->dai_runtime_array[dai->id];
+ if (!dai_runtime) {
+@@ -389,12 +390,8 @@ static int intel_prepare(struct snd_pcm_substream *substream,
+ return -EIO;
+ }
+
++ hw_params = &rtd->dpcm[substream->stream].hw_params;
+ if (dai_runtime->suspended) {
+- struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+- struct snd_pcm_hw_params *hw_params;
+-
+- hw_params = &rtd->dpcm[substream->stream].hw_params;
+-
+ dai_runtime->suspended = false;
+
+ /*
+@@ -415,15 +412,11 @@ static int intel_prepare(struct snd_pcm_substream *substream,
+ /* the SHIM will be configured in the callback functions */
+
+ sdw_cdns_config_stream(cdns, ch, dir, dai_runtime->pdi);
+-
+- /* Inform DSP about PDI stream number */
+- ret = intel_params_stream(sdw, substream, dai,
+- hw_params,
+- sdw->instance,
+- dai_runtime->pdi->intel_alh_id);
+ }
+
+- return ret;
++ /* Inform DSP about PDI stream number */
++ return intel_params_stream(sdw, substream, dai, hw_params, sdw->instance,
++ dai_runtime->pdi->intel_alh_id);
+ }
+
+ static int
+--
+2.47.0
+