Skip to content

Commit 0c75fc7

Browse files
plbossartbroonie
authored andcommitted
ASoC: soc-pcm: protect BE dailink state changes in trigger
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. The code carefully checks when the BE can be stopped or hw_free'ed, but the trigger code does not use any mutual exclusion. Fix by using the same spinlock already used to check FE states, and set the state before the trigger. In case of errors, the initial state will be restored. This patch does not change how the triggers are handled, it only makes sure the states are handled in critical sections. Signed-off-by: Pierre-Louis Bossart <[email protected]> Message-Id: <[email protected]> Signed-off-by: Mark Brown <[email protected]>
1 parent 515b436 commit 0c75fc7

File tree

1 file changed

+85
-18
lines changed

1 file changed

+85
-18
lines changed

sound/soc/soc-pcm.c

Lines changed: 85 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1999,6 +1999,8 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
19991999
struct snd_soc_pcm_runtime *be;
20002000
struct snd_soc_dpcm *dpcm;
20012001
int ret = 0;
2002+
unsigned long flags;
2003+
enum snd_soc_dpcm_state state;
20022004

20032005
for_each_dpcm_be(fe, stream, dpcm) {
20042006
struct snd_pcm_substream *be_substream;
@@ -2015,76 +2017,141 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
20152017

20162018
switch (cmd) {
20172019
case SNDRV_PCM_TRIGGER_START:
2020+
spin_lock_irqsave(&fe->card->dpcm_lock, flags);
20182021
if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) &&
20192022
(be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP) &&
2020-
(be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED))
2023+
(be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED)) {
2024+
spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
20212025
continue;
2026+
}
2027+
state = be->dpcm[stream].state;
2028+
be->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
2029+
spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
20222030

20232031
ret = soc_pcm_trigger(be_substream, cmd);
2024-
if (ret)
2032+
if (ret) {
2033+
spin_lock_irqsave(&fe->card->dpcm_lock, flags);
2034+
be->dpcm[stream].state = state;
2035+
spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
20252036
goto end;
2037+
}
20262038

2027-
be->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
20282039
break;
20292040
case SNDRV_PCM_TRIGGER_RESUME:
2030-
if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_SUSPEND))
2041+
spin_lock_irqsave(&fe->card->dpcm_lock, flags);
2042+
if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_SUSPEND) {
2043+
spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
20312044
continue;
2045+
}
2046+
2047+
state = be->dpcm[stream].state;
2048+
be->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
2049+
spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
20322050

20332051
ret = soc_pcm_trigger(be_substream, cmd);
2034-
if (ret)
2052+
if (ret) {
2053+
spin_lock_irqsave(&fe->card->dpcm_lock, flags);
2054+
be->dpcm[stream].state = state;
2055+
spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
20352056
goto end;
2057+
}
20362058

2037-
be->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
20382059
break;
20392060
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
2040-
if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED))
2061+
spin_lock_irqsave(&fe->card->dpcm_lock, flags);
2062+
if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED) {
2063+
spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
20412064
continue;
2065+
}
2066+
2067+
state = be->dpcm[stream].state;
2068+
be->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
2069+
spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
20422070

20432071
ret = soc_pcm_trigger(be_substream, cmd);
2044-
if (ret)
2072+
if (ret) {
2073+
spin_lock_irqsave(&fe->card->dpcm_lock, flags);
2074+
be->dpcm[stream].state = state;
2075+
spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
20452076
goto end;
2077+
}
20462078

2047-
be->dpcm[stream].state = SND_SOC_DPCM_STATE_START;
20482079
break;
20492080
case SNDRV_PCM_TRIGGER_STOP:
2081+
spin_lock_irqsave(&fe->card->dpcm_lock, flags);
20502082
if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_START) &&
2051-
(be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED))
2083+
(be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED)) {
2084+
spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
20522085
continue;
2086+
}
2087+
spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
20532088

20542089
if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
20552090
continue;
20562091

2092+
spin_lock_irqsave(&fe->card->dpcm_lock, flags);
2093+
state = be->dpcm[stream].state;
2094+
be->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP;
2095+
spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
2096+
20572097
ret = soc_pcm_trigger(be_substream, cmd);
2058-
if (ret)
2098+
if (ret) {
2099+
spin_lock_irqsave(&fe->card->dpcm_lock, flags);
2100+
be->dpcm[stream].state = state;
2101+
spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
20592102
goto end;
2103+
}
20602104

2061-
be->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP;
20622105
break;
20632106
case SNDRV_PCM_TRIGGER_SUSPEND:
2064-
if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START)
2107+
spin_lock_irqsave(&fe->card->dpcm_lock, flags);
2108+
if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START) {
2109+
spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
20652110
continue;
2111+
}
2112+
spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
20662113

20672114
if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
20682115
continue;
20692116

2117+
spin_lock_irqsave(&fe->card->dpcm_lock, flags);
2118+
state = be->dpcm[stream].state;
2119+
be->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP;
2120+
spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
2121+
20702122
ret = soc_pcm_trigger(be_substream, cmd);
2071-
if (ret)
2123+
if (ret) {
2124+
spin_lock_irqsave(&fe->card->dpcm_lock, flags);
2125+
be->dpcm[stream].state = state;
2126+
spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
20722127
goto end;
2128+
}
20732129

2074-
be->dpcm[stream].state = SND_SOC_DPCM_STATE_SUSPEND;
20752130
break;
20762131
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
2077-
if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START)
2132+
spin_lock_irqsave(&fe->card->dpcm_lock, flags);
2133+
if (be->dpcm[stream].state != SND_SOC_DPCM_STATE_START) {
2134+
spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
20782135
continue;
2136+
}
2137+
spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
20792138

20802139
if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
20812140
continue;
20822141

2142+
spin_lock_irqsave(&fe->card->dpcm_lock, flags);
2143+
state = be->dpcm[stream].state;
2144+
be->dpcm[stream].state = SND_SOC_DPCM_STATE_PAUSED;
2145+
spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
2146+
20832147
ret = soc_pcm_trigger(be_substream, cmd);
2084-
if (ret)
2148+
if (ret) {
2149+
spin_lock_irqsave(&fe->card->dpcm_lock, flags);
2150+
be->dpcm[stream].state = state;
2151+
spin_unlock_irqrestore(&fe->card->dpcm_lock, flags);
20852152
goto end;
2153+
}
20862154

2087-
be->dpcm[stream].state = SND_SOC_DPCM_STATE_PAUSED;
20882155
break;
20892156
}
20902157
}

0 commit comments

Comments
 (0)