Repository navigation
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
Rollback can consume another frontend’s backend reference and interrupt its running stream.
1 open issue
What changed in this PR
This PR adds failure rollback to ASoC PCM and compressed-audio trigger sequences to restore frontends and backends after partial failures.
Changes:
- Fix shared-backend RESUME references and failed PAUSE_RELEASE accounting.
- Unwind successful triggers when a later backend, frontend, or component fails.
- Ignore redundant PCM-core undo triggers after rollback.
| File | Description |
|---|---|
| sound/soc/soc-pcm.c | Refactors backend triggers and adds frontend/backend rollback. |
| sound/soc/soc-compress.c | Adds compressed frontend/backend trigger rollback. |
| sound/soc/soc-component.c | Unwinds previously triggered compressed components. |
| include/sound/soc-dpcm.h | Adds the shared inverse-trigger helper. |
Review effort: Balanced
6588eb0 to
a5c2ba0
Compare
|
Changes since v1:
|
a5c2ba0 to
e7773a0
Compare
e7773a0 to
50b4c09
Compare
|
Changes since v2: |
|
the build fails are fixed by: #5995 |
50b4c09 to
645a425
Compare
The SUSPEND trigger drops the be_start reference of the FE for every BE in START state, but RESUME only takes a reference if the BE is in SUSPEND state. If a BE is shared between FEs, the BE is moved to SUSPEND state only when the last FE is suspended and it is moved back to START state when the first FE is resumed. The rest of the FEs will skip the BE on RESUME and the be_start counter ends up being lower than the number of FEs using the BE, which will cause the BE to be stopped while it is still in use by another FE. The BE is also skipped on RESUME if it has been paused in the meantime by another FE which has been resumed already, leaving the BE paused while the resumed FE is running. Handle these cases similarly to START: if the BE is already running, take a reference for the FE and if the BE is paused, release it. The BE is still skipped if another FE has resumed and then stopped or re-prepared it while this FE is suspended. The suspended FE does not hold a reference on the BE and its DPCM state is STOP, handling this would need the suspended state of the FE to be tracked. Fixes: 848aedf ("ASoC: soc-pcm: test refcount before triggering") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
On PAUSE_RELEASE the fe_pause flag is cleared and the be_pause counter is decremented before the BE is triggered, but only the be_start counter is restored if the trigger fails. The BE stays in PAUSED state while the pause reference of the FE is lost. When the stream is closed later, the PCM core releases the pause before stopping the stream which takes the be_pause counter negative and the STOP will be handled as a pause/stop transition, leaving the BE in PAUSED state. For example with SOF the BE pipeline cannot be freed on close and the stream is not usable anymore after this. Restore the fe_pause and be_pause as well when the PAUSE_RELEASE fails. The pause references are also not cleared when the FE or the BE is opened. If the PAUSE_RELEASE fails again when the paused stream is closed, the PCM core does not send STOP for it and the fe_pause and be_pause would be carried over to the next use of the FE and the BE. Clear them on open, as it is done for be_start. Fixes: 9995c1d ("ASoC: soc-pcm: improve BE transition for PAUSE_RELEASE") Cc: stable@vger.kernel.org # 6.1+ Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Move the trigger handling of a single BE out from dpcm_be_dai_trigger() to a new helper function to make it possible to re-use it for unwinding the BE triggers in case of an error. While at it, drop the unnecessary parentheses in the moved conditions and put the dpcm_be_dai_trigger() prototype on a single line. No functional change. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
If the trigger of a BE fails in dpcm_be_dai_trigger() then the BEs which have been already triggered are left in the new state with the be_start and be_pause reference counts taken for the FE. The PCM core issues an undo trigger for a single substream, but not for the failing substream of a linked group and the compress core does not undo failed triggers at all. dpcm_run_update_startup() also expects that the BEs are stopped on failure. The references and BE states will remain unbalanced, the BEs might be left running. Unwind the already triggered BEs when a START, RESUME or PAUSE_RELEASE fails with the corresponding STOP, SUSPEND or PAUSE_PUSH command. Record in the DPCM link if the BE reference has been taken for the FE and only unwind those BEs. The state of a BE which has been skipped can be changed by the trigger of another FE between the trigger and the unwind, so the decision cannot be made on the BE state at unwind time. The per BE trigger handling takes care of the reference counting and the pause/stop transitions. The STOP used to unwind a failed START must not be handled as a pause/stop transition since the START does not take the BE pause reference: the FE can be paused when new BEs are started by dpcm_run_update_startup(). The SUSPEND used to unwind a RESUME must not suspend the BE if another FE has paused it in the meantime, this would leave the BE in SUSPEND state and the PAUSE_RELEASE of the other FE would skip it. Pause the BE instead, as it would have been done by the PAUSE_PUSH of the other FE without the RESUME. Add the dpcm_be_dai_trigger_unwind() function and the snd_soc_trigger_unwind_cmd() helper to soc-internal.h so they can be used to unwind the BEs when the FE trigger fails and by soc-compress as well. Failing STOP, SUSPEND or PAUSE_PUSH is not unwound. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
If the FE is triggered before the BEs and the BE trigger fails then the FE is left in the new state. If the BEs are triggered first and the FE trigger fails then the BEs are left in the new state. For a single substream the PCM core issues an undo trigger after a failed START (STOP) or PAUSE_RELEASE (PAUSE_PUSH), but there is no undo for the failing substream of a linked group, for a failed RESUME or for a trigger which has been deferred by DPCM during a runtime update. Unwind the part which has been successfully triggered when START, RESUME or PAUSE_RELEASE fails, the same way as soc_pcm_trigger() rolls back the DAIs and components. With the unwind in place the undo trigger from the PCM core is redundant and it is also harmful already without the unwind: the undo is propagated to the BEs as well, regardless of whether they have been triggered for this FE or not. If a BE is shared with another running FE then its be_start reference is dropped without being taken and the BE is stopped under the other FE. The FE components also receive a STOP or PAUSE_PUSH without the corresponding successful START or PAUSE_RELEASE. This has been observed with SOF using FE-first trigger order (as used by IPC4 for capture) when the START of the Speaker PCM fails while the Deepbuffer Speaker PCM is running on the same BE: the BE is stopped and the Deepbuffer playback fails. Ignore the STOP and PAUSE_PUSH if the FE is not in a state where these are valid. The dpcm state of the FE is only updated on successful trigger, so it can be used to detect this case. Link: https://lore.kernel.org/linux-sound/asS7ktuZo6z68chF@sirena.co.uk/ Suggested-by: Mark Brown <broonie@kernel.org> Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
…lure snd_soc_component_compr_trigger() returns on the first failing component leaving the components which have been triggered before in the new state. The compress core does not issue any undo trigger on failure, so these components would be left running. Unwind the already triggered components when a START, RESUME or PAUSE_RELEASE fails with the corresponding STOP, SUSPEND or PAUSE_PUSH command. Do the same in soc_compr_trigger() for non-DPCM compressed streams if the CPU DAI trigger fails after the components have been triggered. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
If the FE is triggered before the BEs and the BE trigger fails then the FE is left in the new state. If the BEs are triggered first and the FE trigger fails then the BEs are left in the new state. The same is true for the FE CPU DAI if the component trigger fails. The compress core does not issue any undo trigger on failure and soc_compr_free_fe() will only stop the stream if the FE is in START state, leaving the FE or the BEs running with unbalanced reference counts after the stream is closed. For example with SOF the pipelines of the stream cannot be set up again after such failure and the stream is not usable anymore. Unwind the part which has been successfully triggered when START or PAUSE_RELEASE fails, the same way as it is done in soc-pcm. Link: https://lore.kernel.org/linux-sound/asS7ktuZo6z68chF@sirena.co.uk/ Suggested-by: Mark Brown <broonie@kernel.org> Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>

