From: Sasha Levin Date: Fri, 12 May 2023 01:49:27 +0000 (-0400) Subject: Fixes for 5.15 X-Git-Tag: v4.14.315~111 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=d9463efffbbb95ad2a60d4de921cb1fa930d1e3a;p=thirdparty%2Fkernel%2Fstable-queue.git Fixes for 5.15 Signed-off-by: Sasha Levin --- 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 index 00000000000..9e6904aa61d --- /dev/null +++ b/queue-5.15/asoc-soc-pcm-align-be-atomicity-with-that-of-the-fe.patch @@ -0,0 +1,71 @@ +From 15b0d8aaea3eee8dcd37352d6bbb3effdb269bf3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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 +[ removed FE stream lock by tiwai ] +Signed-off-by: Takashi Iwai +Reviewed-by: Kai Vehmanen +Reviewed-by: Bard Liao +Reviewed-by: Ranjani Sridharan +Link: https://lore.kernel.org/r/20211207173745.15850-3-pierre-louis.bossart@linux.intel.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..dc9adebe190 --- /dev/null +++ b/queue-5.15/asoc-soc-pcm-fix-and-cleanup-dpcm-locking.patch @@ -0,0 +1,760 @@ +From 11064cbb1d41902bcc8d8c89095fe224323c6472 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 7 Dec 2021 11:37:42 -0600 +Subject: ASoC: soc-pcm: Fix and cleanup DPCM locking + +From: Takashi Iwai + +[ 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 +[clarification of commit message by plbossart] +Signed-off-by: Pierre-Louis Bossart +Reviewed-by: Kai Vehmanen +Reviewed-by: Bard Liao +Reviewed-by: Ranjani Sridharan +Link: https://lore.kernel.org/r/20211207173745.15850-4-pierre-louis.bossart@linux.intel.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + 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 + #include + ++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 index 00000000000..075ee7cc687 --- /dev/null +++ b/queue-5.15/asoc-soc-pcm-fix-be-handling-of-pause_release.patch @@ -0,0 +1,78 @@ +From e41e7485d3d6c95a71a1d0fb6c525b32b82ab61a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 7 Dec 2021 11:37:45 -0600 +Subject: ASoC: soc-pcm: fix BE handling of PAUSE_RELEASE + +From: Pierre-Louis Bossart + +[ 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 +Signed-off-by: Pierre-Louis Bossart +Reviewed-by: Kai Vehmanen +Reviewed-by: Bard Liao +Reviewed-by: Ranjani Sridharan +Link: https://lore.kernel.org/r/20211207173745.15850-7-pierre-louis.bossart@linux.intel.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..c7923ab2f41 --- /dev/null +++ b/queue-5.15/asoc-soc-pcm-serialize-be-triggers.patch @@ -0,0 +1,176 @@ +From a35e54d4c8d72fa5da81e4350ad4eca1ed349cd6 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 7 Dec 2021 11:37:43 -0600 +Subject: ASoC: soc-pcm: serialize BE triggers + +From: Takashi Iwai + +[ 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 +[test, checkpatch fix and clarification of commit message by plbossart] +Signed-off-by: Pierre-Louis Bossart +Reviewed-by: Kai Vehmanen +Reviewed-by: Bard Liao +Reviewed-by: Ranjani Sridharan +Link: https://lore.kernel.org/r/20211207173745.15850-5-pierre-louis.bossart@linux.intel.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..f0365949ea9 --- /dev/null +++ b/queue-5.15/asoc-soc-pcm-test-refcount-before-triggering.patch @@ -0,0 +1,181 @@ +From c20826fcf2b6106dd37bb49d1c638791c0e0ac92 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 7 Dec 2021 11:37:44 -0600 +Subject: ASoC: soc-pcm: test refcount before triggering + +From: Pierre-Louis Bossart + +[ 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 +Reviewed-by: Kai Vehmanen +Reviewed-by: Bard Liao +Reviewed-by: Ranjani Sridharan +Link: https://lore.kernel.org/r/20211207173745.15850-6-pierre-louis.bossart@linux.intel.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..b3ae87d9c8e --- /dev/null +++ b/queue-5.15/asoc-soc-pcm-use-gfp_atomic-for-dpcm-structure.patch @@ -0,0 +1,41 @@ +From 364f2950d8dca8f867a74364e455c85076df19d1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 7 Dec 2021 11:37:40 -0600 +Subject: ASoC: soc-pcm: use GFP_ATOMIC for dpcm structure + +From: Pierre-Louis Bossart + +[ 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 +Signed-off-by: Pierre-Louis Bossart +Reviewed-by: Kai Vehmanen +Reviewed-by: Bard Liao +Reviewed-by: Ranjani Sridharan +Link: https://lore.kernel.org/r/20211207173745.15850-2-pierre-louis.bossart@linux.intel.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..103c614c67a --- /dev/null +++ b/queue-5.15/drm-hyperv-don-t-overwrite-dirt_needed-value-set-by-.patch @@ -0,0 +1,42 @@ +From ce907e3637467bfd832a98cc1e8e1b60b150107b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 23 Sep 2022 10:09:50 +0000 +Subject: drm/hyperv: Don't overwrite dirt_needed value set by host + +From: Saurabh Sengar + +[ 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 +Reviewed-by: Dexuan Cui +Reviewed-by: Michael Kelley +Link: https://lore.kernel.org/r/1662996766-19304-1-git-send-email-ssengar@linux.microsoft.com +Signed-off-by: Wei Liu +Signed-off-by: Sasha Levin +--- + 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 index 00000000000..5924af21545 --- /dev/null +++ b/queue-5.15/fs-ntfs3-fix-null-ptr-deref-on-inode-i_op-in-ntfs_lo.patch @@ -0,0 +1,80 @@ +From 02b04dc439d0a4eceb429cd84562417bd3163722 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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: + + 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 +Signed-off-by: Konstantin Komarov +Signed-off-by: Sasha Levin +--- + 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 + diff --git a/queue-5.15/series b/queue-5.15/series index 1d3d93f66b7..f293965ad46 100644 --- a/queue-5.15/series +++ b/queue-5.15/series @@ -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