History

PM: hibernate: Drain trailing zero pages on userspace restore

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.

Alberto Garcia committed 8 months ago

Commit 734eba61 parent c3692991 file · +11 −0Browse files

Unified · Split

kernel/power/snapshot.c+11−0
@@ -2855,6 +2855,17 @@ int snapshot_write_finalize(struct snapshot_handle *handle) · 2854 unmodified lines
2855{2855{
2856 int error;2856 int error;
2857 2857
2858 /*
2859 * Call snapshot_write_next() to drain any trailing zero pages,
2860 * but make sure we're in the data page region first.
2861 * This function can return PAGE_SIZE if the kernel was expecting
2862 * another copy page. Return -ENODATA in that situation.
2863 */
2864 if (handle->cur > nr_meta_pages + 1) {
2865 error = snapshot_write_next(handle);
2866 if (error)
2867 return error > 0 ? -ENODATA : error;
2868 }
2858 copy_last_highmem_page();2869 copy_last_highmem_page();
2859 error = hibernate_restore_protect_page(handle->buffer);2870 error = hibernate_restore_protect_page(handle->buffer);
2860 /* Do that only if we have loaded the image entirely */2871 /* Do that only if we have loaded the image entirely */