Skip to content

audio/pcm: skip period_elapsed for streams that are not started#31

Open
Vakarux12 wants to merge 2 commits into
t2linux:aurfrom
Vakarux12:fix-aaudio-scheduling-while-atomic
Open

audio/pcm: skip period_elapsed for streams that are not started#31
Vakarux12 wants to merge 2 commits into
t2linux:aurfrom
Vakarux12:fix-aaudio-scheduling-while-atomic

Conversation

@Vakarux12

@Vakarux12 Vakarux12 commented Jun 29, 2026

Copy link
Copy Markdown

Problem

On PREEMPT(full) kernels, connecting speakers or headphones to the 3.5mm jack causes a kernel crash:

BUG: scheduling while atomic: irq/80-bce_dma/758/0x00000002

The T2 chip sends continuous timestamp events for all registered audio devices, even when no ALSA stream is open. When a jack is connected, ALSA calls `snd_pcm_period_elapsed`, which can trigger `snd_pcm_do_stop` → `aaudio_pcm_trigger(STOP)` → `aaudio_cmd_stop_io` → `wait_for_completion_timeout` inside the BCE DMA BH handler (atomic context). This also causes a deadlock when called from PipeWire's main thread: the RT data thread holds a lock the main thread needs, while the main thread blocks waiting for T2 to respond.

Fix

Three commits:

  1. Skip period_elapsed for stopped streams — guard aaudio_handle_stream_timestamp with !stream->started to avoid calling snd_pcm_period_elapsed on streams that are not running.

  2. Use os_timestamp for playback — fixes a reload crash when the timestamp function receives an incompatible device timestamp for playback streams.

  3. Defer stop_io to a workqueueaaudio_cmd_stop_io blocks waiting for a T2 reply, which is unsafe in both atomic context and from PipeWire's main thread. Always defer it via schedule_work. In TRIGGER_START, use cancel_work_sync to cancel any pending (not yet running) stop before starting, avoiding a start/stop race without blocking callers unnecessarily.

Testing

Tested on MacBook Pro 16,1 (T2) running Arch Linux with kernel 7.0.12 (PREEMPT full). Previously crashed immediately on speaker/headphone connection. Now:

  • Speaker and headphone connection no longer crashes
  • Audio continues working correctly during Discord calls with XRUNs
  • Volume control works without PipeWire deadlock

The T2 chip continuously sends timestamp events for all registered audio
devices regardless of whether an ALSA stream is open. Previously,
aaudio_handle_stream_timestamp() would call snd_pcm_period_elapsed() for
every substream that had a PCM, even when no audio was playing.

On PREEMPT(full) kernels this is fatal: the BCE DMA IRQ handler runs with
bottom halves disabled, so any call chain that reaches schedule() triggers
BUG: scheduling while atomic. The specific path is:

  bce_handle_dma_irq
    aaudio_handle_cmd_timestamp
      aaudio_handle_stream_timestamp
        snd_pcm_period_elapsed        <- called on stopped stream
          snd_pcm_update_state        <- ALSA detects XRUN, tries to stop
            snd_pcm_do_stop
              aaudio_pcm_trigger(STOP)
                aaudio_cmd_stop_io
                  __aaudio_send_cmd_sync
                    wait_for_completion_timeout  <- sleeps, crash

Fix this by returning early when stream->started is 0. This matches the
existing guard in aaudio_pcm_pointer() which already handles the same
condition.
aaudio_cmd_stop_io blocks waiting for a T2 firmware reply. When called
from PCM trigger(STOP), it can block PipeWire's main thread while the
RT data thread holds a lock the main thread needs, causing a deadlock.
It can also be called from atomic context (BCE DMA BH handler via
snd_pcm_period_elapsed -> snd_pcm_do_stop) causing scheduling-while-atomic.

Always defer the stop via schedule_work so trigger(STOP) returns
immediately. In trigger(START), use cancel_work_sync to cancel any
pending (not yet running) stop before starting, avoiding a start/stop
race without blocking callers unnecessarily.
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.

1 participant