Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ASoC: soc-pcm: serialize BE triggers

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>

authored by

Takashi Iwai and committed by
Mark Brown
b2ae8066 b7898396

+29 -17
+29 -17
sound/soc/soc-pcm.c
··· 46 46 snd_pcm_stream_lock_irq(snd_soc_dpcm_get_substream(rtd, stream)); 47 47 } 48 48 49 + #define snd_soc_dpcm_stream_lock_irqsave(rtd, stream, flags) \ 50 + snd_pcm_stream_lock_irqsave(snd_soc_dpcm_get_substream(rtd, stream), flags) 51 + 49 52 static inline void snd_soc_dpcm_stream_unlock_irq(struct snd_soc_pcm_runtime *rtd, 50 53 int stream) 51 54 { 52 55 snd_pcm_stream_unlock_irq(snd_soc_dpcm_get_substream(rtd, stream)); 53 56 } 57 + 58 + #define snd_soc_dpcm_stream_unlock_irqrestore(rtd, stream, flags) \ 59 + snd_pcm_stream_unlock_irqrestore(snd_soc_dpcm_get_substream(rtd, stream), flags) 54 60 55 61 #define DPCM_MAX_BE_USERS 8 56 62 ··· 2085 2079 { 2086 2080 struct snd_soc_pcm_runtime *be; 2087 2081 struct snd_soc_dpcm *dpcm; 2082 + unsigned long flags; 2088 2083 int ret = 0; 2089 2084 2090 2085 for_each_dpcm_be(fe, stream, dpcm) { ··· 2094 2087 be = dpcm->be; 2095 2088 be_substream = snd_soc_dpcm_get_substream(be, stream); 2096 2089 2090 + snd_soc_dpcm_stream_lock_irqsave(be, stream, flags); 2091 + 2097 2092 /* is this op for this BE ? */ 2098 2093 if (!snd_soc_dpcm_be_can_update(fe, be, stream)) 2099 - continue; 2094 + goto next; 2100 2095 2101 2096 dev_dbg(be->dev, "ASoC: trigger BE %s cmd %d\n", 2102 2097 be->dai_link->name, cmd); ··· 2108 2099 if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) && 2109 2100 (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP) && 2110 2101 (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED)) 2111 - continue; 2102 + goto next; 2112 2103 2113 2104 ret = soc_pcm_trigger(be_substream, cmd); 2114 2105 if (ret) 2115 - goto end; 2106 + goto next; 2116 2107 2117 2108 be->dpcm[stream].state = SND_SOC_DPCM_STATE_START; 2118 2109 break; 2119 2110 case SNDRV_PCM_TRIGGER_RESUME: 2120 2111 if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_SUSPEND)) 2121 - continue; 2112 + goto next; 2122 2113 2123 2114 ret = soc_pcm_trigger(be_substream, cmd); 2124 2115 if (ret) 2125 - goto end; 2116 + goto next; 2126 2117 2127 2118 be->dpcm[stream].state = SND_SOC_DPCM_STATE_START; 2128 2119 break; 2129 2120 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 2130 2121 if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED)) 2131 - continue; 2122 + goto next; 2132 2123 2133 2124 ret = soc_pcm_trigger(be_substream, cmd); 2134 2125 if (ret) 2135 - goto end; 2126 + goto next; 2136 2127 2137 2128 be->dpcm[stream].state = SND_SOC_DPCM_STATE_START; 2138 2129 break; 2139 2130 case SNDRV_PCM_TRIGGER_STOP: 2140 2131 if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_START) && 2141 2132 (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED)) 2142 - continue; 2133 + goto next; 2143 2134 2144 2135 if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream)) 2145 - continue; 2136 + goto next; 2146 2137 2147 2138 ret = soc_pcm_trigger(be_substream, cmd); 2148 2139 if (ret) 2149 - goto end; 2140 + goto next; 2150 2141 2151 2142 be->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP; 2152 2143 break; 2153 2144 case SNDRV_PCM_TRIGGER_SUSPEND: 2154 2145 if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START) 2155 - continue; 2146 + goto next; 2156 2147 2157 2148 if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream)) 2158 - continue; 2149 + goto next; 2159 2150 2160 2151 ret = soc_pcm_trigger(be_substream, cmd); 2161 2152 if (ret) 2162 - goto end; 2153 + goto next; 2163 2154 2164 2155 be->dpcm[stream].state = SND_SOC_DPCM_STATE_SUSPEND; 2165 2156 break; 2166 2157 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 2167 2158 if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START) 2168 - continue; 2159 + goto next; 2169 2160 2170 2161 if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream)) 2171 - continue; 2162 + goto next; 2172 2163 2173 2164 ret = soc_pcm_trigger(be_substream, cmd); 2174 2165 if (ret) 2175 - goto end; 2166 + goto next; 2176 2167 2177 2168 be->dpcm[stream].state = SND_SOC_DPCM_STATE_PAUSED; 2178 2169 break; 2179 2170 } 2171 + next: 2172 + snd_soc_dpcm_stream_unlock_irqrestore(be, stream, flags); 2173 + if (ret) 2174 + break; 2180 2175 } 2181 - end: 2182 2176 if (ret < 0) 2183 2177 dev_err(fe->dev, "ASoC: %s() failed at %s (%d)\n", 2184 2178 __func__, be->dai_link->name, ret);