[SL7 Intel] s2idle hangs indefinitely in dw8250_suspend on the SAM UART — soft lockup, free_irq never completes
Surface Laptop 7 for Business (Intel, Lunar Lake). I think I've found the cause of the intermittent "closes lid, never wakes, battery flat" failures on this machine, and it isn't any of the usual suspects. Posting the evidence in case it's useful to others here and to see whether anyone recognises a fix.
Summary: suspend hangs in dw8250_suspend on ttyS4 — the DesignWare UART that carries the Surface Aggregator Module. free_irq() waits in __synchronize_hardirq() for the UART interrupt handler to go idle, and it never does, because IRQ 16 is storming. The result is a soft lockup with a CPU pinned for as long as the machine is left closed.
Environment
Hardware Microsoft Corporation / Surface Laptop for Business 7th Edition with Intel
BIOS 17.101.143 (2025-11-19)
Kernel 6.19.8-surface-3 (linux-image-surface 6.19.8-surface-3)
SAM firmware 11.103.139
Distro Ubuntu 26.04
cmdline ... quiet splash acpi_mask_gpe=0xAB crashkernel=...
Sleep state s2idle (no S3 on this platform)
The hang
watchdog: BUG: soft lockup - CPU#1 stuck for 33170s! [systemd-sleep:13024]
33,170 s — 9.2 hours, i.e. the entire time the lid was closed. Trace:
CPU: 1 UID: 0 PID: 13024 Comm: systemd-sleep 6.19.8-surface-3 #3 PREEMPT(voluntary)
RIP: 0010:_raw_spin_unlock_irqrestore+0x21/0x60
Call Trace:
<TASK>
__synchronize_hardirq+0x94/0xd0
__synchronize_irq+0x2b/0xb0
free_irq+0x11e/0x2f0
univ8250_release_irq+0xd8/0xe0
serial8250_do_shutdown+0x1eb/0x340
serial8250_shutdown+0x26/0x30
uart_suspend_port+0x291/0x480
serial8250_suspend_port+0x4f/0xb0
dw8250_suspend+0x14/0x20 [8250_dw]
platform_pm_suspend+0x2b/0x70
dpm_run_callback+0x93/0x200
device_suspend+0x18a/0x650
dpm_suspend+0x1c7/0x3a0
dpm_suspend_start+0x6c/0x80
suspend_devices_and_enter+0x177/0xa10
pm_suspend+0x224/0x5b0
state_store+0x85/0xf0
...
entry_SYSCALL_64_after_hwframe+0x76/0x7e
The port is the SAM transport
Serial: 8250/16550 driver, 32 ports, IRQ sharing enabled
dw-apb-uart.4: ttyS4 at MMIO 0x202c004000 (irq = 16, base_baud = 6250000) is a 16550A
serial serial0: tty port ttyS4 registered
surface_serial_hub serial0-0: SAM firmware version: 11.103.139
So ttyS4 = dw-apb-uart.4 = serial0 = the MSHW0551 hub carrying keyboard and touchpad. This is why the hang is unrecoverable rather than merely annoying - the input transport is the thing that's wedged.
Why free_irq never returns: IRQ 16 is storming
16: 0 0 0 0 1561055591 0 0 0 IR-IO-APIC 16-fasteoi idma64.4, ttyS4
1,561,055,591 interrupts in 19.4 h of uptime — ~22,500/s average, and 17× more than the system timer (LOC = 91,043,998). All on a single CPU. IRQ 16 is shared between idma64.4 and ttyS4.
With the line permanently active, __synchronize_hardirq() can never observe the handler idle, so free_irq() spins forever and dw8250_suspend never returns.
The storm is episodic, not continuous — sampling /proc/interrupts while idle often shows 0/s. That matches the failure being intermittent: over ~3 months I have 33 suspends that never resumed, interleaved with many that worked fine. Anything
evaluated over a handful of suspend cycles can therefore look like a fix by luck; I wasted a fair amount of time on that.
Corroboration: the SAM RX parser sees garbage at the same instant
Sep 01 08:21:03 surface_serial_hub serial0-0: rx: parser: invalid start of frame, skipping
Sep 01 08:21:03 surface_serial_hub serial0-0: rx: parser: invalid payload CRC
Sep 01 08:21:03 surface_serial_hub serial0-0: rx: parser: invalid start of frame, skipping
Same timestamp as the lockup. The UART is delivering junk into the SAM frame parser, which is consistent with a bogus RX-timeout interrupt condition rather than real traffic.
Things I ruled out first, in case it saves anyone else the effort
- Zombie EVDI devices (DisplayLink). Four phantom DRM cards were being created at boot via
/etc/modules-load.d/evdi.conf + initial_device_count=4. Removing them was worth doing, but did not fix the hangs.
- GPE 0xAB.
acpi_mask_gpe=0xAB is applied and effective (ACPI: Masking GPE 0xab., counter reads 0 enabled masked), yet \_GPE._LAB still storms (Could not resolve symbol [\_GPE._LAB.SLAB], AE_NOT_FOUND). Necessary but not sufficient, and not this bug.
- Bluetooth. A sleep hook here unloaded
btintel_pcie on suspend; the crash-time module list reads [last unloaded: btintel]. That makes Bluetooth appear to crash (the post hook never runs, so it stays unloaded until reboot) but it is a consequence of the hang, not a cause. Removing the hook changed nothing about the hangs.
pm_trace. ⚠️ Warning to others: arming /sys/power/pm_trace on this machine makes suspend impossible. It writes its hash to the RTC and that itself generates an immediate wake, so every attempt bounces straight back out with PM: Some devices failed to suspend, or early wake event detected. It corrupted my RTC to 2036 and cost a flat battery. Use pm_debug_messages instead.
- Hibernation. Worth masking
hibernate.target / hybrid-sleep.target here:
/sys/power/resume is 0:0 and swap is an 8 G file against 30 GiB RAM, so a failed s2idle escalating to hibernate produces a second, guaranteed hang.
How to check whether you're affected
grep -E '^\s*16:' /proc/interrupts # or whichever IRQ lists ttyS4
A count in the hundreds of millions means the storm has been running. To get a live rate, sample twice a few seconds apart. If it's high, don't trust suspend.
Questions
- Does anyone recognise this as the known DesignWare bogus RX-timeout condition (cf.
serial: 8250_dw: Avoid "too much work" from bogus rx timeout interrupt)?
Is that mitigation present in 6.19, and if not would it apply here?
idma64.4 shares IRQ 16 with ttyS4. Is the storm plausibly the DMA side rather than the UART? Is there a way to run the SAM UART without DMA to test that?
- Is there a reason
uart_suspend_port() needs to free_irq() at all on a port whose device must stay alive across s2idle — or would IRQF_NO_SUSPEND / keeping the IRQ registered be viable for the SAM port specifically?
Happy to test patches. Full dmesg from the affected boot and the previous boot are attached.
[SL7 Intel] s2idle hangs indefinitely in
dw8250_suspendon the SAM UART — soft lockup,free_irqnever completesSurface Laptop 7 for Business (Intel, Lunar Lake). I think I've found the cause of the intermittent "closes lid, never wakes, battery flat" failures on this machine, and it isn't any of the usual suspects. Posting the evidence in case it's useful to others here and to see whether anyone recognises a fix.
Summary: suspend hangs in
dw8250_suspendonttyS4— the DesignWare UART that carries the Surface Aggregator Module.free_irq()waits in__synchronize_hardirq()for the UART interrupt handler to go idle, and it never does, because IRQ 16 is storming. The result is a soft lockup with a CPU pinned for as long as the machine is left closed.Environment
The hang
33,170 s — 9.2 hours, i.e. the entire time the lid was closed. Trace:
The port is the SAM transport
So
ttyS4=dw-apb-uart.4=serial0= theMSHW0551hub carrying keyboard and touchpad. This is why the hang is unrecoverable rather than merely annoying - the input transport is the thing that's wedged.Why
free_irqnever returns: IRQ 16 is storming1,561,055,591 interrupts in 19.4 h of uptime — ~22,500/s average, and 17× more than the system timer (
LOC= 91,043,998). All on a single CPU. IRQ 16 is shared betweenidma64.4andttyS4.With the line permanently active,
__synchronize_hardirq()can never observe the handler idle, sofree_irq()spins forever anddw8250_suspendnever returns.The storm is episodic, not continuous — sampling
/proc/interruptswhile idle often shows 0/s. That matches the failure being intermittent: over ~3 months I have 33 suspends that never resumed, interleaved with many that worked fine. Anythingevaluated over a handful of suspend cycles can therefore look like a fix by luck; I wasted a fair amount of time on that.
Corroboration: the SAM RX parser sees garbage at the same instant
Same timestamp as the lockup. The UART is delivering junk into the SAM frame parser, which is consistent with a bogus RX-timeout interrupt condition rather than real traffic.
Things I ruled out first, in case it saves anyone else the effort
/etc/modules-load.d/evdi.conf+initial_device_count=4. Removing them was worth doing, but did not fix the hangs.acpi_mask_gpe=0xABis applied and effective (ACPI: Masking GPE 0xab., counter reads0 enabled masked), yet\_GPE._LABstill storms (Could not resolve symbol [\_GPE._LAB.SLAB], AE_NOT_FOUND). Necessary but not sufficient, and not this bug.btintel_pcieon suspend; the crash-time module list reads[last unloaded: btintel]. That makes Bluetooth appear to crash (theposthook never runs, so it stays unloaded until reboot) but it is a consequence of the hang, not a cause. Removing the hook changed nothing about the hangs.pm_trace./sys/power/pm_traceon this machine makes suspend impossible. It writes its hash to the RTC and that itself generates an immediate wake, so every attempt bounces straight back out withPM: Some devices failed to suspend, or early wake event detected. It corrupted my RTC to 2036 and cost a flat battery. Usepm_debug_messagesinstead.hibernate.target/hybrid-sleep.targethere:/sys/power/resumeis0:0and swap is an 8 G file against 30 GiB RAM, so a failed s2idle escalating to hibernate produces a second, guaranteed hang.How to check whether you're affected
A count in the hundreds of millions means the storm has been running. To get a live rate, sample twice a few seconds apart. If it's high, don't trust suspend.
Questions
serial: 8250_dw: Avoid "too much work" from bogus rx timeout interrupt)?Is that mitigation present in 6.19, and if not would it apply here?
idma64.4shares IRQ 16 withttyS4. Is the storm plausibly the DMA side rather than the UART? Is there a way to run the SAM UART without DMA to test that?uart_suspend_port()needs tofree_irq()at all on a port whose device must stay alive across s2idle — or wouldIRQF_NO_SUSPEND/ keeping the IRQ registered be viable for the SAM port specifically?Happy to test patches. Full
dmesgfrom the affected boot and the previous boot are attached.