Skip to content

ASoC: DPCM fixes - #3188

Closed
plbossart wants to merge 6 commits into
thesofproject:topic/sof-devfrom
plbossart:test/dpcm-lock2
Closed

plbossart wants to merge 6 commits into
thesofproject:topic/sof-devfrom
plbossart:test/dpcm-lock2

Conversation

@plbossart

@plbossart plbossart commented Sep 30, 2021 •

Copy link
Copy Markdown
Member

This version is a lot simpler/consistent than previous attempts, let's start reviewing it.

I need to double-check results with SoundWire/HDaudio in deep-buffer mode, but it's ready for internal reviews.

This function is not used anywhere, including soc-pcm.c

Remove dead code.

Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
No one uses the following functions outside of soc-pcm.c

snd_soc_dpcm_can_be_free_stop()
snd_soc_dpcm_can_be_params()
snd_soc_dpcm_be_can_update()

In preparation for locking changes, move them to static functions and
remove the EXPORT_SYMBOL_GPL()

Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
It's not clear why a spinlock was used, and even less why the
'dpcm_lock' is used with the irqsave/irqrestore: DPCM functions are
typically not used in interrupt context.

Move to a mutex which will allow us to sleep for longer periods of
time and handle non-atomic triggers.

Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
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.

This can be fixed by using the dpcm_mutex.

As discussed on the alsa-mailing list [1], there is an additional
issue that the BEs can be disconnected dynamically, leading to
potential accesses to freed memory.

This patch suggests a general protection of the for_each_dpcm_be()
loop with the dpcm_mutex, to solve both the problem of multiple
triggers and BE dynamic addition/removal.

[1] https://lore.kernel.org/alsa-devel/002f01d7b4f5$c030f4a0$4092dde0$@samsung.com/

Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
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 a mutex.

Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
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

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.

[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>
@plbossart plbossart changed the title [TEST] protect BE changes with dpcm_mutex ASoC: DPCM fixes Sep 30, 2021
@plbossart
plbossart marked this pull request as ready for review September 30, 2021 19:19
@plbossart

plbossart commented Oct 4, 2021 •

Copy link
Copy Markdown
Member Author

sent as upstream RFC - no issues detected in validation so far, apart from alsa-bat settings that need to be updated for the new topologies.

Comment thread sound/soc/soc-pcm.c Outdated
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))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how about

if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED) || be->dpcm[stream].be_start)
    continue

and remove " if (be->dpcm[stream].be_start != 1) "

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RanderWang your proposal mixes state handling and refcount handling. I think it's better to keep the two parts separate for clarity. The goal is not to have minimalistic code, it's really to make the state management clearer.

@RanderWang RanderWang Oct 12, 2021 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure. I don't like

if (be->dpcm[stream].be_start != 1)

how about

if (be->dpcm[stream].be_start > 1)  // multiple call
      continue;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RanderWang IMHO, I like if (be->dpcm[stream].be_start != 0) and if (be->dpcm[stream].be_start != 1). which says that the code is only executed when be_start is 0 or 1 exactly.

Comment thread sound/soc/soc-pcm.c Resolved
@plbossart
plbossart marked this pull request as draft October 12, 2021 13:55

@ranj063 ranj063 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@plbossart

plbossart commented Oct 12, 2021 •

Copy link
Copy Markdown
Member Author

replaced by PR #3209

@plbossart plbossart closed this Oct 12, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants