apple_mbox_send() leaves irq_send_empty enabled when its wait for the A2I FIFO does not complete, so the next send that finds the FIFO full calls enable_irq() on an already-enabled interrupt. __enable_irq() warns and underflows the interrupt's disable depth, and every later stalled send warns again with a full backtrace.
Both failure paths are reachable in normal operation: -ETIMEDOUT whenever a coprocessor stalls, and a negative return whenever a send issued from a syscall path takes a signal. Every RTKit user of this mailbox goes through the function: sep, dcp, dcpext, aop, isp, and nvme. drivers/soc/apple/mailbox.c is byte-identical to mainline in this function, so the bug is not asahi-specific.
Environment
- Machine: MacBook Pro 16-inch 2021,
apple,j316s / apple,t6000 (M1 Pro)
- Kernel:
7.1.6-1-4-ARCH (Arch linux-asahi 7.1.6.asahi1 plus local patches)
- Coprocessor: SEP, mailbox interrupt 57
Steps to reproduce
- Stop a coprocessor from draining its A2I FIFO. On t6000, send
msg0 = 0x080d0000 to SEPOS's cntl endpoint (endpoint 0x00, type 0x0d) early in a boot; SEPOS then ignores the mailbox for the rest of the boot.
- Send until the FIFO is full. Eight messages fit.
- Send twice more.
Expected behavior
Each send that cannot place a message returns -ETIMEDOUT with the send-empty interrupt left disabled, and the kernel log stays clean.
Actual behavior
The first timed-out send is silent; every send after it warns:
[3572.897070] apple_sep 396400000.sep: debugfs send msg0=0x0000000008170000 msg1=0x0000000000000000
[3572.897510] ------------[ cut here ]------------
[3572.897561] Unbalanced enable for IRQ 57
[3572.897604] WARNING: kernel/irq/manage.c:774 at __enable_irq+0x4c/0x80, CPU#0: bash/22444
[3572.897730] CPU: 0 UID: 0 PID: 22444 Comm: bash Tainted: G S O 7.1.6-1-4-ARCH #1 PREEMPT(full)
[3572.897795] Hardware name: Apple Inc. MacBookPro18,1/J316s, BIOS 2026.04 04/01/2026
[3572.898225] Call trace:
[3572.898253] __enable_irq+0x4c/0x80 (P)
[3572.898276] enable_irq+0x74/0xe4
[3572.898307] apple_mbox_send+0xb4/0x1b8
[3572.898333] _RNvMs0_CsgSCLNLCOefh_3sepNtB5_7SepData8send_raw+0xa4/0xdc
[3572.898386] full_proxy_write+0x64/0xa0
[3572.898421] vfs_write+0xd0/0x3bc
[3572.898458] ksys_write+0x60/0xfc
[3572.898479] __arm64_sys_write+0x18/0x24
[3572.898506] invoke_syscall.constprop.0+0x48/0xe8
[3572.898532] do_el0_svc+0xa8/0xb8
[3572.898565] el0_svc+0x3c/0x1d8
[3572.898586] el0t_64_sync_handler+0xa0/0xe4
[3572.898617] el0t_64_sync+0x198/0x19c
[3572.898639] ---[ end trace 0000000000000000 ]---
Eight consecutive timed-out sends produced seven such WARNs. The backtraces also flushed the kernel ring buffer, costing the boot-time log of what SEPOS had advertised.
The sep frames come from a local research-only debugfs interface that calls apple_mbox_send(). Nothing in the failing path depends on it; any caller that finds the FIFO full twice reproduces the WARN.
Cause
irq_send_empty is requested with IRQF_NO_AUTOEN, so it starts disabled.
- The slow path in
apple_mbox_send() acks the interrupt at the mailbox level, calls enable_irq(), reinitializes tx_empty, drops tx_lock, and waits on wait_for_completion_interruptible_timeout().
- Only
apple_mbox_send_empty_irq() disables the interrupt again, and it runs only when the FIFO drains.
- The
t < 0 and t == 0 returns skip that disable, leaving the interrupt enabled at depth 0 with no handler having run.
Fix
See PR #599, which tracks the interrupt's state in struct apple_mbox under tx_lock and disables it on both failure paths.
apple_mbox_send()leavesirq_send_emptyenabled when its wait for the A2I FIFO does not complete, so the next send that finds the FIFO full callsenable_irq()on an already-enabled interrupt.__enable_irq()warns and underflows the interrupt's disable depth, and every later stalled send warns again with a full backtrace.Both failure paths are reachable in normal operation:
-ETIMEDOUTwhenever a coprocessor stalls, and a negative return whenever a send issued from a syscall path takes a signal. Every RTKit user of this mailbox goes through the function:sep,dcp,dcpext,aop,isp, andnvme.drivers/soc/apple/mailbox.cis byte-identical to mainline in this function, so the bug is not asahi-specific.Environment
apple,j316s/apple,t6000(M1 Pro)7.1.6-1-4-ARCH(Archlinux-asahi7.1.6.asahi1 plus local patches)Steps to reproduce
msg0 = 0x080d0000to SEPOS'scntlendpoint (endpoint0x00, type0x0d) early in a boot; SEPOS then ignores the mailbox for the rest of the boot.Expected behavior
Each send that cannot place a message returns
-ETIMEDOUTwith the send-empty interrupt left disabled, and the kernel log stays clean.Actual behavior
The first timed-out send is silent; every send after it warns:
Eight consecutive timed-out sends produced seven such WARNs. The backtraces also flushed the kernel ring buffer, costing the boot-time log of what SEPOS had advertised.
The
sepframes come from a local research-only debugfs interface that callsapple_mbox_send(). Nothing in the failing path depends on it; any caller that finds the FIFO full twice reproduces the WARN.Cause
irq_send_emptyis requested withIRQF_NO_AUTOEN, so it starts disabled.apple_mbox_send()acks the interrupt at the mailbox level, callsenable_irq(), reinitializestx_empty, dropstx_lock, and waits onwait_for_completion_interruptible_timeout().apple_mbox_send_empty_irq()disables the interrupt again, and it runs only when the FIFO drains.t < 0andt == 0returns skip that disable, leaving the interrupt enabled at depth 0 with no handler having run.Fix
See PR #599, which tracks the interrupt's state in
struct apple_mboxundertx_lockand disables it on both failure paths.