[PATCH] PM: hibernate: Freeze kernel threads after image preallocation
From: Florian Schmaus via B4 Relay
Date: Sun Sep 20 2026 - 10:36:43 EST
From: Florian Schmaus <flo@xxxxxxxxxxxx>
Commit 783c81098445 ("PM: hibernate: call preallocate_image() after freeze
prepare") moved hibernate_preallocate_memory() after dpm_prepare() so
that device drivers have the opportunity to release pinned/unswappable
memory during their ->prepare() callback before memory is preallocated
for the snapshot image.
However, that commit also placed hibernate_preallocate_memory() after
freeze_kernel_threads(). While it was assumed during review that swap
I/O submitted via submit_bio() is synchronous and would not depend on
frozen kernel threads, this does not hold in practice. Calling
hibernate_preallocate_memory() with kernel threads frozen leads to
intermittent deadlocks during hibernation.
Inside hibernate_preallocate_memory(), shrink_all_memory() is invoked with
.may_writepage = 1 and .may_swap = 1 to aggressively reclaim and swap out
pages. Any writeback or swap I/O that relies on freezable kernel threads,
block device helpers, or WQ_FREEZABLE workqueues (such as those in storage
drivers, device mapper, or filesystems) deadlocks waiting on tasks that
are stuck in the refrigerator.
Fix this by reordering hibernation_snapshot():
1. Call dpm_prepare(PMSG_FREEZE) first, allowing device drivers to release
pinned resources while kernel threads are still active.
2. Call hibernate_preallocate_memory() second, performing page reclaim and
swapout while storage layers, workqueues, and kernel threads are alive.
3. Call freeze_kernel_threads() third, only after all memory preallocation
and swap I/O have completed.
Additionally, restore the call to swsusp_free() in the cleanup path so
that preallocated image memory is properly freed if freeze_kernel_threads()
fails or if TEST_FREEZER is enabled.
Fixes: 783c81098445 ("PM: hibernate: call preallocate_image() after freeze prepare")
Signed-off-by: Florian Schmaus <flo@xxxxxxxxxxxx>
---
During the review of commit 783c81098445 ("PM: hibernate: call
preallocate_image() after freeze prepare") [1], concerns were raised
regarding whether memory reclaim and swap I/O could deadlock if kernel
threads were already frozen.
At the time, it was thought that pageout to swap would not depend on
frozen threads. However, on Linux 7.2, I ran into reliable issues with
suspend-to-disk hanging during hibernation. Reordering the sequence so
that kernel threads are frozen after image preallocation (as done in
this patch) fixes the issue for me.
This patch restores the ordering where kernel threads are frozen only after
memory preallocation and swap I/O have completed, while keeping the
benefit of calling dpm_prepare() beforehand so drivers can release pinned
pages.
[1] https://patch.msgid.link/20260403-hibernation-fixes-v3-1-31bc9fa3ba2d@xxxxxxxxxxxxx
---
kernel/power/hibernate.c | 26 ++++++++++++++------------
1 file changed, 14 insertions(+), 12 deletions(-)
diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c
index d2479c69d71a..c13f68ab7f6e 100644
--- a/kernel/power/hibernate.c
+++ b/kernel/power/hibernate.c
@@ -408,9 +408,18 @@ int hibernation_snapshot(int platform_mode)
if (error)
goto Close;
+ error = dpm_prepare(PMSG_FREEZE);
+ if (error)
+ goto Complete;
+
+ /* Preallocate image memory before freezing kernel threads and shutting down devices. */
+ error = hibernate_preallocate_memory();
+ if (error)
+ goto Complete;
+
error = freeze_kernel_threads();
if (error)
- goto Close;
+ goto Cleanup;
if (hibernation_test(TEST_FREEZER)) {
@@ -422,15 +431,6 @@ int hibernation_snapshot(int platform_mode)
goto Thaw;
}
- error = dpm_prepare(PMSG_FREEZE);
- if (error)
- goto Complete;
-
- /* Preallocate image memory before shutting down devices. */
- error = hibernate_preallocate_memory();
- if (error)
- goto Complete;
-
console_suspend_all();
pm_restrict_gfp_mask();
@@ -464,10 +464,12 @@ int hibernation_snapshot(int platform_mode)
platform_end(platform_mode);
return error;
- Complete:
- dpm_complete(PMSG_RECOVER);
Thaw:
thaw_kernel_threads();
+ Cleanup:
+ swsusp_free();
+ Complete:
+ dpm_complete(PMSG_RECOVER);
goto Close;
}
---
base-commit: 40288c9206c17eb66a603262e06a58d300d0f279
change-id: 20260915-fix-hibernation-aad94ce17506
Best regards,
--
Florian Schmaus <flo@xxxxxxxxxxxx>