For CI testing before sending upstream
Hi,
This series is the follow-up promised in the compressed audio series
review [1]: if a trigger fails part way through the DPCM FE-BE sequence,
the already triggered FE or BEs are left in the new state.
The PCM core issues an undo trigger for a single substream only, not for
the failing substream of a linked group, not for a failed RESUME and not
for triggers deferred by DPCM during a runtime update. The compress core
does not undo failed triggers at all. The undo trigger from the PCM core
is also propagated to all BEs of the FE, which can stop a BE shared with
another running FE.
Patches 1-2 fix the BE reference handling on RESUME and on failing
PAUSE_RELEASE.
Patch 3 is a no-functional-change move of the per BE trigger handling to
a helper.
Patch 4 unwinds the BEs in dpcm_be_dai_trigger().
Patch 5 unwinds the FE/BE sequence in soc-pcm and ignores the undo
STOP/PAUSE_PUSH from the PCM core when the FE has not been started.
Patches 6-7 do the same for compressed streams.
Only failing START, RESUME and PAUSE_RELEASE are unwound (with STOP,
SUSPEND and PAUSE_PUSH), failing stop-direction triggers are not.
Testing was done with SOF (IPC4) on a SoundWire laptop by injecting
trigger failures in the FE and BE drivers for PCM and compressed
playback, with both FE-first and BE-first trigger ordering and with a BE
shared between FEs. Without the series a single failed trigger leaves
the stream unusable in several cases (failed BE PAUSE_RELEASE, failed
compressed START, failed FE START with a shared BE running), with the
series all cases recover.
The rollback of multiple BEs within one FE (patch 4) is not covered by
the testing due to lack of device.
Multiple AI tools were used for static code path validation which did
resulted additional patches as issues found by the analysis.
[1] https://lore.kernel.org/linux-sound/asS7ktuZo6z68chF@sirena.co.uk/
Regards,
Peter
Peter Ujfalusi (7):
ASoC: soc-pcm: Take a BE reference on RESUME when the BE is already
running
ASoC: soc-pcm: Restore the pause state when BE PAUSE_RELEASE fails
ASoC: soc-pcm: Move the BE trigger handling to a helper function
ASoC: soc-pcm: Unwind the triggered BEs on failure
ASoC: soc-pcm: Unwind the FE and BE triggers on failure
ASoC: soc-component: Unwind the compress trigger of components on
failure
ASoC: soc-compress: Unwind the FE and BE triggers on failure
include/sound/soc-dpcm.h | 18 +++
sound/soc/soc-component.c | 22 ++-
sound/soc/soc-compress.c | 39 +++--
sound/soc/soc-pcm.c | 307 ++++++++++++++++++++++----------------
4 files changed, 246 insertions(+), 140 deletions(-)
--
2.56.0