]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
Fixes for 5.15
authorSasha Levin <sashal@kernel.org>
Fri, 12 May 2023 01:49:27 +0000 (21:49 -0400)
committerSasha Levin <sashal@kernel.org>
Fri, 12 May 2023 01:49:27 +0000 (21:49 -0400)
Signed-off-by: Sasha Levin <sashal@kernel.org>
queue-5.15/asoc-soc-pcm-align-be-atomicity-with-that-of-the-fe.patch [new file with mode: 0644]
queue-5.15/asoc-soc-pcm-fix-and-cleanup-dpcm-locking.patch [new file with mode: 0644]
queue-5.15/asoc-soc-pcm-fix-be-handling-of-pause_release.patch [new file with mode: 0644]
queue-5.15/asoc-soc-pcm-serialize-be-triggers.patch [new file with mode: 0644]
queue-5.15/asoc-soc-pcm-test-refcount-before-triggering.patch [new file with mode: 0644]
queue-5.15/asoc-soc-pcm-use-gfp_atomic-for-dpcm-structure.patch [new file with mode: 0644]
queue-5.15/drm-hyperv-don-t-overwrite-dirt_needed-value-set-by-.patch [new file with mode: 0644]
queue-5.15/fs-ntfs3-fix-null-ptr-deref-on-inode-i_op-in-ntfs_lo.patch [new file with mode: 0644]
queue-5.15/series

