You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[BUG] start/stop floods with applications using start threshold, e.g. with aplay+S3 #1572
Describe the bug
There seems to be an issue with handling a TRIG_START when following conditions apply:
stream has been previously running and then restarted (1) S3 suspend+resume, 2) application stopped with SIGSUSPEND, 3) XRUN, or 4) applications which start/stop the stream with snd_pcm_drop() + snd_pcm_prepare() + snd_pcm_start()))
application has set the "start_threshold" SW parameter to any lower value than the maximum, or explicitly starts the stream with snd_pcm_start()
With these conditions met, FW is not able to start the stream succesfully, but either ends up in a short start/stop loop causing dozens of XRUNs lasting multiple milliseconds (and making a pretty awful nosie), or in infinite loop (application must be terminated to recover).
This problem is at least a contributing factor to Pulseaudio issues tracked in linux#1000
To Reproduce
To simplify testing, I figured out following way to reproduce this with aplay (but this will impact a wide range of other ALSA applications):
aplay -R 21000 -Dhw:0 -vv -i testfile.wav
suspend aplay with 'ctrl-Z'
resume aplay with 'fg'
go back to (2) until error occurs
The error happens with 21ms start threshdold in this example, but notably
higher values (-R 200000) also cause the issue. Clearly DSP should have plenty of time to resume the stream with this much buffering in place.
Reproduction Rate
5/10
Expected behavior
Playback should resume succesfully.
Impact
Marked is as P2 as DSP should under no circumstance go into a state machine loop like this.
Note: attached log file has some additional patches (prints with "KV:"), but the bug is reproduced without these patches with the unmodified version above. The additional patches are to explain the problem better.
Name of the topology file: sof-hda-generic-2ch.tplg
Then starts a repeated sequence where host trying to restart the stream, it doesn't work, it stops it again (xrun handling), and tries to start again. This happens many times:
Status update: The root of the problem seems to be the first DMA position that is visible to driver. Not clear yet whether this is FW or Linux driver bug (could be the latter as well, based on today's debug).
Log file showing the problem (driver log):
[ 1758.669701] sof-audio-pci 0000:00:1f.3: D: trig-ptr0 12408
[ 1758.669713] sof-audio-pci 0000:00:1f.3: ipc tx: 0x60040000: GLB_STREAM_MSG: TRIG_START
[ 1758.669864] sof-audio-pci 0000:00:1f.3: D: stream_check
[ 1758.669976] sof-audio-pci 0000:00:1f.3: ipc tx succeeded: 0x60040000: GLB_STREAM_MSG: TRIG_START
[ 1758.669986] sof-audio-pci 0000:00:1f.3: In hda_link_pcm_trigger cmd=1
[ 1758.670000] skl_hda_dsp_generic skl_hda_dsp_generic: D: avail = 12288, state 3, buffer size = 16368, period size = 4080
[ 1758.670087] sof-audio-pci 0000:00:1f.3: D: PCM: position 49792 (hdareg), position 0 (ipc)
NOTE: First driver checks for DMA position, half of the buffer is already consumed.
[ 1758.670092] skl_hda_dsp_generic skl_hda_dsp_generic: D: ptr0 12448 (old 0)
[ 1758.670099] skl_hda_dsp_generic skl_hda_dsp_generic: D: avail = 24736, state 3, buffer size = 16368, period size = 4080
NOTE: But this is even more suspicious, avail>buffer_size indicating an XRUN, which should not yet be the case.
The root cause seems to be mis-synchronization of the host DMA pointer.
When Linux application goes through the STOP/XRUN->PREPARE->START transitions, at PREPARE the DMA position should be reset to 0. This is however not done. FW does not do this in host.c TRIG_START, and Linux driver does not do this in sof/pcm.c:prepare(). What follows is this (success case first, then failure case):
Success case:
[ 421.704543] sof-audio-pci 0000:00:1f.3: ipc tx succeeded: 0x60040000: GLB_STREAM_MSG: TRIG_START
[ 421.704546] sof-audio-pci 0000:00:1f.3: In hda_link_pcm_trigger cmd=1
[ 421.704556] skl_hda_dsp_generic skl_hda_dsp_generic: D: avail = 12288 (hw_ptr=0/appl_ptr=4080), state 3, buffer size = 16368, period size = 4080
[ 421.704603] sof-audio-pci 0000:00:1f.3: D: PCM: position 384 (hdareg), position 0 (ipc)
[ 421.704604] skl_hda_dsp_generic skl_hda_dsp_generic: D: ptr0 96 (old 0)
[ 421.704605] skl_hda_dsp_generic skl_hda_dsp_generic: D: avail = 12384 (hw_ptr=96/appl_ptr=4080), state 3, buffer size = 16368, period size = 4080
[ 421.704607] skl_hda_dsp_generic skl_hda_dsp_generic: D: avail = 8304 (hw_ptr=96/appl_ptr=8160), state 3, buffer size = 16368, period size = 4080
[ 421.704641] sof-audio-pci 0000:00:1f.3: D: PCM: position 384 (hdareg), position 0 (ipc)
[ 421.704641] skl_hda_dsp_generic skl_hda_dsp_generic: D: ptr0 96 (old 96)
[ 421.704643] skl_hda_dsp_generic skl_hda_dsp_generic: D: avail = 4224 (hw_ptr=96/appl_ptr=12240), state 3, buffer size = 16368, period size = 4080
[ 421.704735] sof-audio-pci 0000:00:1f.3: D: PCM: position 384 (hdareg), position 0 (ipc)
As you can note, DMA starts consuming the buffer from zero (96frames==384bytes), as reported by
DGBRP and DGBWP registers. So this is ok. But then:
Failure case (when aplay is resumed in the bug scenario):
[ 428.462266] sof-audio-pci 0000:00:1f.3: ipc tx succeeded: 0x60040000: GLB_STREAM_MSG: TRIG_START
[ 428.462279] sof-audio-pci 0000:00:1f.3: In hda_link_pcm_trigger cmd=1
[ 428.462296] skl_hda_dsp_generic skl_hda_dsp_generic: D: avail = 12968 (hw_ptr=0/appl_ptr=3400), state 3, buffer size = 16368, period size = 4080
[ 428.462376] sof-audio-pci 0000:00:1f.3: D: PCM: position 32672 (hdareg), position 0 (ipc)
[ 428.462379] skl_hda_dsp_generic skl_hda_dsp_generic: D: ptr0 8168 (old 0)
[ 428.462385] skl_hda_dsp_generic skl_hda_dsp_generic: D: avail = 21136 (hw_ptr=8168/appl_ptr=3400), state 3, buffer size = 16368, period size = 4080
-> avail>buffer_size means XRUN condition and stream will stop
... the DMA is not correctly reset in TRIG_START, so Linux side will get random values and in the original use-case of this bug, leads to an immediate XRUN condition. This sequence repeats infinitely. Confirmed to be the rootcause also for Linux bug thesofproject/linux#1000 .
@tlauda@jajanusz : can you confirm the expected behaviour of the FW? Is TRIG_START expected to continue from same position? And if yes, what is the right IPC sequence to reset DMA position in the ring buffer?
@kv2019i If we are talking about DMA position read from stream HW registers, then it can only be reset on kernel side by resetting Host DMA.
Hmm, thanks, so it seems to be. And if host resets the DMA, it needs to let FW know about it and currently this means IPC_STREAM_PCM_PARAMS.
@lgirdwood Any historical reasons why IPC interface does not have IPC_STREAM_PREPARE()? Both ALSA PCM and ASOC ops interface have a separate prepare() method, but SOF IPC does not have. In SOF side IPC implementation, handler.c seems to implement both hw_params and prepare logic, so the split is there as well, just wrapped into a single IPC.
Addition of new prepare IPC is a separate topic and will be handled as an enhancement if other solutions to solve this issue prove out to be problematic.
Activity
kv2019i commentedon Jun 17, 2019
Annotating the attached log 20190617-soflog.txt:
The succesful playback of stream ends in the log with this:
Then starts a repeated sequence where host trying to restart the stream, it doesn't work, it stops it again (xrun handling), and tries to start again. This happens many times:
Sometime we get into FW XRUN handling:
And yet one case in the start/loop is failure to start up in preload state:
In host side, aplay output looks like this:
kv2019i commentedon Jun 18, 2019
Status update: The root of the problem seems to be the first DMA position that is visible to driver. Not clear yet whether this is FW or Linux driver bug (could be the latter as well, based on today's debug).
Log file showing the problem (driver log):
[ 1758.669701] sof-audio-pci 0000:00:1f.3: D: trig-ptr0 12408
[ 1758.669713] sof-audio-pci 0000:00:1f.3: ipc tx: 0x60040000: GLB_STREAM_MSG: TRIG_START
[ 1758.669864] sof-audio-pci 0000:00:1f.3: D: stream_check
[ 1758.669976] sof-audio-pci 0000:00:1f.3: ipc tx succeeded: 0x60040000: GLB_STREAM_MSG: TRIG_START
[ 1758.669986] sof-audio-pci 0000:00:1f.3: In hda_link_pcm_trigger cmd=1
[ 1758.670000] skl_hda_dsp_generic skl_hda_dsp_generic: D: avail = 12288, state 3, buffer size = 16368, period size = 4080
[ 1758.670087] sof-audio-pci 0000:00:1f.3: D: PCM: position 49792 (hdareg), position 0 (ipc)
NOTE: First driver checks for DMA position, half of the buffer is already consumed.
[ 1758.670092] skl_hda_dsp_generic skl_hda_dsp_generic: D: ptr0 12448 (old 0)
[ 1758.670099] skl_hda_dsp_generic skl_hda_dsp_generic: D: avail = 24736, state 3, buffer size = 16368, period size = 4080
NOTE: But this is even more suspicious, avail>buffer_size indicating an XRUN, which should not yet be the case.
Debug continues...
kv2019i commentedon Jun 19, 2019
The root cause seems to be mis-synchronization of the host DMA pointer.
When Linux application goes through the STOP/XRUN->PREPARE->START transitions, at PREPARE the DMA position should be reset to 0. This is however not done. FW does not do this in host.c TRIG_START, and Linux driver does not do this in sof/pcm.c:prepare(). What follows is this (success case first, then failure case):
Success case:
[ 421.704543] sof-audio-pci 0000:00:1f.3: ipc tx succeeded: 0x60040000: GLB_STREAM_MSG: TRIG_START
[ 421.704546] sof-audio-pci 0000:00:1f.3: In hda_link_pcm_trigger cmd=1
[ 421.704556] skl_hda_dsp_generic skl_hda_dsp_generic: D: avail = 12288 (hw_ptr=0/appl_ptr=4080), state 3, buffer size = 16368, period size = 4080
[ 421.704603] sof-audio-pci 0000:00:1f.3: D: PCM: position 384 (hdareg), position 0 (ipc)
[ 421.704604] skl_hda_dsp_generic skl_hda_dsp_generic: D: ptr0 96 (old 0)
[ 421.704605] skl_hda_dsp_generic skl_hda_dsp_generic: D: avail = 12384 (hw_ptr=96/appl_ptr=4080), state 3, buffer size = 16368, period size = 4080
[ 421.704607] skl_hda_dsp_generic skl_hda_dsp_generic: D: avail = 8304 (hw_ptr=96/appl_ptr=8160), state 3, buffer size = 16368, period size = 4080
[ 421.704641] sof-audio-pci 0000:00:1f.3: D: PCM: position 384 (hdareg), position 0 (ipc)
[ 421.704641] skl_hda_dsp_generic skl_hda_dsp_generic: D: ptr0 96 (old 96)
[ 421.704643] skl_hda_dsp_generic skl_hda_dsp_generic: D: avail = 4224 (hw_ptr=96/appl_ptr=12240), state 3, buffer size = 16368, period size = 4080
[ 421.704735] sof-audio-pci 0000:00:1f.3: D: PCM: position 384 (hdareg), position 0 (ipc)
As you can note, DMA starts consuming the buffer from zero (96frames==384bytes), as reported by
DGBRP and DGBWP registers. So this is ok. But then:
Failure case (when aplay is resumed in the bug scenario):
[ 428.462266] sof-audio-pci 0000:00:1f.3: ipc tx succeeded: 0x60040000: GLB_STREAM_MSG: TRIG_START
[ 428.462279] sof-audio-pci 0000:00:1f.3: In hda_link_pcm_trigger cmd=1
[ 428.462296] skl_hda_dsp_generic skl_hda_dsp_generic: D: avail = 12968 (hw_ptr=0/appl_ptr=3400), state 3, buffer size = 16368, period size = 4080
[ 428.462376] sof-audio-pci 0000:00:1f.3: D: PCM: position 32672 (hdareg), position 0 (ipc)
[ 428.462379] skl_hda_dsp_generic skl_hda_dsp_generic: D: ptr0 8168 (old 0)
[ 428.462385] skl_hda_dsp_generic skl_hda_dsp_generic: D: avail = 21136 (hw_ptr=8168/appl_ptr=3400), state 3, buffer size = 16368, period size = 4080
-> avail>buffer_size means XRUN condition and stream will stop
... the DMA is not correctly reset in TRIG_START, so Linux side will get random values and in the original use-case of this bug, leads to an immediate XRUN condition. This sequence repeats infinitely. Confirmed to be the rootcause also for Linux bug thesofproject/linux#1000 .
@tlauda @jajanusz : can you confirm the expected behaviour of the FW? Is TRIG_START expected to continue from same position? And if yes, what is the right IPC sequence to reset DMA position in the ring buffer?
@ranj063 FYI
tlauda commentedon Jun 25, 2019
@kv2019i If we are talking about DMA position read from stream HW registers, then it can only be reset on kernel side by resetting Host DMA.
kv2019i commentedon Jun 25, 2019
@tlauda wrote:
Hmm, thanks, so it seems to be. And if host resets the DMA, it needs to let FW know about it and currently this means IPC_STREAM_PCM_PARAMS.
@lgirdwood Any historical reasons why IPC interface does not have IPC_STREAM_PREPARE()? Both ALSA PCM and ASOC ops interface have a separate prepare() method, but SOF IPC does not have. In SOF side IPC implementation, handler.c seems to implement both hw_params and prepare logic, so the split is there as well, just wrapped into a single IPC.
kv2019i commentedon Jun 27, 2019
For now, this bug seems to be a driver side issue, moving to driver -> thesofproject/linux#1062
Addition of new prepare IPC is a separate topic and will be handled as an enhancement if other solutions to solve this issue prove out to be problematic.