Hibernate resume failures on Framework 13 AMD (Ryzen AI 300 / Krackan): A/B-tested workaround — `pm_async=0`

Below the horizontal rule is an AI-generated summary of the issues I was having and how I solved it. I struggled for quite a while to diagnose this in my free time. I have confirmed 100% this is the fix. Apologies if this is a duplicate or if it’s at all foolish. I don’t have much time to spend on this, but I was thinking it’d be good of me to share what worked, notwithstanding. Debian Sid, BTW.


Hibernate resume failures on Framework 13 AMD (Ryzen AI 300 / Krackan): A/B-tested workaround — pm_async=0

TL;DR: After months of intermittent (~15%) hibernate-resume failures — black-screen freeze or instant reset right after typing the LUKS passphrase — setting /sys/power/pm_async to 0 gave many consecutive clean cycles. Flipping it back to 1 (the kernel default) reproduced the failure on the very first cycle. This looks like an async device-resume ordering race (most likely amdgpu) in the restored kernel, and serializing device PM works around it. Would love others on AI 300 hardware to A/B this and report back.

Hardware / software

  • Framework Laptop 13, Ryzen AI 7 350 / Radeon 860M (“Krackan”, RDNA 3.5, gfx1152), BIOS 03.05

  • Debian sid, kernel 7.0.10, LVM-on-LUKS, 64 GB swapfile, HibernateMode=shutdown, initramfs resume target aligned to the swapfile (resume= + resume_offset=)

Symptom

Hibernate write works every time (machine powers off properly). The failure is on restore: power on → GRUB → type LUKS passphrase → resume starts → either freezes on a black screen or instantly resets back to a second LUKS prompt, and you cold-boot with the session lost. Roughly 1 cycle in 7 over a 10-day tally.

Two things that confused diagnosis for months, in case they’re confusing yours:

  1. The failed restore consumes the swap signature, so the next boot logs PM: Image not found (code -22). That line looks like the image was never written — it wasn’t; it’s the tombstone of a restore that started and died.

  2. The crash happens before journald exists, so the failing boot logs nothing. Even panic=30 + full SysRq + efi_pstore captured zero bytes — the instant-reset variant dies below the kernel’s reach.

The experiment

As instrumentation (not a fix), I set pm_async=0 + pm_print_times=1, so device resume would run serially and a frozen screen’s last line would name the culprit device. With that in place: many, many consecutive clean hibernate/resume cycles — suspiciously clean for a ~15% bug.

So I flipped it back: echo 1 | sudo tee /sys/power/pm_async, hibernated once. First restore attempt: instant reset after the LUKS passphrase. Same kernel, same config, same evening — the only variable was the knob.

Why the knob matters on the restore side: pm_async lives in kernel memory, so its value is saved inside the hibernation image and governs the restored kernel’s device re-init — exactly the window where these failures strike. 1 = devices resume concurrently (ordering races possible), 0 = serially in dependency order.

Try it

echo 0 | sudo tee /sys/power/pm_async

To persist (there’s no kernel cmdline parameter for this; a tmpfiles rule is the canonical way):

# /etc/tmpfiles.d/pm-async-workaround.conf
w /sys/power/pm_async - - - - 0

Cost: device suspend/resume runs serially, so hibernate entry/exit is somewhat slower. No functional downside I’ve found.

Honest caveats

  • n=1 machine. ~A dozen clean cycles at 0 plus a first-try failure at 1 is about a 2% coincidence under my old failure rate — strong, not proof.

  • This may be the same bug as the “hibernate is spotty at best” reports in the AI 300 threads (and possibly the resume freezes where dcdebugmask=0x10, sg_display=0, cwsr_enable=0, BIOS/firmware updates, and kernel hopping didn’t help — none of them helped here either), but I can’t confirm that for anyone else’s machine.

  • If you A/B it, toggle both directions and count cycles — one clean cycle means nothing with an intermittent bug.

If this holds for others, it’s also a useful data point for the amdgpu maintainers: it would localize the failure to an async ordering dependency in the restore path rather than a flat driver bug.

Follow-up: the instant-reset variant isn’t fully “below kernel reach” — it’s a data fabric sync flood (S5_RESET_STATUS = 0x08000800)

Update to my own post. I said the instant-reset variant “dies below the kernel’s reach” and captured zero bytes. True at the moment of the crash — but it turns out AMD stashes the reason for an unexpected reset in an FCH register (S5_RESET_STATUS) that survives into the next boot, and since Linux 6.16 the kernel reads and prints it at startup. So the failing boot logs nothing, but the boot after it does.

After one of these instant resets, the next boot logged:

$ sudo journalctl -b 0 -k | grep -i 'previous system reset'
x86/amd: Previous system reset reason [0x08000800]: an uncorrected error caused a data fabric sync flood event

So the instant-reset variant is: S4 restore → uncorrected Infinity Fabric error → sync flood → full SoC reset. The fabric resets the CPU itself, which is why panic=30 / SysRq / efi_pstore never caught anything — by then there’s no CPU left to run them.

What I think this tightens:

  • It’s not an amdgpu driver bug in the usual sense. A display-driver fault doesn’t sync-flood the fabric, which fits why dcdebugmask=0x10, sg_display=0, and cwsr_enable=0 did nothing here.
  • It’s a fabric / PCIe-root-complex / memory-controller fault on the restore path. That lines up with the pm_async sensitivity: serializing device resume changes the order fabric clients re-init, and an async ordering race provoking an uncorrected fabric error is exactly the kind of thing that’d be timing-dependent. pm_async=0 narrows the window; the sync flood says it doesn’t fully close it.
  • It lives in the platform-firmware layer (AGESA/PMFW). BIOS 4.02 didn’t bump AMD PI (still 1.1.0.0c), which may be why firmware updates haven’t moved it. The next one to watch is whatever bumps AGESA.

Asks, same as before but sharper:

  1. If you get the reboot variant, please check journalctl -b 0 -k | grep -i 'previous system reset' on the next boot and post your value. Trying to confirm whether everyone’s instant-reset is the same 0x08000800 sync flood or something else.
  2. Anyone on AGESA newer than 1.1.0.0c — does it still sync-flood?

Still n=1 here. But “uncorrected data fabric sync flood on S4 restore” is at least a specific thing to hand the amdgpu / AGESA folks instead of “resume is flaky.”

For the “sync flood”, it might be worth reading:

Specifically, the bit about “aer”