diff --git a/queue-5.15/asoc-soc-pcm-align-be-atomicity-with-that-of-the-fe.patch b/queue-5.15/asoc-soc-pcm-align-be-atomicity-with-that-of-the-fe.patch
new file mode 100644 (file)
index 0000000..9e6904a
--- /dev/null
@@ -0,0 +1,71 @@
+From 15b0d8aaea3eee8dcd37352d6bbb3effdb269bf3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 7 Dec 2021 11:37:41 -0600
+Subject: ASoC: soc-pcm: align BE 'atomicity' with that of the FE
+
+From: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
+
+[ Upstream commit bbf7d3b1c4f40eb02dd1dffb500ba00b0bff0303 ]
+
+Since the flow for DPCM is based on taking a lock for the FE first, we
+need to make sure during the connection between a BE and an FE that
+they both use the same 'atomicity', otherwise we may sleep in atomic
+context.
+
+If the FE is nonatomic, this patch forces the BE to be nonatomic as
+well. That should have no negative impact since the BE 'inherits' the
+FE properties.
+
+However, if the FE is atomic and the BE is not, then the configuration
+is flagged as invalid.
+
+Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
+[ removed FE stream lock by tiwai ]
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Reviewed-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
+Reviewed-by: Bard Liao <yung-chuan.liao@linux.intel.com>
+Reviewed-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
+Link: https://lore.kernel.org/r/20211207173745.15850-3-pierre-louis.bossart@linux.intel.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ sound/soc/soc-pcm.c | 16 ++++++++++++++++
+ 1 file changed, 16 insertions(+)
+
+diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
+index a2b20526e7e2b..7bea8fa59f676 100644
+--- a/sound/soc/soc-pcm.c
++++ b/sound/soc/soc-pcm.c
+@@ -1123,6 +1123,8 @@ static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream)
+ static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe,
+               struct snd_soc_pcm_runtime *be, int stream)
+ {
++      struct snd_pcm_substream *fe_substream;
++      struct snd_pcm_substream *be_substream;
+       struct snd_soc_dpcm *dpcm;
+       unsigned long flags;
+@@ -1132,6 +1134,20 @@ static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe,
+                       return 0;
+       }
++      fe_substream = snd_soc_dpcm_get_substream(fe, stream);
++      be_substream = snd_soc_dpcm_get_substream(be, stream);
++
++      if (!fe_substream->pcm->nonatomic && be_substream->pcm->nonatomic) {
++              dev_err(be->dev, "%s: FE is atomic but BE is nonatomic, invalid configuration\n",
++                      __func__);
++              return -EINVAL;
++      }
++      if (fe_substream->pcm->nonatomic && !be_substream->pcm->nonatomic) {
++              dev_warn(be->dev, "%s: FE is nonatomic but BE is not, forcing BE as nonatomic\n",
++                       __func__);
++              be_substream->pcm->nonatomic = 1;
++      }
++
+       dpcm = kzalloc(sizeof(struct snd_soc_dpcm), GFP_ATOMIC);
+       if (!dpcm)
+               return -ENOMEM;
+-- 
+2.39.2
+
diff --git a/queue-5.15/asoc-soc-pcm-fix-and-cleanup-dpcm-locking.patch b/queue-5.15/asoc-soc-pcm-fix-and-cleanup-dpcm-locking.patch
new file mode 100644 (file)
index 0000000..dc9adeb
--- /dev/null
@@ -0,0 +1,760 @@
+From 11064cbb1d41902bcc8d8c89095fe224323c6472 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 7 Dec 2021 11:37:42 -0600
+Subject: ASoC: soc-pcm: Fix and cleanup DPCM locking
+
+From: Takashi Iwai <tiwai@suse.de>
+
+[ Upstream commit b7898396f4bbe160f546d0c5e9fa17cca9a7d153 ]
+
+The existing locking for DPCM has several issues
+a) a confusing mix of card->mutex and card->pcm_mutex.
+b) a dpcm_lock spinlock added inconsistently and on paths that could
+be recursively taken. The use of irqsave/irqrestore was also overkill.
+
+The suggested model is:
+
+1) The pcm_mutex is the top-most protection of BE links in the FE. The
+pcm_mutex is applied always on either the top PCM callbacks or the
+external call from DAPM, not taken in the internal functions.
+
+2) the FE stream lock is taken in higher levels before invoking
+dpcm_be_dai_trigger()
+
+3) when adding and deleting a BE, both the pcm_mutex and FE stream
+lock are taken.
+
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+[clarification of commit message by plbossart]
+Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
+Reviewed-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
+Reviewed-by: Bard Liao <yung-chuan.liao@linux.intel.com>
+Reviewed-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
+Link: https://lore.kernel.org/r/20211207173745.15850-4-pierre-louis.bossart@linux.intel.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/sound/soc.h  |   2 -
+ sound/soc/soc-core.c |   1 -
+ sound/soc/soc-pcm.c  | 229 ++++++++++++++++++++++++++++---------------
+ 3 files changed, 152 insertions(+), 80 deletions(-)
+
+diff --git a/include/sound/soc.h b/include/sound/soc.h
+index 8e6dd8a257c56..5872a8864f3b6 100644
+--- a/include/sound/soc.h
++++ b/include/sound/soc.h
+@@ -893,8 +893,6 @@ struct snd_soc_card {
+       struct mutex pcm_mutex;
+       enum snd_soc_pcm_subclass pcm_subclass;
+-      spinlock_t dpcm_lock;
+-
+       int (*probe)(struct snd_soc_card *card);
+       int (*late_probe)(struct snd_soc_card *card);
+       int (*remove)(struct snd_soc_card *card);
+diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
+index 1b1749b920f45..a5b3ee69fb886 100644
+--- a/sound/soc/soc-core.c
++++ b/sound/soc/soc-core.c
+@@ -2339,7 +2339,6 @@ int snd_soc_register_card(struct snd_soc_card *card)
+       mutex_init(&card->mutex);
+       mutex_init(&card->dapm_mutex);
+       mutex_init(&card->pcm_mutex);
+-      spin_lock_init(&card->dpcm_lock);
+       return snd_soc_bind_card(card);
+ }
+diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
+index 7bea8fa59f676..d26a1f12c513a 100644
+--- a/sound/soc/soc-pcm.c
++++ b/sound/soc/soc-pcm.c
+@@ -27,6 +27,31 @@
+ #include <sound/soc-link.h>
+ #include <sound/initval.h>
++static inline void snd_soc_dpcm_mutex_lock(struct snd_soc_pcm_runtime *rtd)
++{
++      mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass);
++}
++
++static inline void snd_soc_dpcm_mutex_unlock(struct snd_soc_pcm_runtime *rtd)
++{
++      mutex_unlock(&rtd->card->pcm_mutex);
++}
++
++#define snd_soc_dpcm_mutex_assert_held(rtd) \
++      lockdep_assert_held(&(rtd)->card->pcm_mutex)
++
++static inline void snd_soc_dpcm_stream_lock_irq(struct snd_soc_pcm_runtime *rtd,
++                                              int stream)
++{
++      snd_pcm_stream_lock_irq(snd_soc_dpcm_get_substream(rtd, stream));
++}
++
++static inline void snd_soc_dpcm_stream_unlock_irq(struct snd_soc_pcm_runtime *rtd,
++                                                int stream)
++{
++      snd_pcm_stream_unlock_irq(snd_soc_dpcm_get_substream(rtd, stream));
++}
++
+ #define DPCM_MAX_BE_USERS     8
+ static inline const char *soc_cpu_dai_name(struct snd_soc_pcm_runtime *rtd)
+@@ -73,7 +98,6 @@ static ssize_t dpcm_show_state(struct snd_soc_pcm_runtime *fe,
+       struct snd_pcm_hw_params *params = &fe->dpcm[stream].hw_params;
+       struct snd_soc_dpcm *dpcm;
+       ssize_t offset = 0;
+-      unsigned long flags;
+       /* FE state */
+       offset += scnprintf(buf + offset, size - offset,
+@@ -101,7 +125,6 @@ static ssize_t dpcm_show_state(struct snd_soc_pcm_runtime *fe,
+               goto out;
+       }
+-      spin_lock_irqsave(&fe->card->dpcm_lock, flags);
+       for_each_dpcm_be(fe, stream, dpcm) {
+               struct snd_soc_pcm_runtime *be = dpcm->be;
+               params = &dpcm->hw_params;
+@@ -122,7 +145,6 @@ static ssize_t dpcm_show_state(struct snd_soc_pcm_runtime *fe,
+                                          params_channels(params),
+                                          params_rate(params));
+       }
+-      spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
+ out:
+       return offset;
+ }
+@@ -145,11 +167,13 @@ static ssize_t dpcm_state_read_file(struct file *file, char __user *user_buf,
+       if (!buf)
+               return -ENOMEM;
++      snd_soc_dpcm_mutex_lock(fe);
+       for_each_pcm_streams(stream)
+               if (snd_soc_dai_stream_valid(asoc_rtd_to_cpu(fe, 0), stream))
+                       offset += dpcm_show_state(fe, stream,
+                                                 buf + offset,
+                                                 out_count - offset);
++      snd_soc_dpcm_mutex_unlock(fe);
+       ret = simple_read_from_buffer(user_buf, count, ppos, buf, offset);
+@@ -221,14 +245,14 @@ static void dpcm_set_fe_update_state(struct snd_soc_pcm_runtime *fe,
+       struct snd_pcm_substream *substream =
+               snd_soc_dpcm_get_substream(fe, stream);
+-      snd_pcm_stream_lock_irq(substream);
++      snd_soc_dpcm_stream_lock_irq(fe, stream);
+       if (state == SND_SOC_DPCM_UPDATE_NO && fe->dpcm[stream].trigger_pending) {
+               dpcm_fe_dai_do_trigger(substream,
+                                      fe->dpcm[stream].trigger_pending - 1);
+               fe->dpcm[stream].trigger_pending = 0;
+       }
+       fe->dpcm[stream].runtime_update = state;
+-      snd_pcm_stream_unlock_irq(substream);
++      snd_soc_dpcm_stream_unlock_irq(fe, stream);
+ }
+ static void dpcm_set_be_update_state(struct snd_soc_pcm_runtime *be,
+@@ -256,7 +280,7 @@ void snd_soc_runtime_action(struct snd_soc_pcm_runtime *rtd,
+       struct snd_soc_dai *dai;
+       int i;
+-      lockdep_assert_held(&rtd->card->pcm_mutex);
++      snd_soc_dpcm_mutex_assert_held(rtd);
+       for_each_rtd_dais(rtd, i, dai)
+               snd_soc_dai_action(dai, stream, action);
+@@ -309,6 +333,8 @@ int dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir,
+ {
+       struct snd_soc_dpcm *dpcm;
++      snd_soc_dpcm_mutex_assert_held(fe);
++
+       for_each_dpcm_be(fe, dir, dpcm) {
+               struct snd_soc_pcm_runtime *be = dpcm->be;
+@@ -646,14 +672,14 @@ static int soc_pcm_components_close(struct snd_pcm_substream *substream,
+       return ret;
+ }
+-static int soc_pcm_clean(struct snd_pcm_substream *substream, int rollback)
++static int soc_pcm_clean(struct snd_soc_pcm_runtime *rtd,
++                       struct snd_pcm_substream *substream, int rollback)
+ {
+-      struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+       struct snd_soc_component *component;
+       struct snd_soc_dai *dai;
+       int i;
+-      mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass);
++      snd_soc_dpcm_mutex_assert_held(rtd);
+       if (!rollback)
+               snd_soc_runtime_deactivate(rtd, substream->stream);
+@@ -665,9 +691,6 @@ static int soc_pcm_clean(struct snd_pcm_substream *substream, int rollback)
+       soc_pcm_components_close(substream, rollback);
+-
+-      mutex_unlock(&rtd->card->pcm_mutex);
+-
+       snd_soc_pcm_component_pm_runtime_put(rtd, substream, rollback);
+       for_each_rtd_components(rtd, i, component)
+@@ -682,9 +705,21 @@ static int soc_pcm_clean(struct snd_pcm_substream *substream, int rollback)
+  * freed here. The cpu DAI, codec DAI, machine and components are also
+  * shutdown.
+  */
++static int __soc_pcm_close(struct snd_soc_pcm_runtime *rtd,
++                         struct snd_pcm_substream *substream)
++{
++      return soc_pcm_clean(rtd, substream, 0);
++}
++
++/* PCM close ops for non-DPCM streams */
+ static int soc_pcm_close(struct snd_pcm_substream *substream)
+ {
+-      return soc_pcm_clean(substream, 0);
++      struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
++
++      snd_soc_dpcm_mutex_lock(rtd);
++      soc_pcm_clean(rtd, substream, 0);
++      snd_soc_dpcm_mutex_unlock(rtd);
++      return 0;
+ }
+ static int soc_hw_sanity_check(struct snd_pcm_substream *substream)
+@@ -730,21 +765,21 @@ static int soc_hw_sanity_check(struct snd_pcm_substream *substream)
+  * then initialized and any private data can be allocated. This also calls
+  * startup for the cpu DAI, component, machine and codec DAI.
+  */
+-static int soc_pcm_open(struct snd_pcm_substream *substream)
++static int __soc_pcm_open(struct snd_soc_pcm_runtime *rtd,
++                        struct snd_pcm_substream *substream)
+ {
+-      struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+       struct snd_soc_component *component;
+       struct snd_soc_dai *dai;
+       int i, ret = 0;
++      snd_soc_dpcm_mutex_assert_held(rtd);
++
+       for_each_rtd_components(rtd, i, component)
+               pinctrl_pm_select_default_state(component->dev);
+       ret = snd_soc_pcm_component_pm_runtime_get(rtd, substream);
+       if (ret < 0)
+-              goto pm_err;
+-
+-      mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass);
++              goto err;
+       ret = soc_pcm_components_open(substream);
+       if (ret < 0)
+@@ -786,16 +821,26 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
+       snd_soc_runtime_activate(rtd, substream->stream);
+       ret = 0;
+ err:
+-      mutex_unlock(&rtd->card->pcm_mutex);
+-pm_err:
+       if (ret < 0) {
+-              soc_pcm_clean(substream, 1);
++              soc_pcm_clean(rtd, substream, 1);
+               dev_err(rtd->dev, "%s() failed (%d)", __func__, ret);
+       }
+       return ret;
+ }
++/* PCM open ops for non-DPCM streams */
++static int soc_pcm_open(struct snd_pcm_substream *substream)
++{
++      struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
++      int ret;
++
++      snd_soc_dpcm_mutex_lock(rtd);
++      ret = __soc_pcm_open(rtd, substream);
++      snd_soc_dpcm_mutex_unlock(rtd);
++      return ret;
++}
++
+ static void codec2codec_close_delayed_work(struct snd_soc_pcm_runtime *rtd)
+ {
+       /*
+@@ -811,13 +856,13 @@ static void codec2codec_close_delayed_work(struct snd_soc_pcm_runtime *rtd)
+  * rate, etc.  This function is non atomic and can be called multiple times,
+  * it can refer to the runtime info.
+  */
+-static int soc_pcm_prepare(struct snd_pcm_substream *substream)
++static int __soc_pcm_prepare(struct snd_soc_pcm_runtime *rtd,
++                           struct snd_pcm_substream *substream)
+ {
+-      struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+       struct snd_soc_dai *dai;
+       int i, ret = 0;
+-      mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass);
++      snd_soc_dpcm_mutex_assert_held(rtd);
+       ret = snd_soc_link_prepare(substream);
+       if (ret < 0)
+@@ -845,14 +890,24 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
+               snd_soc_dai_digital_mute(dai, 0, substream->stream);
+ out:
+-      mutex_unlock(&rtd->card->pcm_mutex);
+-
+       if (ret < 0)
+               dev_err(rtd->dev, "ASoC: %s() failed (%d)\n", __func__, ret);
+       return ret;
+ }
++/* PCM prepare ops for non-DPCM streams */
++static int soc_pcm_prepare(struct snd_pcm_substream *substream)
++{
++      struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
++      int ret;
++
++      snd_soc_dpcm_mutex_lock(rtd);
++      ret = __soc_pcm_prepare(rtd, substream);
++      snd_soc_dpcm_mutex_unlock(rtd);
++      return ret;
++}
++
+ static void soc_pcm_codec_params_fixup(struct snd_pcm_hw_params *params,
+                                      unsigned int mask)
+ {
+@@ -864,13 +919,13 @@ static void soc_pcm_codec_params_fixup(struct snd_pcm_hw_params *params,
+       interval->max = channels;
+ }
+-static int soc_pcm_hw_clean(struct snd_pcm_substream *substream, int rollback)
++static int soc_pcm_hw_clean(struct snd_soc_pcm_runtime *rtd,
++                          struct snd_pcm_substream *substream, int rollback)
+ {
+-      struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+       struct snd_soc_dai *dai;
+       int i;
+-      mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass);
++      snd_soc_dpcm_mutex_assert_held(rtd);
+       /* clear the corresponding DAIs parameters when going to be inactive */
+       for_each_rtd_dais(rtd, i, dai) {
+@@ -900,16 +955,28 @@ static int soc_pcm_hw_clean(struct snd_pcm_substream *substream, int rollback)
+               snd_soc_dai_hw_free(dai, substream, rollback);
+       }
+-      mutex_unlock(&rtd->card->pcm_mutex);
+       return 0;
+ }
+ /*
+  * Frees resources allocated by hw_params, can be called multiple times
+  */
++static int __soc_pcm_hw_free(struct snd_soc_pcm_runtime *rtd,
++                           struct snd_pcm_substream *substream)
++{
++      return soc_pcm_hw_clean(rtd, substream, 0);
++}
++
++/* hw_free PCM ops for non-DPCM streams */
+ static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
+ {
+-      return soc_pcm_hw_clean(substream, 0);
++      struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
++      int ret;
++
++      snd_soc_dpcm_mutex_lock(rtd);
++      ret = __soc_pcm_hw_free(rtd, substream);
++      snd_soc_dpcm_mutex_unlock(rtd);
++      return ret;
+ }
+ /*
+@@ -917,15 +984,15 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
+  * function can also be called multiple times and can allocate buffers
+  * (using snd_pcm_lib_* ). It's non-atomic.
+  */
+-static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
+-                              struct snd_pcm_hw_params *params)
++static int __soc_pcm_hw_params(struct snd_soc_pcm_runtime *rtd,
++                             struct snd_pcm_substream *substream,
++                             struct snd_pcm_hw_params *params)
+ {
+-      struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+       struct snd_soc_dai *cpu_dai;
+       struct snd_soc_dai *codec_dai;
+       int i, ret = 0;
+-      mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass);
++      snd_soc_dpcm_mutex_assert_held(rtd);
+       ret = soc_pcm_params_symmetry(substream, params);
+       if (ret)
+@@ -997,16 +1064,27 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
+       ret = snd_soc_pcm_component_hw_params(substream, params);
+ out:
+-      mutex_unlock(&rtd->card->pcm_mutex);
+-
+       if (ret < 0) {
+-              soc_pcm_hw_clean(substream, 1);
++              soc_pcm_hw_clean(rtd, substream, 1);
+               dev_err(rtd->dev, "ASoC: %s() failed (%d)\n", __func__, ret);
+       }
+       return ret;
+ }
++/* hw_params PCM ops for non-DPCM streams */
++static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
++                           struct snd_pcm_hw_params *params)
++{
++      struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
++      int ret;
++
++      snd_soc_dpcm_mutex_lock(rtd);
++      ret = __soc_pcm_hw_params(rtd, substream, params);
++      snd_soc_dpcm_mutex_unlock(rtd);
++      return ret;
++}
++
+ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+ {
+       struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+@@ -1126,7 +1204,8 @@ static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe,
+       struct snd_pcm_substream *fe_substream;
+       struct snd_pcm_substream *be_substream;
+       struct snd_soc_dpcm *dpcm;
+-      unsigned long flags;
++
++      snd_soc_dpcm_mutex_assert_held(fe);
+       /* only add new dpcms */
+       for_each_dpcm_be(fe, stream, dpcm) {
+@@ -1156,10 +1235,10 @@ static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe,
+       dpcm->fe = fe;
+       be->dpcm[stream].runtime = fe->dpcm[stream].runtime;
+       dpcm->state = SND_SOC_DPCM_LINK_STATE_NEW;
+-      spin_lock_irqsave(&fe->card->dpcm_lock, flags);
++      snd_soc_dpcm_stream_lock_irq(fe, stream);
+       list_add(&dpcm->list_be, &fe->dpcm[stream].be_clients);
+       list_add(&dpcm->list_fe, &be->dpcm[stream].fe_clients);
+-      spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
++      snd_soc_dpcm_stream_unlock_irq(fe, stream);
+       dev_dbg(fe->dev, "connected new DPCM %s path %s %s %s\n",
+                       stream ? "capture" : "playback",  fe->dai_link->name,
+@@ -1204,8 +1283,10 @@ static void dpcm_be_reparent(struct snd_soc_pcm_runtime *fe,
+ void dpcm_be_disconnect(struct snd_soc_pcm_runtime *fe, int stream)
+ {
+       struct snd_soc_dpcm *dpcm, *d;
+-      unsigned long flags;
++      snd_soc_dpcm_mutex_assert_held(fe);
++
++      snd_soc_dpcm_stream_lock_irq(fe, stream);
+       for_each_dpcm_be_safe(fe, stream, dpcm, d) {
+               dev_dbg(fe->dev, "ASoC: BE %s disconnect check for %s\n",
+                               stream ? "capture" : "playback",
+@@ -1223,12 +1304,11 @@ void dpcm_be_disconnect(struct snd_soc_pcm_runtime *fe, int stream)
+               dpcm_remove_debugfs_state(dpcm);
+-              spin_lock_irqsave(&fe->card->dpcm_lock, flags);
+               list_del(&dpcm->list_be);
+               list_del(&dpcm->list_fe);
+-              spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
+               kfree(dpcm);
+       }
++      snd_soc_dpcm_stream_unlock_irq(fe, stream);
+ }
+ /* get BE for DAI widget and stream */
+@@ -1442,12 +1522,9 @@ int dpcm_process_paths(struct snd_soc_pcm_runtime *fe,
+ void dpcm_clear_pending_state(struct snd_soc_pcm_runtime *fe, int stream)
+ {
+       struct snd_soc_dpcm *dpcm;
+-      unsigned long flags;
+-      spin_lock_irqsave(&fe->card->dpcm_lock, flags);
+       for_each_dpcm_be(fe, stream, dpcm)
+               dpcm_set_be_update_state(dpcm->be, stream, SND_SOC_DPCM_UPDATE_NO);
+-      spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
+ }
+ void dpcm_be_dai_stop(struct snd_soc_pcm_runtime *fe, int stream,
+@@ -1483,12 +1560,12 @@ void dpcm_be_dai_stop(struct snd_soc_pcm_runtime *fe, int stream,
+                               continue;
+                       if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) {
+-                              soc_pcm_hw_free(be_substream);
++                              __soc_pcm_hw_free(be, be_substream);
+                               be->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE;
+                       }
+               }
+-              soc_pcm_close(be_substream);
++              __soc_pcm_close(be, be_substream);
+               be_substream->runtime = NULL;
+               be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
+       }
+@@ -1536,7 +1613,7 @@ int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream)
+                       stream ? "capture" : "playback", be->dai_link->name);
+               be_substream->runtime = be->dpcm[stream].runtime;
+-              err = soc_pcm_open(be_substream);
++              err = __soc_pcm_open(be, be_substream);
+               if (err < 0) {
+                       be->dpcm[stream].users--;
+                       if (be->dpcm[stream].users < 0)
+@@ -1784,7 +1861,7 @@ static int dpcm_fe_dai_startup(struct snd_pcm_substream *fe_substream)
+       dev_dbg(fe->dev, "ASoC: open FE %s\n", fe->dai_link->name);
+       /* start the DAI frontend */
+-      ret = soc_pcm_open(fe_substream);
++      ret = __soc_pcm_open(fe, fe_substream);
+       if (ret < 0)
+               goto unwind;
+@@ -1815,6 +1892,8 @@ static int dpcm_fe_dai_shutdown(struct snd_pcm_substream *substream)
+       struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(substream);
+       int stream = substream->stream;
++      snd_soc_dpcm_mutex_assert_held(fe);
++
+       dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
+       /* shutdown the BEs */
+@@ -1823,7 +1902,7 @@ static int dpcm_fe_dai_shutdown(struct snd_pcm_substream *substream)
+       dev_dbg(fe->dev, "ASoC: close FE %s\n", fe->dai_link->name);
+       /* now shutdown the frontend */
+-      soc_pcm_close(substream);
++      __soc_pcm_close(fe, substream);
+       /* run the stream stop event */
+       dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP);
+@@ -1868,7 +1947,7 @@ void dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream)
+               dev_dbg(be->dev, "ASoC: hw_free BE %s\n",
+                       be->dai_link->name);
+-              soc_pcm_hw_free(be_substream);
++              __soc_pcm_hw_free(be, be_substream);
+               be->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE;
+       }
+@@ -1879,13 +1958,13 @@ static int dpcm_fe_dai_hw_free(struct snd_pcm_substream *substream)
+       struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(substream);
+       int stream = substream->stream;
+-      mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
++      snd_soc_dpcm_mutex_lock(fe);
+       dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
+       dev_dbg(fe->dev, "ASoC: hw_free FE %s\n", fe->dai_link->name);
+       /* call hw_free on the frontend */
+-      soc_pcm_hw_free(substream);
++      soc_pcm_hw_clean(fe, substream, 0);
+       /* only hw_params backends that are either sinks or sources
+        * to this frontend DAI */
+@@ -1894,7 +1973,7 @@ static int dpcm_fe_dai_hw_free(struct snd_pcm_substream *substream)
+       fe->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE;
+       dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
+-      mutex_unlock(&fe->card->mutex);
++      snd_soc_dpcm_mutex_unlock(fe);
+       return 0;
+ }
+@@ -1938,7 +2017,7 @@ int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream)
+               dev_dbg(be->dev, "ASoC: hw_params BE %s\n",
+                       be->dai_link->name);
+-              ret = soc_pcm_hw_params(be_substream, &dpcm->hw_params);
++              ret = __soc_pcm_hw_params(be, be_substream, &dpcm->hw_params);
+               if (ret < 0)
+                       goto unwind;
+@@ -1968,7 +2047,7 @@ int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream)
+                  (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP))
+                       continue;
+-              soc_pcm_hw_free(be_substream);
++              __soc_pcm_hw_free(be, be_substream);
+       }
+       return ret;
+@@ -1980,7 +2059,7 @@ static int dpcm_fe_dai_hw_params(struct snd_pcm_substream *substream,
+       struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(substream);
+       int ret, stream = substream->stream;
+-      mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
++      snd_soc_dpcm_mutex_lock(fe);
+       dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
+       memcpy(&fe->dpcm[stream].hw_params, params,
+@@ -1994,7 +2073,7 @@ static int dpcm_fe_dai_hw_params(struct snd_pcm_substream *substream,
+                       params_channels(params), params_format(params));
+       /* call hw_params on the frontend */
+-      ret = soc_pcm_hw_params(substream, params);
++      ret = __soc_pcm_hw_params(fe, substream, params);
+       if (ret < 0)
+               dpcm_be_dai_hw_free(fe, stream);
+       else
+@@ -2002,7 +2081,7 @@ static int dpcm_fe_dai_hw_params(struct snd_pcm_substream *substream,
+ out:
+       dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
+-      mutex_unlock(&fe->card->mutex);
++      snd_soc_dpcm_mutex_unlock(fe);
+       if (ret < 0)
+               dev_err(fe->dev, "ASoC: %s failed (%d)\n", __func__, ret);
+@@ -2273,7 +2352,7 @@ int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream)
+               dev_dbg(be->dev, "ASoC: prepare BE %s\n",
+                       be->dai_link->name);
+-              ret = soc_pcm_prepare(be_substream);
++              ret = __soc_pcm_prepare(be, be_substream);
+               if (ret < 0)
+                       break;
+@@ -2291,7 +2370,7 @@ static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream)
+       struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(substream);
+       int stream = substream->stream, ret = 0;
+-      mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
++      snd_soc_dpcm_mutex_lock(fe);
+       dev_dbg(fe->dev, "ASoC: prepare FE %s\n", fe->dai_link->name);
+@@ -2310,7 +2389,7 @@ static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream)
+               goto out;
+       /* call prepare on the frontend */
+-      ret = soc_pcm_prepare(substream);
++      ret = __soc_pcm_prepare(fe, substream);
+       if (ret < 0)
+               goto out;
+@@ -2318,7 +2397,7 @@ static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream)
+ out:
+       dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
+-      mutex_unlock(&fe->card->mutex);
++      snd_soc_dpcm_mutex_unlock(fe);
+       if (ret < 0)
+               dev_err(fe->dev, "ASoC: %s() failed (%d)\n", __func__, ret);
+@@ -2369,7 +2448,6 @@ static int dpcm_run_update_startup(struct snd_soc_pcm_runtime *fe, int stream)
+       struct snd_soc_dpcm *dpcm;
+       enum snd_soc_dpcm_trigger trigger = fe->dai_link->trigger[stream];
+       int ret = 0;
+-      unsigned long flags;
+       dev_dbg(fe->dev, "ASoC: runtime %s open on FE %s\n",
+                       stream ? "capture" : "playback", fe->dai_link->name);
+@@ -2438,7 +2516,6 @@ static int dpcm_run_update_startup(struct snd_soc_pcm_runtime *fe, int stream)
+       dpcm_be_dai_shutdown(fe, stream);
+ disconnect:
+       /* disconnect any pending BEs */
+-      spin_lock_irqsave(&fe->card->dpcm_lock, flags);
+       for_each_dpcm_be(fe, stream, dpcm) {
+               struct snd_soc_pcm_runtime *be = dpcm->be;
+@@ -2450,7 +2527,6 @@ static int dpcm_run_update_startup(struct snd_soc_pcm_runtime *fe, int stream)
+                       be->dpcm[stream].state == SND_SOC_DPCM_STATE_NEW)
+                               dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE;
+       }
+-      spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
+       if (ret < 0)
+               dev_err(fe->dev, "ASoC: %s() failed (%d)\n", __func__, ret);
+@@ -2525,7 +2601,7 @@ int snd_soc_dpcm_runtime_update(struct snd_soc_card *card)
+       struct snd_soc_pcm_runtime *fe;
+       int ret = 0;
+-      mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
++      mutex_lock_nested(&card->pcm_mutex, card->pcm_subclass);
+       /* shutdown all old paths first */
+       for_each_card_rtds(card, fe) {
+               ret = soc_dpcm_fe_runtime_update(fe, 0);
+@@ -2541,7 +2617,7 @@ int snd_soc_dpcm_runtime_update(struct snd_soc_card *card)
+       }
+ out:
+-      mutex_unlock(&card->mutex);
++      mutex_unlock(&card->pcm_mutex);
+       return ret;
+ }
+ EXPORT_SYMBOL_GPL(snd_soc_dpcm_runtime_update);
+@@ -2552,6 +2628,8 @@ static void dpcm_fe_dai_cleanup(struct snd_pcm_substream *fe_substream)
+       struct snd_soc_dpcm *dpcm;
+       int stream = fe_substream->stream;
++      snd_soc_dpcm_mutex_assert_held(fe);
++
+       /* mark FE's links ready to prune */
+       for_each_dpcm_be(fe, stream, dpcm)
+               dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE;
+@@ -2566,12 +2644,12 @@ static int dpcm_fe_dai_close(struct snd_pcm_substream *fe_substream)
+       struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(fe_substream);
+       int ret;
+-      mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
++      snd_soc_dpcm_mutex_lock(fe);
+       ret = dpcm_fe_dai_shutdown(fe_substream);
+       dpcm_fe_dai_cleanup(fe_substream);
+-      mutex_unlock(&fe->card->mutex);
++      snd_soc_dpcm_mutex_unlock(fe);
+       return ret;
+ }
+@@ -2582,7 +2660,7 @@ static int dpcm_fe_dai_open(struct snd_pcm_substream *fe_substream)
+       int ret;
+       int stream = fe_substream->stream;
+-      mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
++      snd_soc_dpcm_mutex_lock(fe);
+       fe->dpcm[stream].runtime = fe_substream->runtime;
+       ret = dpcm_path_get(fe, stream, &list);
+@@ -2599,7 +2677,7 @@ static int dpcm_fe_dai_open(struct snd_pcm_substream *fe_substream)
+       dpcm_clear_pending_state(fe, stream);
+       dpcm_path_put(&list);
+ open_end:
+-      mutex_unlock(&fe->card->mutex);
++      snd_soc_dpcm_mutex_unlock(fe);
+       return ret;
+ }
+@@ -2860,10 +2938,8 @@ static int snd_soc_dpcm_check_state(struct snd_soc_pcm_runtime *fe,
+       struct snd_soc_dpcm *dpcm;
+       int state;
+       int ret = 1;
+-      unsigned long flags;
+       int i;
+-      spin_lock_irqsave(&fe->card->dpcm_lock, flags);
+       for_each_dpcm_fe(be, stream, dpcm) {
+               if (dpcm->fe == fe)
+@@ -2877,7 +2953,6 @@ static int snd_soc_dpcm_check_state(struct snd_soc_pcm_runtime *fe,
+                       }
+               }
+       }
+-      spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
+       /* it's safe to do this BE DAI */
+       return ret;
+-- 
+2.39.2
+
diff --git a/queue-5.15/asoc-soc-pcm-fix-be-handling-of-pause_release.patch b/queue-5.15/asoc-soc-pcm-fix-be-handling-of-pause_release.patch
new file mode 100644 (file)
index 0000000..075ee7c
--- /dev/null
@@ -0,0 +1,78 @@
+From e41e7485d3d6c95a71a1d0fb6c525b32b82ab61a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 7 Dec 2021 11:37:45 -0600
+Subject: ASoC: soc-pcm: fix BE handling of PAUSE_RELEASE
+
+From: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
+
+[ Upstream commit 3aa1e96a2b95e2ece198f8dd01e96818971b84df ]
+
+A BE connected to more than one FE, e.g. in a mixer case, can go
+through the following transitions.
+
+play FE1    -> BE state is START
+pause FE1   -> BE state is PAUSED
+play FE2    -> BE state is START
+stop FE2    -> BE state is STOP (see note [1] below)
+release FE1 -> BE state is START
+stop FE1    -> BE state is STOP
+
+play FE1    -> BE state is START
+pause FE1   -> BE state is PAUSED
+play FE2    -> BE state is START
+release FE1 -> BE state is START
+stop FE2    -> BE state is START
+stop FE1    -> BE state is STOP
+
+play FE1    -> BE state is START
+play FE2    -> BE state is START (no change)
+pause FE1   -> BE state is START (no change)
+pause FE2   -> BE state is PAUSED
+release FE1 -> BE state is START
+release FE2 -> BE state is START (no change)
+stop FE1    -> BE state is START (no change)
+stop FE2    -> BE state is STOP
+
+The existing code for PAUSE_RELEASE only allows for the case where the
+BE is paused, which clearly would not work in the sequences above.
+
+Extend the allowed states to restart the BE when PAUSE_RELEASE is
+received, and increase the refcount if the BE is already in START.
+
+[1] the existing logic does not move the BE state back to PAUSED when
+the FE2 is stopped. This patch does not change the logic; it would be
+painful to keep a history of changes on the FE side, the state machine
+is already rather complicated with transitions based on the last BE
+state and the trigger type.
+
+Reported-by: Bard Liao <bard.liao@intel.com>
+Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
+Reviewed-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
+Reviewed-by: Bard Liao <yung-chuan.liao@linux.intel.com>
+Reviewed-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
+Link: https://lore.kernel.org/r/20211207173745.15850-7-pierre-louis.bossart@linux.intel.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ sound/soc/soc-pcm.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
+index 83977a715a61d..7f96b7d4b3dac 100644
+--- a/sound/soc/soc-pcm.c
++++ b/sound/soc/soc-pcm.c
+@@ -2155,7 +2155,10 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
+                       be->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
+                       break;
+               case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+-                      if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED))
++                      if (!be->dpcm[stream].be_start &&
++                          (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START) &&
++                          (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP) &&
++                          (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED))
+                               goto next;
+                       be->dpcm[stream].be_start++;
+-- 
+2.39.2
+
diff --git a/queue-5.15/asoc-soc-pcm-serialize-be-triggers.patch b/queue-5.15/asoc-soc-pcm-serialize-be-triggers.patch
new file mode 100644 (file)
index 0000000..c7923ab
--- /dev/null
@@ -0,0 +1,176 @@
+From a35e54d4c8d72fa5da81e4350ad4eca1ed349cd6 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 7 Dec 2021 11:37:43 -0600
+Subject: ASoC: soc-pcm: serialize BE triggers
+
+From: Takashi Iwai <tiwai@suse.de>
+
+[ Upstream commit b2ae80663008a7662febe7d13f14ea1b2eb0cd51 ]
+
+When more than one FE is connected to a BE, e.g. in a mixing use case,
+the BE can be triggered multiple times when the FE are opened/started
+concurrently. This race condition is problematic in the case of
+SoundWire BE dailinks, and this is not desirable in a general
+case.
+
+This patch relies on the existing BE PCM lock, which takes atomicity into
+account. The locking model assumes that all interactions start with
+the FE, so that there is no deadlock between FE and BE locks.
+
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+[test, checkpatch fix and clarification of commit message by plbossart]
+Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
+Reviewed-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
+Reviewed-by: Bard Liao <yung-chuan.liao@linux.intel.com>
+Reviewed-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
+Link: https://lore.kernel.org/r/20211207173745.15850-5-pierre-louis.bossart@linux.intel.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ sound/soc/soc-pcm.c | 46 ++++++++++++++++++++++++++++-----------------
+ 1 file changed, 29 insertions(+), 17 deletions(-)
+
+diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
+index d26a1f12c513a..7c7ae69f9410d 100644
+--- a/sound/soc/soc-pcm.c
++++ b/sound/soc/soc-pcm.c
+@@ -46,12 +46,18 @@ static inline void snd_soc_dpcm_stream_lock_irq(struct snd_soc_pcm_runtime *rtd,
+       snd_pcm_stream_lock_irq(snd_soc_dpcm_get_substream(rtd, stream));
+ }
++#define snd_soc_dpcm_stream_lock_irqsave(rtd, stream, flags) \
++      snd_pcm_stream_lock_irqsave(snd_soc_dpcm_get_substream(rtd, stream), flags)
++
+ static inline void snd_soc_dpcm_stream_unlock_irq(struct snd_soc_pcm_runtime *rtd,
+                                                 int stream)
+ {
+       snd_pcm_stream_unlock_irq(snd_soc_dpcm_get_substream(rtd, stream));
+ }
++#define snd_soc_dpcm_stream_unlock_irqrestore(rtd, stream, flags) \
++      snd_pcm_stream_unlock_irqrestore(snd_soc_dpcm_get_substream(rtd, stream), flags)
++
+ #define DPCM_MAX_BE_USERS     8
+ static inline const char *soc_cpu_dai_name(struct snd_soc_pcm_runtime *rtd)
+@@ -2094,6 +2100,7 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
+ {
+       struct snd_soc_pcm_runtime *be;
+       struct snd_soc_dpcm *dpcm;
++      unsigned long flags;
+       int ret = 0;
+       for_each_dpcm_be(fe, stream, dpcm) {
+@@ -2102,9 +2109,11 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
+               be = dpcm->be;
+               be_substream = snd_soc_dpcm_get_substream(be, stream);
++              snd_soc_dpcm_stream_lock_irqsave(be, stream, flags);
++
+               /* is this op for this BE ? */
+               if (!snd_soc_dpcm_be_can_update(fe, be, stream))
+-                      continue;
++                      goto next;
+               dev_dbg(be->dev, "ASoC: trigger BE %s cmd %d\n",
+                       be->dai_link->name, cmd);
+@@ -2114,77 +2123,80 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
+                       if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) &&
+                           (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP) &&
+                           (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED))
+-                              continue;
++                              goto next;
+                       ret = soc_pcm_trigger(be_substream, cmd);
+                       if (ret)
+-                              goto end;
++                              goto next;
+                       be->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
+                       break;
+               case SNDRV_PCM_TRIGGER_RESUME:
+                       if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_SUSPEND))
+-                              continue;
++                              goto next;
+                       ret = soc_pcm_trigger(be_substream, cmd);
+                       if (ret)
+-                              goto end;
++                              goto next;
+                       be->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
+                       break;
+               case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+                       if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED))
+-                              continue;
++                              goto next;
+                       ret = soc_pcm_trigger(be_substream, cmd);
+                       if (ret)
+-                              goto end;
++                              goto next;
+                       be->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
+                       break;
+               case SNDRV_PCM_TRIGGER_STOP:
+                       if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_START) &&
+                           (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED))
+-                              continue;
++                              goto next;
+                       if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
+-                              continue;
++                              goto next;
+                       ret = soc_pcm_trigger(be_substream, cmd);
+                       if (ret)
+-                              goto end;
++                              goto next;
+                       be->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP;
+                       break;
+               case SNDRV_PCM_TRIGGER_SUSPEND:
+                       if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START)
+-                              continue;
++                              goto next;
+                       if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
+-                              continue;
++                              goto next;
+                       ret = soc_pcm_trigger(be_substream, cmd);
+                       if (ret)
+-                              goto end;
++                              goto next;
+                       be->dpcm[stream].state = SND_SOC_DPCM_STATE_SUSPEND;
+                       break;
+               case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+                       if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START)
+-                              continue;
++                              goto next;
+                       if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
+-                              continue;
++                              goto next;
+                       ret = soc_pcm_trigger(be_substream, cmd);
+                       if (ret)
+-                              goto end;
++                              goto next;
+                       be->dpcm[stream].state = SND_SOC_DPCM_STATE_PAUSED;
+                       break;
+               }
++next:
++              snd_soc_dpcm_stream_unlock_irqrestore(be, stream, flags);
++              if (ret)
++                      break;
+       }
+-end:
+       if (ret < 0)
+               dev_err(fe->dev, "ASoC: %s() failed at %s (%d)\n",
+                       __func__, be->dai_link->name, ret);
+-- 
+2.39.2
+
diff --git a/queue-5.15/asoc-soc-pcm-test-refcount-before-triggering.patch b/queue-5.15/asoc-soc-pcm-test-refcount-before-triggering.patch
new file mode 100644 (file)
index 0000000..f036594
--- /dev/null
@@ -0,0 +1,181 @@
+From c20826fcf2b6106dd37bb49d1c638791c0e0ac92 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 7 Dec 2021 11:37:44 -0600
+Subject: ASoC: soc-pcm: test refcount before triggering
+
+From: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
+
+[ Upstream commit 848aedfdc6ba25ad5652797db9266007773e44dd ]
+
+On start/pause_release/resume, when more than one FE is connected to
+the same BE, it's possible that the trigger is sent more than
+once. This is not desirable, we only want to trigger a BE once, which
+is straightforward to implement with a refcount.
+
+For stop/pause/suspend, the problem is more complicated: the check
+implemented in snd_soc_dpcm_can_be_free_stop() may fail due to a
+conceptual deadlock when we trigger the BE before the FE. In this
+case, the FE states have not yet changed, so there are corner cases
+where the TRIGGER_STOP is never sent - the dual case of start where
+multiple triggers might be sent.
+
+This patch suggests an unconditional trigger in all cases, without
+checking the FE states, using a refcount protected by the BE PCM
+stream lock.
+
+Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
+Reviewed-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
+Reviewed-by: Bard Liao <yung-chuan.liao@linux.intel.com>
+Reviewed-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
+Link: https://lore.kernel.org/r/20211207173745.15850-6-pierre-louis.bossart@linux.intel.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/sound/soc-dpcm.h |  2 ++
+ sound/soc/soc-pcm.c      | 53 +++++++++++++++++++++++++++++++---------
+ 2 files changed, 44 insertions(+), 11 deletions(-)
+
+diff --git a/include/sound/soc-dpcm.h b/include/sound/soc-dpcm.h
+index e296a3949b18b..d963f3b608489 100644
+--- a/include/sound/soc-dpcm.h
++++ b/include/sound/soc-dpcm.h
+@@ -101,6 +101,8 @@ struct snd_soc_dpcm_runtime {
+       enum snd_soc_dpcm_state state;
+       int trigger_pending; /* trigger cmd + 1 if pending, 0 if not */
++
++      int be_start; /* refcount protected by BE stream pcm lock */
+ };
+ #define for_each_dpcm_fe(be, stream, _dpcm)                           \
+diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
+index 7c7ae69f9410d..83977a715a61d 100644
+--- a/sound/soc/soc-pcm.c
++++ b/sound/soc/soc-pcm.c
+@@ -1630,7 +1630,7 @@ int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream)
+                       be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
+                       goto unwind;
+               }
+-
++              be->dpcm[stream].be_start = 0;
+               be->dpcm[stream].state = SND_SOC_DPCM_STATE_OPEN;
+               count++;
+       }
+@@ -2120,14 +2120,21 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
+               switch (cmd) {
+               case SNDRV_PCM_TRIGGER_START:
+-                      if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) &&
++                      if (!be->dpcm[stream].be_start &&
++                          (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) &&
+                           (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP) &&
+                           (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED))
+                               goto next;
++                      be->dpcm[stream].be_start++;
++                      if (be->dpcm[stream].be_start != 1)
++                              goto next;
++
+                       ret = soc_pcm_trigger(be_substream, cmd);
+-                      if (ret)
++                      if (ret) {
++                              be->dpcm[stream].be_start--;
+                               goto next;
++                      }
+                       be->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
+                       break;
+@@ -2135,9 +2142,15 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
+                       if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_SUSPEND))
+                               goto next;
++                      be->dpcm[stream].be_start++;
++                      if (be->dpcm[stream].be_start != 1)
++                              goto next;
++
+                       ret = soc_pcm_trigger(be_substream, cmd);
+-                      if (ret)
++                      if (ret) {
++                              be->dpcm[stream].be_start--;
+                               goto next;
++                      }
+                       be->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
+                       break;
+@@ -2145,9 +2158,15 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
+                       if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED))
+                               goto next;
++                      be->dpcm[stream].be_start++;
++                      if (be->dpcm[stream].be_start != 1)
++                              goto next;
++
+                       ret = soc_pcm_trigger(be_substream, cmd);
+-                      if (ret)
++                      if (ret) {
++                              be->dpcm[stream].be_start--;
+                               goto next;
++                      }
+                       be->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
+                       break;
+@@ -2156,12 +2175,18 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
+                           (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED))
+                               goto next;
+-                      if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
++                      if (be->dpcm[stream].state == SND_SOC_DPCM_STATE_START)
++                              be->dpcm[stream].be_start--;
++
++                      if (be->dpcm[stream].be_start != 0)
+                               goto next;
+                       ret = soc_pcm_trigger(be_substream, cmd);
+-                      if (ret)
++                      if (ret) {
++                              if (be->dpcm[stream].state == SND_SOC_DPCM_STATE_START)
++                                      be->dpcm[stream].be_start++;
+                               goto next;
++                      }
+                       be->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP;
+                       break;
+@@ -2169,12 +2194,15 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
+                       if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START)
+                               goto next;
+-                      if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
++                      be->dpcm[stream].be_start--;
++                      if (be->dpcm[stream].be_start != 0)
+                               goto next;
+                       ret = soc_pcm_trigger(be_substream, cmd);
+-                      if (ret)
++                      if (ret) {
++                              be->dpcm[stream].be_start++;
+                               goto next;
++                      }
+                       be->dpcm[stream].state = SND_SOC_DPCM_STATE_SUSPEND;
+                       break;
+@@ -2182,12 +2210,15 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
+                       if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START)
+                               goto next;
+-                      if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
++                      be->dpcm[stream].be_start--;
++                      if (be->dpcm[stream].be_start != 0)
+                               goto next;
+                       ret = soc_pcm_trigger(be_substream, cmd);
+-                      if (ret)
++                      if (ret) {
++                              be->dpcm[stream].be_start++;
+                               goto next;
++                      }
+                       be->dpcm[stream].state = SND_SOC_DPCM_STATE_PAUSED;
+                       break;
+-- 
+2.39.2
+
diff --git a/queue-5.15/asoc-soc-pcm-use-gfp_atomic-for-dpcm-structure.patch b/queue-5.15/asoc-soc-pcm-use-gfp_atomic-for-dpcm-structure.patch
new file mode 100644 (file)
index 0000000..b3ae87d
--- /dev/null
@@ -0,0 +1,41 @@
+From 364f2950d8dca8f867a74364e455c85076df19d1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 7 Dec 2021 11:37:40 -0600
+Subject: ASoC: soc-pcm: use GFP_ATOMIC for dpcm structure
+
+From: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
+
+[ Upstream commit d8a9c6e1f6766a16cf02b4e99a629f3c5512c183 ]
+
+We allocate a structure in dpcm_be_connect(), which may be called in
+atomic context. Using GFP_KERNEL is not quite right, we have to use
+GFP_ATOMIC to prevent the allocator from sleeping.
+
+Suggested-by: Guennadi Liakhovetski <guennadi.liakhovetski@linux.intel.com>
+Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
+Reviewed-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
+Reviewed-by: Bard Liao <yung-chuan.liao@linux.intel.com>
+Reviewed-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
+Link: https://lore.kernel.org/r/20211207173745.15850-2-pierre-louis.bossart@linux.intel.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ sound/soc/soc-pcm.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
+index 6f616ac4490f0..a2b20526e7e2b 100644
+--- a/sound/soc/soc-pcm.c
++++ b/sound/soc/soc-pcm.c
+@@ -1132,7 +1132,7 @@ static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe,
+                       return 0;
+       }
+-      dpcm = kzalloc(sizeof(struct snd_soc_dpcm), GFP_KERNEL);
++      dpcm = kzalloc(sizeof(struct snd_soc_dpcm), GFP_ATOMIC);
+       if (!dpcm)
+               return -ENOMEM;
+-- 
+2.39.2
+
diff --git a/queue-5.15/drm-hyperv-don-t-overwrite-dirt_needed-value-set-by-.patch b/queue-5.15/drm-hyperv-don-t-overwrite-dirt_needed-value-set-by-.patch
new file mode 100644 (file)
index 0000000..103c614
--- /dev/null
@@ -0,0 +1,42 @@
+From ce907e3637467bfd832a98cc1e8e1b60b150107b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 23 Sep 2022 10:09:50 +0000
+Subject: drm/hyperv: Don't overwrite dirt_needed value set by host
+
+From: Saurabh Sengar <ssengar@linux.microsoft.com>
+
+[ Upstream commit 19b5e6659eaf537ebeac90ae30c7df0296fe5ab9 ]
+
+Existing code is causing a race condition where dirt_needed value is
+already set by the host and gets overwritten with default value. Remove
+this default setting of dirt_needed, to avoid overwriting the value
+received in the channel callback set by vmbus_open. Removing this
+setting also means the default value for dirt_needed is changed to false
+as it's allocated by kzalloc which is similar to legacy hyperv_fb driver.
+
+Signed-off-by: Saurabh Sengar <ssengar@linux.microsoft.com>
+Reviewed-by: Dexuan Cui <decui@microsoft.com>
+Reviewed-by: Michael Kelley <mikelley@microsoft.com>
+Link: https://lore.kernel.org/r/1662996766-19304-1-git-send-email-ssengar@linux.microsoft.com
+Signed-off-by: Wei Liu <wei.liu@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/hyperv/hyperv_drm_drv.c | 2 --
+ 1 file changed, 2 deletions(-)
+
+diff --git a/drivers/gpu/drm/hyperv/hyperv_drm_drv.c b/drivers/gpu/drm/hyperv/hyperv_drm_drv.c
+index 00e53de4812bb..584d3a73db96c 100644
+--- a/drivers/gpu/drm/hyperv/hyperv_drm_drv.c
++++ b/drivers/gpu/drm/hyperv/hyperv_drm_drv.c
+@@ -198,8 +198,6 @@ static int hyperv_vmbus_probe(struct hv_device *hdev,
+       if (ret)
+               drm_warn(dev, "Failed to update vram location.\n");
+-      hv->dirt_needed = true;
+-
+       ret = hyperv_mode_config_init(hv);
+       if (ret)
+               goto err_vmbus_close;
+-- 
+2.39.2
+
diff --git a/queue-5.15/fs-ntfs3-fix-null-ptr-deref-on-inode-i_op-in-ntfs_lo.patch b/queue-5.15/fs-ntfs3-fix-null-ptr-deref-on-inode-i_op-in-ntfs_lo.patch
new file mode 100644 (file)
index 0000000..5924af2
--- /dev/null
@@ -0,0 +1,80 @@
+From 02b04dc439d0a4eceb429cd84562417bd3163722 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 25 Nov 2022 10:21:59 +0000
+Subject: fs/ntfs3: Fix null-ptr-deref on inode->i_op in ntfs_lookup()
+
+From: ZhangPeng <zhangpeng362@huawei.com>
+
+[ Upstream commit 254e69f284d7270e0abdc023ee53b71401c3ba0c ]
+
+Syzbot reported a null-ptr-deref bug:
+
+ntfs3: loop0: Different NTFS' sector size (1024) and media sector size
+(512)
+ntfs3: loop0: Mark volume as dirty due to NTFS errors
+general protection fault, probably for non-canonical address
+0xdffffc0000000001: 0000 [#1] PREEMPT SMP KASAN
+KASAN: null-ptr-deref in range [0x0000000000000008-0x000000000000000f]
+RIP: 0010:d_flags_for_inode fs/dcache.c:1980 [inline]
+RIP: 0010:__d_add+0x5ce/0x800 fs/dcache.c:2796
+Call Trace:
+ <TASK>
+ d_splice_alias+0x122/0x3b0 fs/dcache.c:3191
+ lookup_open fs/namei.c:3391 [inline]
+ open_last_lookups fs/namei.c:3481 [inline]
+ path_openat+0x10e6/0x2df0 fs/namei.c:3688
+ do_filp_open+0x264/0x4f0 fs/namei.c:3718
+ do_sys_openat2+0x124/0x4e0 fs/open.c:1310
+ do_sys_open fs/open.c:1326 [inline]
+ __do_sys_open fs/open.c:1334 [inline]
+ __se_sys_open fs/open.c:1330 [inline]
+ __x64_sys_open+0x221/0x270 fs/open.c:1330
+ do_syscall_x64 arch/x86/entry/common.c:50 [inline]
+ do_syscall_64+0x3d/0xb0 arch/x86/entry/common.c:80
+ entry_SYSCALL_64_after_hwframe+0x63/0xcd
+
+If the MFT record of ntfs inode is not a base record, inode->i_op can be
+NULL. And a null-ptr-deref may happen:
+
+ntfs_lookup()
+    dir_search_u() # inode->i_op is set to NULL
+    d_splice_alias()
+        __d_add()
+            d_flags_for_inode() # inode->i_op->get_link null-ptr-deref
+
+Fix this by adding a Check on inode->i_op before calling the
+d_splice_alias() function.
+
+Fixes: 4342306f0f0d ("fs/ntfs3: Add file operations and implementation")
+Reported-by: syzbot+a8f26a403c169b7593fe@syzkaller.appspotmail.com
+Signed-off-by: ZhangPeng <zhangpeng362@huawei.com>
+Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/ntfs3/namei.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+diff --git a/fs/ntfs3/namei.c b/fs/ntfs3/namei.c
+index bc741213ad848..29fd76d94c744 100644
+--- a/fs/ntfs3/namei.c
++++ b/fs/ntfs3/namei.c
+@@ -86,6 +86,16 @@ static struct dentry *ntfs_lookup(struct inode *dir, struct dentry *dentry,
+               __putname(uni);
+       }
++      /*
++       * Check for a null pointer
++       * If the MFT record of ntfs inode is not a base record, inode->i_op can be NULL.
++       * This causes null pointer dereference in d_splice_alias().
++       */
++      if (!IS_ERR(inode) && inode->i_op == NULL) {
++              iput(inode);
++              inode = ERR_PTR(-EINVAL);
++      }
++
+       return d_splice_alias(inode, dentry);
+ }
+-- 
+2.39.2
+
index 1d3d93f66b736634f3871bffde1e95327c4e2b8b..f293965ad46d0f704d4aa4ae75086ffa5a3a4733 100644 (file)
@@ -8,3 +8,11 @@ bus-mhi-host-use-mhi_tryset_pm_state-for-setting-fw-.patch
 bus-mhi-host-range-check-chdboff-and-erdboff.patch
 mailbox-zynq-switch-to-flexible-array-to-simplify-co.patch
 mailbox-zynqmp-fix-counts-of-child-nodes.patch
+asoc-soc-pcm-use-gfp_atomic-for-dpcm-structure.patch
+asoc-soc-pcm-align-be-atomicity-with-that-of-the-fe.patch
+asoc-soc-pcm-fix-and-cleanup-dpcm-locking.patch
+asoc-soc-pcm-serialize-be-triggers.patch
+asoc-soc-pcm-test-refcount-before-triggering.patch
+asoc-soc-pcm-fix-be-handling-of-pause_release.patch
+fs-ntfs3-fix-null-ptr-deref-on-inode-i_op-in-ntfs_lo.patch
+drm-hyperv-don-t-overwrite-dirt_needed-value-set-by-.patch