]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
PM: hibernate: Drain trailing zero pages on userspace restore
authorAlberto Garcia <berto@igalia.com>
Mon, 9 Mar 2026 17:39:41 +0000 (18:39 +0100)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Mon, 23 Mar 2026 12:33:06 +0000 (13:33 +0100)
Commit 005e8dddd497 ("PM: hibernate: don't store zero pages in the
image file") added an optimization to skip zero-filled pages in the
hibernation image. On restore, zero pages are handled internally by
snapshot_write_next() in a loop that processes them without returning
to the caller.

With the userspace restore interface, writing the last non-zero page
to /dev/snapshot is followed by the SNAPSHOT_ATOMIC_RESTORE ioctl. At
this point there are no more calls to snapshot_write_next() so any
trailing zero pages are not processed, snapshot_image_loaded() fails
because handle->cur is smaller than expected, the ioctl returns -EPERM
and the image is not restored.

The in-kernel restore path is not affected by this because the loop in
load_image() in swap.c calls snapshot_write_next() until it returns 0.
It is this final call that drains any trailing zero pages.

Fixed by calling snapshot_write_next() in snapshot_write_finalize(),
giving the kernel the chance to drain any trailing zero pages.

Fixes: 005e8dddd497 ("PM: hibernate: don't store zero pages in the image file")
Signed-off-by: Alberto Garcia <berto@igalia.com>
Acked-by: Brian Geffon <bgeffon@google.com>
Link: https://patch.msgid.link/ef5a7c5e3e3dbd17dcb20efaa0c53a47a23498bb.1773075892.git.berto@igalia.com
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
kernel/power/snapshot.c

index 6e1321837c66876086c174230fc416933bb752ef..a564650734dcdceda7193ca3c1bc6b347cc1ec8b 100644 (file)
@@ -2855,6 +2855,17 @@ int snapshot_write_finalize(struct snapshot_handle *handle)
 {
        int error;
 
+       /*
+        * Call snapshot_write_next() to drain any trailing zero pages,
+        * but make sure we're in the data page region first.
+        * This function can return PAGE_SIZE if the kernel was expecting
+        * another copy page. Return -ENODATA in that situation.
+        */
+       if (handle->cur > nr_meta_pages + 1) {
+               error = snapshot_write_next(handle);
+               if (error)
+                       return error > 0 ? -ENODATA : error;
+       }
        copy_last_highmem_page();
        error = hibernate_restore_protect_page(handle->buffer);
        /* Do that only if we have loaded the image entirely */