From: "Nícolas F. R. A. Prado" <nfraprado@collabora.com>
To: "Rafael J. Wysocki" <rafael@kernel.org>
Cc: Len Brown <lenb@kernel.org>, Pavel Machek <pavel@kernel.org>,
Brian Geffon <bgeffon@google.com>,
kernel@collabora.com, linux-pm@vger.kernel.org,
linux-kernel@vger.kernel.org
Subject: Re: [PATCH 3/3] PM: hibernate: Allow disabling zero page check in copy_data_pages()
Date: Mon, 01 Jun 2026 18:52:29 -0400 [thread overview]
Message-ID: <c4176f8a63cd537a1ced7325079f5e7da30fb7fe.camel@collabora.com> (raw)
In-Reply-To: <CAJZ5v0iHezk28gWu+wNni01wi73bV7vkqmVKXD14Gtk+CKyGDg@mail.gmail.com>
On Tue, 2026-05-26 at 13:53 +0200, Rafael J. Wysocki wrote:
> On Mon, May 18, 2026 at 11:34 PM Nícolas F. R. A. Prado
> <nfraprado@collabora.com> wrote:
> >
> > The current implementation of copy_data_pages() has at its center
> > do_copy_page(), which copies data of all saveable pages in a loop
> > one
> > long at a time, while checking whether the page is fully zero to
> > allow
> > it to not be saved in the hibernation image.
> >
> > As the comment above do_copy_page() mentions, this loop was used
> > instead
> > of the more optimized copy_page() and memcpy() due to them
> > depending on
> > fpu_begin()/fpu_end() (see [1] and commit 95018f7c94cb ("[PATCH]
> > swsusp:
> > do not use memcpy for snapshotting memory")). However, since commit
> > c6dbd3e5e69c ("x86/mmx_32: Remove X86_USE_3DNOW"), this limitation
> > no
> > longer holds, and it only affected x86-32 in the first place.
> >
> > From testing it is clear that removing the zero page check and
> > using
> > copy_page() makes it almost 3 times quicker:
> > - Current:
> > PM: hibernation: Copied 4940240 kbytes in 11.33 seconds (436.03
> > MB/s)
> > - With just the zero check removed:
> > PM: hibernation: Copied 4974664 kbytes in 9.03 seconds (550.90
> > MB/s)
> > - With the zero check removed and using copy_page() instead:
> > PM: hibernation: Copied 6275216 kbytes in 3.96 seconds (1584.65
> > MB/s)
> >
> > Given that copy_data_pages() runs inside a critical section where
> > only
> > a single CPU is online and syscore is suspended, it should be kept
> > as
> > short as possible to keep the system responsive.
>
> I'm not sure if being responsive during hibernation is all that
> important.
>
> Also, the change is about using copy_page() to reduce the time spent
> in syscore_suspend(), so the subject is a bit confusing.
>
> > While switching from the loop to copy_page() brings big speed
> > improvements, it also makes the zero page check much costlier since
> > it
> > can no longer be done in the middle of the copy. In fact upon
> > testing
> > adding the zero check alongside copy_page() made it slower than the
> > current code.
>
> Guess what, CPUs have caches.
>
> > The following shows a rough comparison of a few more metrics
> > between the
> > current code and when both copy_page() is used and the zero page
> > check
> > is disabled:
> > - Total time to hibernate:
> > - before: 13.77s
> > - after: 14.13s
>
> Score for the current code.
>
> > - Total time to resume:
> > - before: 5.85s
> > - after: 5.85s
> > - Total time in syscore_suspend():
> > - before: 11.3s
> > - after: 4.08s
>
> Why does this matter?
Reducing the time spent here means reduced time between
pm_wakeup_pending() checks, which ultimately means the kernel is
quicker to abort hibernation in reaction to a pending wakeup.
Thanks,
Nícolas
>
> > - Total image size written to disk:
> > - before: 4956296kB => 2606402kB compressed = 2.60MB
> > - after: 6274608 => 2616624kB compressed = 2.61MB
>
> "kB" seems to be missing in the bottom row.
>
> Which means that this is a score for the current code again because
> it
> needs to copy and compress fewer pages which means that less energy
> needs to be used (which may be kind of relevant in low-battery
> situations).
>
> > As can be seen the total time to hibernate is roughly the same,
> > suggesting that the time saved with the more efficient copy_page()
> > is
> > payed by having to compress more data (the zero pages). The time to
> > resume remains the same. And the hibernation image size is
> > basically the
> > same, as the zero pages compress well (the case would be quite
> > different
> > if compression is disabled). The big win here is that the time
> > between
> > syscore_suspend() and syscore_resume() is much lower, meaning the
> > system
> > will be more responsive throughout hibernation.
> >
> > Expose a 'nozerocheck' module parameter
>
> I'd rather not.
>
> Either the switch-over is compelling enough to do it unconditionally,
> or it is not, in which case the existing code can be used just fine.
>
> As it stands, I'm not convinced.
>
> > to allow the zero page check to
> > be disabled and the faster copy_page() to be used, giving the
> > option for
> > userspace to choose between a more responsive system and reducing
> > the
> > disk usage particularly when compression is disabled.
> >
> > Testing was done on the SteamDeck OLED.
> >
> > [1]
> > https://lore.kernel.org/all/1096877559.9064.45.camel@desktop.cunninghams/
> >
> > Signed-off-by: Nícolas F. R. A. Prado <nfraprado@collabora.com>
> > ---
> > kernel/power/snapshot.c | 37 ++++++++++++++++++++++++++-----------
> > 1 file changed, 26 insertions(+), 11 deletions(-)
> >
> > diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c
> > index 4f452baf31dc..00feac18faae 100644
> > --- a/kernel/power/snapshot.c
> > +++ b/kernel/power/snapshot.c
> > @@ -40,6 +40,9 @@
> >
> > #include "power.h"
> >
> > +static bool nozerocheck;
> > +module_param(nozerocheck, bool, 0644);
> > +
> > #if defined(CONFIG_STRICT_KERNEL_RWX) &&
> > defined(CONFIG_ARCH_HAS_SET_MEMORY)
> > static bool hibernate_restore_protection;
> > static bool hibernate_restore_protection_active;
> > @@ -1425,11 +1428,9 @@ static unsigned int count_data_pages(void)
> > }
> >
> > /*
> > - * This is needed, because copy_page and memcpy are not usable for
> > copying
> > - * task structs. Returns true if the page was filled with only
> > zeros,
> > - * otherwise false.
> > + * Returns true if the page was filled with only zeros, otherwise
> > false.
> > */
> > -static inline bool do_copy_page(long *dst, long *src)
> > +static inline bool do_copy_page_zerocheck(long *dst, long *src)
> > {
> > long z = 0;
> > int n;
> > @@ -1441,6 +1442,12 @@ static inline bool do_copy_page(long *dst,
> > long *src)
> > return !z;
> > }
> >
> > +static inline bool do_copy_page_nozerocheck(long *dst, long *src)
> > +{
> > + copy_page(dst, src);
> > + return false;
> > +}
> > +
> > /**
> > * safe_copy_page - Copy a page in a safe way.
> > *
> > @@ -1450,7 +1457,8 @@ static inline bool do_copy_page(long *dst,
> > long *src)
> > * always returns 'true'. Returns true if the page was entirely
> > composed of
> > * zeros, otherwise it will return false.
> > */
> > -static bool safe_copy_page(void *dst, struct page *s_page)
> > +static bool safe_copy_page(void *dst, struct page *s_page,
> > + bool (*do_copy_page)(long *dst, long
> > *src))
> > {
> > bool zeros_only;
> >
> > @@ -1471,7 +1479,8 @@ static inline struct page
> > *page_is_saveable(struct zone *zone, unsigned long pfn
> > saveable_highmem_page(zone, pfn) :
> > saveable_page(zone, pfn);
> > }
> >
> > -static bool copy_data_page(unsigned long dst_pfn, unsigned long
> > src_pfn)
> > +static bool copy_data_page(unsigned long dst_pfn, unsigned long
> > src_pfn,
> > + bool (*do_copy_page)(long *dst, long
> > *src))
> > {
> > struct page *s_page, *d_page;
> > void *src, *dst;
> > @@ -1491,12 +1500,13 @@ static bool copy_data_page(unsigned long
> > dst_pfn, unsigned long src_pfn)
> > * The page pointed to by src may contain
> > some kernel
> > * data modified by kmap_atomic()
> > */
> > - zeros_only = safe_copy_page(buffer,
> > s_page);
> > + zeros_only = safe_copy_page(buffer, s_page,
> > do_copy_page);
> > dst = kmap_local_page(d_page);
> > copy_page(dst, buffer);
> > kunmap_local(dst);
> > } else {
> > - zeros_only =
> > safe_copy_page(page_address(d_page), s_page);
> > + zeros_only =
> > safe_copy_page(page_address(d_page),
> > + s_page,
> > do_copy_page);
> > }
> > }
> > return zeros_only;
> > @@ -1504,10 +1514,12 @@ static bool copy_data_page(unsigned long
> > dst_pfn, unsigned long src_pfn)
> > #else
> > #define page_is_saveable(zone, pfn) saveable_page(zone, pfn)
> >
> > -static inline int copy_data_page(unsigned long dst_pfn, unsigned
> > long src_pfn)
> > +static inline int
> > +copy_data_page(unsigned long dst_pfn, unsigned long src_pfn,
> > + bool (*do_copy_page)(long *dst, long *src))
> > {
> > return safe_copy_page(page_address(pfn_to_page(dst_pfn)),
> > - pfn_to_page(src_pfn));
> > + pfn_to_page(src_pfn),
> > do_copy_page);
> > }
> > #endif /* CONFIG_HIGHMEM */
> >
> > @@ -1524,6 +1536,9 @@ static unsigned long copy_data_pages(struct
> > memory_bitmap *copy_bm,
> > unsigned long copied_pages = 0;
> > struct zone *zone;
> > unsigned long pfn, copy_pfn;
> > + bool (*do_copy_page)(long *dst, long *src);
> > +
> > + do_copy_page = nozerocheck ? do_copy_page_nozerocheck :
> > do_copy_page_zerocheck;
> >
> > for_each_populated_zone(zone) {
> > unsigned long max_zone_pfn;
> > @@ -1541,7 +1556,7 @@ static unsigned long copy_data_pages(struct
> > memory_bitmap *copy_bm,
> > pfn = memory_bm_next_pfn(orig_bm);
> > if (unlikely(pfn == BM_END_OF_MAP))
> > break;
> > - if (copy_data_page(copy_pfn, pfn)) {
> > + if (copy_data_page(copy_pfn, pfn, do_copy_page)) {
> > memory_bm_set_bit(zero_bm, pfn);
> > /* Use this copy_pfn for a page that is not
> > full of zeros */
> > continue;
> >
> > --
> > 2.53.0
> >
next prev parent reply other threads:[~2026-06-01 22:52 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-18 21:34 [PATCH 0/3] Shrink time spent " Nícolas F. R. A. Prado
2026-05-18 21:34 ` [PATCH 1/3] PM: hibernate: Print speed statistics of copy_data_pages() Nícolas F. R. A. Prado
2026-05-26 11:25 ` Rafael J. Wysocki
2026-06-01 21:15 ` Nícolas F. R. A. Prado
2026-05-18 21:34 ` [PATCH 2/3] PM: hibernate: Add platform_enter hibernation test level Nícolas F. R. A. Prado
2026-05-26 11:28 ` Rafael J. Wysocki
2026-06-01 22:29 ` Nícolas F. R. A. Prado
2026-05-18 21:34 ` [PATCH 3/3] PM: hibernate: Allow disabling zero page check in copy_data_pages() Nícolas F. R. A. Prado
2026-05-26 11:53 ` Rafael J. Wysocki
2026-06-01 22:52 ` Nícolas F. R. A. Prado [this message]
2026-06-02 8:04 ` Rafael J. Wysocki
2026-06-04 1:04 ` Nícolas F. R. A. Prado
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=c4176f8a63cd537a1ced7325079f5e7da30fb7fe.camel@collabora.com \
--to=nfraprado@collabora.com \
--cc=bgeffon@google.com \
--cc=kernel@collabora.com \
--cc=lenb@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pm@vger.kernel.org \
--cc=pavel@kernel.org \
--cc=rafael@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Powered by JetHome