* [PATCH v3] mm: bypass swap readahead for zswap
@ 2026-10-07 15:39 Alexandre Ghiti
2026-10-07 21:04 ` Barry Song
` (4 more replies)
0 siblings, 5 replies; 15+ messages in thread
From: Alexandre Ghiti @ 2026-10-07 15:39 UTC (permalink / raw)
To: akpm, hannes, yosry, nphamcs
Cc: shikemeng, baoquan.he, youngjun.park, chengming.zhou, david, ljs,
liam, vbabka, rppt, surenb, mhocko, kasong, ryncsn, chrisl,
baohua, usama.arif, linux-mm, linux-kernel, Alexandre Ghiti
Commit 0bcac06f27d7 ("mm, swap: skip swapcache for swapin of synchronous
device") made SWP_SYNCHRONOUS_IO devices (e.g. zram) skip swap readahead.
zswap is the same kind of in-memory, synchronous backend as zram, not a
swap device flagged SWP_SYNCHRONOUS_IO so it still goes through
swapin_readahead().
Here are the results from bypassing readahead for zswap too: it was
measured with a kernel build (make -j16) in a memcg, zswap=zstd, shrinker
off, on Sapphire Rapids and 3 iterations.
768M memcg (sustained swap thrash):
metric mm-new + bypass delta
build time (s) 405.0 341.7 -15.6%
zswap-in (GB) 79.5 53.0 -33%
zswap-out (GB) 144.8 115.6 -20%
swap readahead (pages) 6.79M 0.45M -93%
swap_ra hit (%) 72.1 89.9 +18pp
1G memcg (light pressure, build not memory-bound):
metric mm-new + bypass delta
build time (s) 177.7 176.0 ~same (no regression)
zswap-in (GB) 10.2 7.5 -26%
zswap-out (GB) 27.7 25.1 -9%
swap readahead (pages) 1.07M 0.08M -93%
swap_ra hit (%) 68.6 87.2 +19pp
Similar gains were observed on an AMD EPYC 7D13 host.
The gain is from no longer prefetching pages that are pointless for an
in-memory backend: readahead inflates anon residency and thrashes the
page cache (file pages get evicted and re-read), lengthens each fault by
synchronously (de)compressing a cluster of neighbours, and adds
compression traffic when those extra pages are reclaimed.
Bypassing swap readahead for zswap therefore makes sense.
Suggested-by: Usama Arif <usama.arif@linux.dev>
Signed-off-by: Alexandre Ghiti <alex@ghiti.fr>
---
Changes in v3:
- Go back to the RFC's single patch: readahead windows that mix zswap
and disk entries will be dealt with later, the priority is to merge the
zswap readahead bypass
- Move the check into a swap helper, swap_entry_synchronous(), instead
of open-coding it in do_swap_page() (David)
- Bail out early when zswap was never enabled or the tree is empty, so
disk-only systems pay a static branch (Kairui, Nhat)
- Reuse zswap_is_present() rather than adding zswap_present_test()
(Yosry)
- Rebase on mm-new
Changes in v2:
- Keep readahead, but overlap the decompression of zswap neighbours with
the disk I/O of the window, and bypass it when the target needs no
disk I/O (Yosry, Nhat, Barry)
v2: https://lore.kernel.org/all/20260722163536.773166-1-alex@ghiti.fr/
RFC: https://lore.kernel.org/all/20260624075700.751467-1-alex@ghiti.fr/
include/linux/zswap.h | 6 ++++++
mm/memory.c | 4 ++--
mm/swap.h | 25 +++++++++++++++++++++++++
mm/zswap.c | 5 ++++-
4 files changed, 37 insertions(+), 3 deletions(-)
diff --git a/include/linux/zswap.h b/include/linux/zswap.h
index df6cafbe95dc..94746fb71bb6 100644
--- a/include/linux/zswap.h
+++ b/include/linux/zswap.h
@@ -35,6 +35,7 @@ void zswap_lruvec_state_init(struct lruvec *lruvec);
void zswap_folio_swapin(struct folio *folio);
bool zswap_is_enabled(void);
bool zswap_never_enabled(void);
+bool zswap_is_present(swp_entry_t entry, unsigned int nr);
#else
struct zswap_lruvec_state {};
@@ -73,6 +74,11 @@ static inline bool zswap_never_enabled(void)
return true;
}
+static inline bool zswap_is_present(swp_entry_t entry, unsigned int nr)
+{
+ return false;
+}
+
#endif
#endif /* _LINUX_ZSWAP_H */
diff --git a/mm/memory.c b/mm/memory.c
index 330cde31bf8b..396bc71a265f 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -5030,8 +5030,8 @@ vm_fault_t do_swap_page(struct vm_fault *vmf)
if (folio)
swap_update_readahead(folio, vma, vmf->address);
if (!folio) {
- /* Swapin bypasses readahead for SWP_SYNCHRONOUS_IO devices */
- if (data_race(si->flags & SWP_SYNCHRONOUS_IO))
+ /* Swapin bypasses readahead for SWP_SYNCHRONOUS_IO devices and zswap */
+ if (swap_entry_synchronous(si, entry))
folio = swapin_sync(entry, GFP_HIGHUSER_MOVABLE,
thp_swapin_suitable_orders(vmf) | BIT(0),
vmf, NULL, 0);
diff --git a/mm/swap.h b/mm/swap.h
index d5bf21f517dc..b8181bd056ee 100644
--- a/mm/swap.h
+++ b/mm/swap.h
@@ -94,6 +94,7 @@ static inline int mem_cgroup_swappiness(const struct mem_cgroup *memcg)
#ifdef CONFIG_SWAP
#include <linux/swapops.h> /* for swp_offset */
+#include <linux/zswap.h> /* for zswap_is_present */
#include <linux/blk_types.h> /* for bio_end_io_t */
static inline unsigned int swp_cluster_offset(swp_entry_t entry)
@@ -336,6 +337,24 @@ struct folio *swapin_sync(swp_entry_t entry, gfp_t flag, unsigned long orders,
void swap_update_readahead(struct folio *folio, struct vm_area_struct *vma,
unsigned long addr);
+/**
+ * swap_entry_synchronous - check if @entry is read synchronously
+ * @si: the swap device of @entry
+ * @entry: the swap entry
+ *
+ * SWP_SYNCHRONOUS_IO devices and zswap complete a read in the faulting
+ * context, so swap readahead has no asynchronous I/O to overlap.
+ *
+ * Context: The caller must hold a reference on @si. The answer is a hint: the
+ * entry can be written back from zswap or freed concurrently.
+ */
+static inline bool swap_entry_synchronous(struct swap_info_struct *si,
+ swp_entry_t entry)
+{
+ return data_race(si->flags & SWP_SYNCHRONOUS_IO) ||
+ zswap_is_present(entry, 1);
+}
+
#else /* CONFIG_SWAP */
static inline struct swap_cluster_info *swap_cluster_get_and_lock(
@@ -423,6 +442,12 @@ static inline void swap_update_readahead(struct folio *folio,
{
}
+static inline bool swap_entry_synchronous(struct swap_info_struct *si,
+ swp_entry_t entry)
+{
+ return false;
+}
+
static inline int swap_writeout(struct swap_io_ctx *ctx, struct folio *folio)
{
return 0;
diff --git a/mm/zswap.c b/mm/zswap.c
index eb3494491410..1dcf77d5edae 100644
--- a/mm/zswap.c
+++ b/mm/zswap.c
@@ -1605,12 +1605,15 @@ bool zswap_store(struct folio *folio)
* change under it.
* Return: true if at least one slot in the range is in zswap.
*/
-static bool zswap_is_present(swp_entry_t entry, unsigned int nr)
+bool zswap_is_present(swp_entry_t entry, unsigned int nr)
{
pgoff_t offset = swp_offset(entry);
struct xarray *tree = swap_zswap_tree(entry);
unsigned long index = offset;
+ if (zswap_never_enabled() || xa_empty(tree))
+ return false;
+
/*
* A pinned range is at most SWAPFILE_CLUSTER slots and is aligned to
* its own size, so one tree covers all of it and a single lookup is
--
2.53.0-Meta
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v3] mm: bypass swap readahead for zswap
2026-10-07 15:39 [PATCH v3] mm: bypass swap readahead for zswap Alexandre Ghiti
@ 2026-10-07 21:04 ` Barry Song
2026-10-09 6:22 ` Alexandre Ghiti
2026-10-07 21:37 ` Yosry Ahmed
` (3 subsequent siblings)
4 siblings, 1 reply; 15+ messages in thread
From: Barry Song @ 2026-10-07 21:04 UTC (permalink / raw)
To: Alexandre Ghiti
Cc: akpm, hannes, yosry, nphamcs, shikemeng, baoquan.he,
youngjun.park, chengming.zhou, david, ljs, liam, vbabka, rppt,
surenb, mhocko, kasong, ryncsn, chrisl, usama.arif, linux-mm,
linux-kernel
On Wed, Oct 7, 2026 at 11:39 PM Alexandre Ghiti <alex@ghiti.fr> wrote:
>
> Commit 0bcac06f27d7 ("mm, swap: skip swapcache for swapin of synchronous
> device") made SWP_SYNCHRONOUS_IO devices (e.g. zram) skip swap readahead.
>
> zswap is the same kind of in-memory, synchronous backend as zram, not a
> swap device flagged SWP_SYNCHRONOUS_IO so it still goes through
> swapin_readahead().
>
> Here are the results from bypassing readahead for zswap too: it was
> measured with a kernel build (make -j16) in a memcg, zswap=zstd, shrinker
> off, on Sapphire Rapids and 3 iterations.
>
> 768M memcg (sustained swap thrash):
> metric mm-new + bypass delta
> build time (s) 405.0 341.7 -15.6%
> zswap-in (GB) 79.5 53.0 -33%
> zswap-out (GB) 144.8 115.6 -20%
> swap readahead (pages) 6.79M 0.45M -93%
> swap_ra hit (%) 72.1 89.9 +18pp
>
> 1G memcg (light pressure, build not memory-bound):
> metric mm-new + bypass delta
> build time (s) 177.7 176.0 ~same (no regression)
> zswap-in (GB) 10.2 7.5 -26%
> zswap-out (GB) 27.7 25.1 -9%
> swap readahead (pages) 1.07M 0.08M -93%
> swap_ra hit (%) 68.6 87.2 +19pp
Hi Alexandre,
I assume your configuration is as below?
/ # cat /proc/sys/vm/page-cluster
3
/ # cat /sys/kernel/mm/swap/vma_ra_enabled
true
I am curious whether setting `/proc/sys/vm/page-cluster` to either 0
or 1 would result in the same performance gain on a zswap-enabled
system?
>
> Similar gains were observed on an AMD EPYC 7D13 host.
>
> The gain is from no longer prefetching pages that are pointless for an
> in-memory backend: readahead inflates anon residency and thrashes the
> page cache (file pages get evicted and re-read), lengthens each fault by
> synchronously (de)compressing a cluster of neighbours, and adds
> compression traffic when those extra pages are reclaimed.
>
> Bypassing swap readahead for zswap therefore makes sense.
>
The change makes sense to me. I am just curious whether setting those
configurations would result in the same performance gain.
Maybe you can mention the `page-cluster` configuration in the changelog
as well.
> Suggested-by: Usama Arif <usama.arif@linux.dev>
> Signed-off-by: Alexandre Ghiti <alex@ghiti.fr>
> ---
Reviewed-by: Barry Song <baohua@kernel.org>
This is RC6 now. You probably need to respin it after the new RC1.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v3] mm: bypass swap readahead for zswap
2026-10-07 15:39 [PATCH v3] mm: bypass swap readahead for zswap Alexandre Ghiti
2026-10-07 21:04 ` Barry Song
@ 2026-10-07 21:37 ` Yosry Ahmed
2026-10-09 6:21 ` Alexandre Ghiti
2026-10-12 7:56 ` Barry Song
2026-10-12 15:08 ` Yosry Ahmed
` (2 subsequent siblings)
4 siblings, 2 replies; 15+ messages in thread
From: Yosry Ahmed @ 2026-10-07 21:37 UTC (permalink / raw)
To: Alexandre Ghiti, Kairui Song
Cc: akpm, hannes, nphamcs, shikemeng, baoquan.he, youngjun.park,
chengming.zhou, david, ljs, liam, vbabka, rppt, surenb, mhocko,
ryncsn, chrisl, baohua, usama.arif, linux-mm, linux-kernel
On Wed, Oct 7, 2026 at 8:39 AM Alexandre Ghiti <alex@ghiti.fr> wrote:
>
> Commit 0bcac06f27d7 ("mm, swap: skip swapcache for swapin of synchronous
> device") made SWP_SYNCHRONOUS_IO devices (e.g. zram) skip swap readahead.
>
> zswap is the same kind of in-memory, synchronous backend as zram, not a
> swap device flagged SWP_SYNCHRONOUS_IO so it still goes through
> swapin_readahead().
>
> Here are the results from bypassing readahead for zswap too: it was
> measured with a kernel build (make -j16) in a memcg, zswap=zstd, shrinker
> off, on Sapphire Rapids and 3 iterations.
>
> 768M memcg (sustained swap thrash):
> metric mm-new + bypass delta
> build time (s) 405.0 341.7 -15.6%
> zswap-in (GB) 79.5 53.0 -33%
> zswap-out (GB) 144.8 115.6 -20%
> swap readahead (pages) 6.79M 0.45M -93%
> swap_ra hit (%) 72.1 89.9 +18pp
>
> 1G memcg (light pressure, build not memory-bound):
> metric mm-new + bypass delta
> build time (s) 177.7 176.0 ~same (no regression)
> zswap-in (GB) 10.2 7.5 -26%
> zswap-out (GB) 27.7 25.1 -9%
> swap readahead (pages) 1.07M 0.08M -93%
> swap_ra hit (%) 68.6 87.2 +19pp
>
> Similar gains were observed on an AMD EPYC 7D13 host.
>
> The gain is from no longer prefetching pages that are pointless for an
> in-memory backend: readahead inflates anon residency and thrashes the
> page cache (file pages get evicted and re-read), lengthens each fault by
> synchronously (de)compressing a cluster of neighbours, and adds
> compression traffic when those extra pages are reclaimed.
>
> Bypassing swap readahead for zswap therefore makes sense.
No objection here, I am just thinking about Kairui's LPC presentation
about how compressed swap can (in theory at least) still benefit from
readahead: https://lpc.events/event/20/contributions/2421/attachments/2116/4689/Better%20Anon%20(Swap)%20Readahead%20(2).pdf.
Seems like a big portion of the fault cost is still outside of
decompression and can benefit from readahead, but that isn't the case
today. I assume any future improvements to readahead and re-enabling
for zram would also cover zswap?
>
> Suggested-by: Usama Arif <usama.arif@linux.dev>
> Signed-off-by: Alexandre Ghiti <alex@ghiti.fr>
> ---
> Changes in v3:
> - Go back to the RFC's single patch: readahead windows that mix zswap
> and disk entries will be dealt with later, the priority is to merge the
> zswap readahead bypass
> - Move the check into a swap helper, swap_entry_synchronous(), instead
> of open-coding it in do_swap_page() (David)
> - Bail out early when zswap was never enabled or the tree is empty, so
> disk-only systems pay a static branch (Kairui, Nhat)
> - Reuse zswap_is_present() rather than adding zswap_present_test()
> (Yosry)
> - Rebase on mm-new
>
> Changes in v2:
> - Keep readahead, but overlap the decompression of zswap neighbours with
> the disk I/O of the window, and bypass it when the target needs no
> disk I/O (Yosry, Nhat, Barry)
>
> v2: https://lore.kernel.org/all/20260722163536.773166-1-alex@ghiti.fr/
> RFC: https://lore.kernel.org/all/20260624075700.751467-1-alex@ghiti.fr/
>
> include/linux/zswap.h | 6 ++++++
> mm/memory.c | 4 ++--
> mm/swap.h | 25 +++++++++++++++++++++++++
> mm/zswap.c | 5 ++++-
> 4 files changed, 37 insertions(+), 3 deletions(-)
>
> diff --git a/include/linux/zswap.h b/include/linux/zswap.h
> index df6cafbe95dc..94746fb71bb6 100644
> --- a/include/linux/zswap.h
> +++ b/include/linux/zswap.h
> @@ -35,6 +35,7 @@ void zswap_lruvec_state_init(struct lruvec *lruvec);
> void zswap_folio_swapin(struct folio *folio);
> bool zswap_is_enabled(void);
> bool zswap_never_enabled(void);
> +bool zswap_is_present(swp_entry_t entry, unsigned int nr);
> #else
>
> struct zswap_lruvec_state {};
> @@ -73,6 +74,11 @@ static inline bool zswap_never_enabled(void)
> return true;
> }
>
> +static inline bool zswap_is_present(swp_entry_t entry, unsigned int nr)
> +{
> + return false;
> +}
> +
> #endif
>
> #endif /* _LINUX_ZSWAP_H */
> diff --git a/mm/memory.c b/mm/memory.c
> index 330cde31bf8b..396bc71a265f 100644
> --- a/mm/memory.c
> +++ b/mm/memory.c
> @@ -5030,8 +5030,8 @@ vm_fault_t do_swap_page(struct vm_fault *vmf)
> if (folio)
> swap_update_readahead(folio, vma, vmf->address);
> if (!folio) {
> - /* Swapin bypasses readahead for SWP_SYNCHRONOUS_IO devices */
> - if (data_race(si->flags & SWP_SYNCHRONOUS_IO))
> + /* Swapin bypasses readahead for SWP_SYNCHRONOUS_IO devices and zswap */
> + if (swap_entry_synchronous(si, entry))
> folio = swapin_sync(entry, GFP_HIGHUSER_MOVABLE,
> thp_swapin_suitable_orders(vmf) | BIT(0),
> vmf, NULL, 0);
> diff --git a/mm/swap.h b/mm/swap.h
> index d5bf21f517dc..b8181bd056ee 100644
> --- a/mm/swap.h
> +++ b/mm/swap.h
> @@ -94,6 +94,7 @@ static inline int mem_cgroup_swappiness(const struct mem_cgroup *memcg)
>
> #ifdef CONFIG_SWAP
> #include <linux/swapops.h> /* for swp_offset */
> +#include <linux/zswap.h> /* for zswap_is_present */
> #include <linux/blk_types.h> /* for bio_end_io_t */
>
> static inline unsigned int swp_cluster_offset(swp_entry_t entry)
> @@ -336,6 +337,24 @@ struct folio *swapin_sync(swp_entry_t entry, gfp_t flag, unsigned long orders,
> void swap_update_readahead(struct folio *folio, struct vm_area_struct *vma,
> unsigned long addr);
>
> +/**
> + * swap_entry_synchronous - check if @entry is read synchronously
> + * @si: the swap device of @entry
> + * @entry: the swap entry
> + *
> + * SWP_SYNCHRONOUS_IO devices and zswap complete a read in the faulting
> + * context, so swap readahead has no asynchronous I/O to overlap.
> + *
> + * Context: The caller must hold a reference on @si. The answer is a hint: the
> + * entry can be written back from zswap or freed concurrently.
> + */
> +static inline bool swap_entry_synchronous(struct swap_info_struct *si,
> + swp_entry_t entry)
> +{
> + return data_race(si->flags & SWP_SYNCHRONOUS_IO) ||
> + zswap_is_present(entry, 1);
> +}
> +
> #else /* CONFIG_SWAP */
>
> static inline struct swap_cluster_info *swap_cluster_get_and_lock(
> @@ -423,6 +442,12 @@ static inline void swap_update_readahead(struct folio *folio,
> {
> }
>
> +static inline bool swap_entry_synchronous(struct swap_info_struct *si,
> + swp_entry_t entry)
> +{
> + return false;
> +}
> +
> static inline int swap_writeout(struct swap_io_ctx *ctx, struct folio *folio)
> {
> return 0;
> diff --git a/mm/zswap.c b/mm/zswap.c
> index eb3494491410..1dcf77d5edae 100644
> --- a/mm/zswap.c
> +++ b/mm/zswap.c
> @@ -1605,12 +1605,15 @@ bool zswap_store(struct folio *folio)
> * change under it.
> * Return: true if at least one slot in the range is in zswap.
> */
> -static bool zswap_is_present(swp_entry_t entry, unsigned int nr)
> +bool zswap_is_present(swp_entry_t entry, unsigned int nr)
> {
> pgoff_t offset = swp_offset(entry);
> struct xarray *tree = swap_zswap_tree(entry);
> unsigned long index = offset;
>
> + if (zswap_never_enabled() || xa_empty(tree))
> + return false;
> +
> /*
> * A pinned range is at most SWAPFILE_CLUSTER slots and is aligned to
> * its own size, so one tree covers all of it and a single lookup is
> --
> 2.53.0-Meta
>
>
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v3] mm: bypass swap readahead for zswap
2026-10-07 21:37 ` Yosry Ahmed
@ 2026-10-09 6:21 ` Alexandre Ghiti
2026-10-12 7:56 ` Barry Song
1 sibling, 0 replies; 15+ messages in thread
From: Alexandre Ghiti @ 2026-10-09 6:21 UTC (permalink / raw)
To: Yosry Ahmed
Cc: Alexandre Ghiti, Kairui Song, akpm, hannes, nphamcs, shikemeng,
baoquan.he, youngjun.park, chengming.zhou, david, ljs, liam,
vbabka, rppt, surenb, mhocko, ryncsn, chrisl, baohua, usama.arif,
linux-mm, linux-kernel
Hi Yosry,
On Wed, Oct 7, 2026 at 11:37 PM Yosry Ahmed <yosry@kernel.org> wrote:
>
> >
> On Wed, Oct 7, 2026 at 8:39 AM Alexandre Ghiti <alex@ghiti.fr> wrote:
> >
> > Commit 0bcac06f27d7 ("mm, swap: skip swapcache for swapin of synchronous
> > device") made SWP_SYNCHRONOUS_IO devices (e.g. zram) skip swap readahead.
> >
> > zswap is the same kind of in-memory, synchronous backend as zram, not a
> > swap device flagged SWP_SYNCHRONOUS_IO so it still goes through
> > swapin_readahead().
> >
> > Here are the results from bypassing readahead for zswap too: it was
> > measured with a kernel build (make -j16) in a memcg, zswap=zstd, shrinker
> > off, on Sapphire Rapids and 3 iterations.
> >
> > 768M memcg (sustained swap thrash):
> > metric mm-new + bypass delta
> > build time (s) 405.0 341.7 -15.6%
> > zswap-in (GB) 79.5 53.0 -33%
> > zswap-out (GB) 144.8 115.6 -20%
> > swap readahead (pages) 6.79M 0.45M -93%
> > swap_ra hit (%) 72.1 89.9 +18pp
> >
> > 1G memcg (light pressure, build not memory-bound):
> > metric mm-new + bypass delta
> > build time (s) 177.7 176.0 ~same (no regression)
> > zswap-in (GB) 10.2 7.5 -26%
> > zswap-out (GB) 27.7 25.1 -9%
> > swap readahead (pages) 1.07M 0.08M -93%
> > swap_ra hit (%) 68.6 87.2 +19pp
> >
> > Similar gains were observed on an AMD EPYC 7D13 host.
> >
> > The gain is from no longer prefetching pages that are pointless for an
> > in-memory backend: readahead inflates anon residency and thrashes the
> > page cache (file pages get evicted and re-read), lengthens each fault by
> > synchronously (de)compressing a cluster of neighbours, and adds
> > compression traffic when those extra pages are reclaimed.
> >
> > Bypassing swap readahead for zswap therefore makes sense.
>
> No objection here, I am just thinking about Kairui's LPC presentation
> about how compressed swap can (in theory at least) still benefit from
> readahead: https://urldefense.com/v3/__https://lpc.events/event/20/contributions/2421/attachments/2116/4689/Better*20Anon*20(Swap)*20Readahead*20(2).pdf__;JSUlJQ!!Bt8RZUm9aw!4SSWs97-L1JNciWpQqvSw5xpIAUSZorWG4UrxCy7RYlUbAY1njEfPPH-OBH8Ny1fqOOB7TpHMM2-$ .
>
> Seems like a big portion of the fault cost is still outside of
> decompression and can benefit from readahead, but that isn't the case
> today. I assume any future improvements to readahead and re-enabling
> for zram would also cover zswap?
>
I'll follow Kairui's work and ensure zswap stays aligned with zram.
Thanks,
Alex
> >
> > Suggested-by: Usama Arif <usama.arif@linux.dev>
> > Signed-off-by: Alexandre Ghiti <alex@ghiti.fr>
> > ---
> > Changes in v3:
> > - Go back to the RFC's single patch: readahead windows that mix zswap
> > and disk entries will be dealt with later, the priority is to merge the
> > zswap readahead bypass
> > - Move the check into a swap helper, swap_entry_synchronous(), instead
> > of open-coding it in do_swap_page() (David)
> > - Bail out early when zswap was never enabled or the tree is empty, so
> > disk-only systems pay a static branch (Kairui, Nhat)
> > - Reuse zswap_is_present() rather than adding zswap_present_test()
> > (Yosry)
> > - Rebase on mm-new
> >
> > Changes in v2:
> > - Keep readahead, but overlap the decompression of zswap neighbours with
> > the disk I/O of the window, and bypass it when the target needs no
> > disk I/O (Yosry, Nhat, Barry)
> >
> > v2: https://lore.kernel.org/all/20260722163536.773166-1-alex@ghiti.fr/
> > RFC: https://lore.kernel.org/all/20260624075700.751467-1-alex@ghiti.fr/
> >
> > include/linux/zswap.h | 6 ++++++
> > mm/memory.c | 4 ++--
> > mm/swap.h | 25 +++++++++++++++++++++++++
> > mm/zswap.c | 5 ++++-
> > 4 files changed, 37 insertions(+), 3 deletions(-)
> >
> > diff --git a/include/linux/zswap.h b/include/linux/zswap.h
> > index df6cafbe95dc..94746fb71bb6 100644
> > --- a/include/linux/zswap.h
> > +++ b/include/linux/zswap.h
> > @@ -35,6 +35,7 @@ void zswap_lruvec_state_init(struct lruvec *lruvec);
> > void zswap_folio_swapin(struct folio *folio);
> > bool zswap_is_enabled(void);
> > bool zswap_never_enabled(void);
> > +bool zswap_is_present(swp_entry_t entry, unsigned int nr);
> > #else
> >
> > struct zswap_lruvec_state {};
> > @@ -73,6 +74,11 @@ static inline bool zswap_never_enabled(void)
> > return true;
> > }
> >
> > +static inline bool zswap_is_present(swp_entry_t entry, unsigned int nr)
> > +{
> > + return false;
> > +}
> > +
> > #endif
> >
> > #endif /* _LINUX_ZSWAP_H */
> > diff --git a/mm/memory.c b/mm/memory.c
> > index 330cde31bf8b..396bc71a265f 100644
> > --- a/mm/memory.c
> > +++ b/mm/memory.c
> > @@ -5030,8 +5030,8 @@ vm_fault_t do_swap_page(struct vm_fault *vmf)
> > if (folio)
> > swap_update_readahead(folio, vma, vmf->address);
> > if (!folio) {
> > - /* Swapin bypasses readahead for SWP_SYNCHRONOUS_IO devices */
> > - if (data_race(si->flags & SWP_SYNCHRONOUS_IO))
> > + /* Swapin bypasses readahead for SWP_SYNCHRONOUS_IO devices and zswap */
> > + if (swap_entry_synchronous(si, entry))
> > folio = swapin_sync(entry, GFP_HIGHUSER_MOVABLE,
> > thp_swapin_suitable_orders(vmf) | BIT(0),
> > vmf, NULL, 0);
> > diff --git a/mm/swap.h b/mm/swap.h
> > index d5bf21f517dc..b8181bd056ee 100644
> > --- a/mm/swap.h
> > +++ b/mm/swap.h
> > @@ -94,6 +94,7 @@ static inline int mem_cgroup_swappiness(const struct mem_cgroup *memcg)
> >
> > #ifdef CONFIG_SWAP
> > #include <linux/swapops.h> /* for swp_offset */
> > +#include <linux/zswap.h> /* for zswap_is_present */
> > #include <linux/blk_types.h> /* for bio_end_io_t */
> >
> > static inline unsigned int swp_cluster_offset(swp_entry_t entry)
> > @@ -336,6 +337,24 @@ struct folio *swapin_sync(swp_entry_t entry, gfp_t flag, unsigned long orders,
> > void swap_update_readahead(struct folio *folio, struct vm_area_struct *vma,
> > unsigned long addr);
> >
> > +/**
> > + * swap_entry_synchronous - check if @entry is read synchronously
> > + * @si: the swap device of @entry
> > + * @entry: the swap entry
> > + *
> > + * SWP_SYNCHRONOUS_IO devices and zswap complete a read in the faulting
> > + * context, so swap readahead has no asynchronous I/O to overlap.
> > + *
> > + * Context: The caller must hold a reference on @si. The answer is a hint: the
> > + * entry can be written back from zswap or freed concurrently.
> > + */
> > +static inline bool swap_entry_synchronous(struct swap_info_struct *si,
> > + swp_entry_t entry)
> > +{
> > + return data_race(si->flags & SWP_SYNCHRONOUS_IO) ||
> > + zswap_is_present(entry, 1);
> > +}
> > +
> > #else /* CONFIG_SWAP */
> >
> > static inline struct swap_cluster_info *swap_cluster_get_and_lock(
> > @@ -423,6 +442,12 @@ static inline void swap_update_readahead(struct folio *folio,
> > {
> > }
> >
> > +static inline bool swap_entry_synchronous(struct swap_info_struct *si,
> > + swp_entry_t entry)
> > +{
> > + return false;
> > +}
> > +
> > static inline int swap_writeout(struct swap_io_ctx *ctx, struct folio *folio)
> > {
> > return 0;
> > diff --git a/mm/zswap.c b/mm/zswap.c
> > index eb3494491410..1dcf77d5edae 100644
> > --- a/mm/zswap.c
> > +++ b/mm/zswap.c
> > @@ -1605,12 +1605,15 @@ bool zswap_store(struct folio *folio)
> > * change under it.
> > * Return: true if at least one slot in the range is in zswap.
> > */
> > -static bool zswap_is_present(swp_entry_t entry, unsigned int nr)
> > +bool zswap_is_present(swp_entry_t entry, unsigned int nr)
> > {
> > pgoff_t offset = swp_offset(entry);
> > struct xarray *tree = swap_zswap_tree(entry);
> > unsigned long index = offset;
> >
> > + if (zswap_never_enabled() || xa_empty(tree))
> > + return false;
> > +
> > /*
> > * A pinned range is at most SWAPFILE_CLUSTER slots and is aligned to
> > * its own size, so one tree covers all of it and a single lookup is
> > --
> > 2.53.0-Meta
> >
> >
>
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v3] mm: bypass swap readahead for zswap
2026-10-07 21:04 ` Barry Song
@ 2026-10-09 6:22 ` Alexandre Ghiti
2026-10-12 6:59 ` Alexandre Ghiti
0 siblings, 1 reply; 15+ messages in thread
From: Alexandre Ghiti @ 2026-10-09 6:22 UTC (permalink / raw)
To: Barry Song
Cc: Alexandre Ghiti, akpm, hannes, yosry, nphamcs, shikemeng,
baoquan.he, youngjun.park, chengming.zhou, david, ljs, liam,
vbabka, rppt, surenb, mhocko, kasong, ryncsn, chrisl, usama.arif,
linux-mm, linux-kernel
Hi Barry,
On Wed, Oct 7, 2026 at 11:05 PM Barry Song <baohua@kernel.org> wrote:
>
> >
> On Wed, Oct 7, 2026 at 11:39 PM Alexandre Ghiti <alex@ghiti.fr> wrote:
> >
> > Commit 0bcac06f27d7 ("mm, swap: skip swapcache for swapin of synchronous
> > device") made SWP_SYNCHRONOUS_IO devices (e.g. zram) skip swap readahead.
> >
> > zswap is the same kind of in-memory, synchronous backend as zram, not a
> > swap device flagged SWP_SYNCHRONOUS_IO so it still goes through
> > swapin_readahead().
> >
> > Here are the results from bypassing readahead for zswap too: it was
> > measured with a kernel build (make -j16) in a memcg, zswap=zstd, shrinker
> > off, on Sapphire Rapids and 3 iterations.
> >
> > 768M memcg (sustained swap thrash):
> > metric mm-new + bypass delta
> > build time (s) 405.0 341.7 -15.6%
> > zswap-in (GB) 79.5 53.0 -33%
> > zswap-out (GB) 144.8 115.6 -20%
> > swap readahead (pages) 6.79M 0.45M -93%
> > swap_ra hit (%) 72.1 89.9 +18pp
> >
> > 1G memcg (light pressure, build not memory-bound):
> > metric mm-new + bypass delta
> > build time (s) 177.7 176.0 ~same (no regression)
> > zswap-in (GB) 10.2 7.5 -26%
> > zswap-out (GB) 27.7 25.1 -9%
> > swap readahead (pages) 1.07M 0.08M -93%
> > swap_ra hit (%) 68.6 87.2 +19pp
>
> Hi Alexandre,
>
> I assume your configuration is as below?
>
> / # cat /proc/sys/vm/page-cluster
> 3
> / # cat /sys/kernel/mm/swap/vma_ra_enabled
> true
Yes
>
> I am curious whether setting `/proc/sys/vm/page-cluster` to either 0
> or 1 would result in the same performance gain on a zswap-enabled
> system?
I'll test and report back here.
>
> >
> > Similar gains were observed on an AMD EPYC 7D13 host.
> >
> > The gain is from no longer prefetching pages that are pointless for an
> > in-memory backend: readahead inflates anon residency and thrashes the
> > page cache (file pages get evicted and re-read), lengthens each fault by
> > synchronously (de)compressing a cluster of neighbours, and adds
> > compression traffic when those extra pages are reclaimed.
> >
> > Bypassing swap readahead for zswap therefore makes sense.
> >
>
> The change makes sense to me. I am just curious whether setting those
> configurations would result in the same performance gain.
>
> Maybe you can mention the `page-cluster` configuration in the changelog
> as well.
Sure, I'll do that.
>
> > Suggested-by: Usama Arif <usama.arif@linux.dev>
> > Signed-off-by: Alexandre Ghiti <alex@ghiti.fr>
> > ---
>
> Reviewed-by: Barry Song <baohua@kernel.org>
>
> This is RC6 now. You probably need to respin it after the new RC1.
>
Thanks Barry!
Alex
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v3] mm: bypass swap readahead for zswap
2026-10-09 6:22 ` Alexandre Ghiti
@ 2026-10-12 6:59 ` Alexandre Ghiti
2026-10-12 8:01 ` Barry Song
0 siblings, 1 reply; 15+ messages in thread
From: Alexandre Ghiti @ 2026-10-12 6:59 UTC (permalink / raw)
To: Barry Song
Cc: Alexandre Ghiti, akpm, hannes, yosry, nphamcs, shikemeng,
baoquan.he, youngjun.park, chengming.zhou, david, ljs, liam,
vbabka, rppt, surenb, mhocko, kasong, ryncsn, chrisl, usama.arif,
linux-mm, linux-kernel
Hi Barry,
On Fri, Oct 9, 2026 at 8:22 AM Alexandre Ghiti <alexghiti@meta.com> wrote:
>
> Hi Barry,
>
>
> On Wed, Oct 7, 2026 at 11:05 PM Barry Song <baohua@kernel.org> wrote:
> >
> > > >
> > On Wed, Oct 7, 2026 at 11:39 PM Alexandre Ghiti <alex@ghiti.fr> wrote:
> > >
> > > Commit 0bcac06f27d7 ("mm, swap: skip swapcache for swapin of synchronous
> > > device") made SWP_SYNCHRONOUS_IO devices (e.g. zram) skip swap readahead.
> > >
> > > zswap is the same kind of in-memory, synchronous backend as zram, not a
> > > swap device flagged SWP_SYNCHRONOUS_IO so it still goes through
> > > swapin_readahead().
> > >
> > > Here are the results from bypassing readahead for zswap too: it was
> > > measured with a kernel build (make -j16) in a memcg, zswap=zstd, shrinker
> > > off, on Sapphire Rapids and 3 iterations.
> > >
> > > 768M memcg (sustained swap thrash):
> > > metric mm-new + bypass delta
> > > build time (s) 405.0 341.7 -15.6%
> > > zswap-in (GB) 79.5 53.0 -33%
> > > zswap-out (GB) 144.8 115.6 -20%
> > > swap readahead (pages) 6.79M 0.45M -93%
> > > swap_ra hit (%) 72.1 89.9 +18pp
> > >
> > > 1G memcg (light pressure, build not memory-bound):
> > > metric mm-new + bypass delta
> > > build time (s) 177.7 176.0 ~same (no regression)
> > > zswap-in (GB) 10.2 7.5 -26%
> > > zswap-out (GB) 27.7 25.1 -9%
> > > swap readahead (pages) 1.07M 0.08M -93%
> > > swap_ra hit (%) 68.6 87.2 +19pp
> >
> > Hi Alexandre,
> >
> > I assume your configuration is as below?
> >
> > / # cat /proc/sys/vm/page-cluster
> > 3
> > / # cat /sys/kernel/mm/swap/vma_ra_enabled
> > true
>
> Yes
>
> >
> > I am curious whether setting `/proc/sys/vm/page-cluster` to either 0
> > or 1 would result in the same performance gain on a zswap-enabled
> > system?
>
> I'll test and report back here.
So I ran the same workload (kbuild -j16, 768M memcg, zswap only, n=3 per arm):
768M memcg (sustained swap thrash):
metric mm-new
page-cluster=1 page-cluster=0 + bypass
build time (s) 530.0 462.0
-12.8% 452.3 -14.7% 452.7 -14.6%
zswap-in (GB) 95.7 66.7
-30% 64.2 -33% 64.4 -33%
zswap-out (GB) 169.3 138.3
-18% 135.2 -20% 135.6 -20%
swap readahead (pages) 8.37M 1.93M -77%
0 -100% 0.53M -94%
swap_ra hit (%) 73.6 86.5
+13pp - - 90.7 +17pp
Exact same results with page-cluster to 0, so I'll add that to the
changelog as you suggested.
Thanks again for the review,
Alex
>
> >
> > >
> > > Similar gains were observed on an AMD EPYC 7D13 host.
> > >
> > > The gain is from no longer prefetching pages that are pointless for an
> > > in-memory backend: readahead inflates anon residency and thrashes the
> > > page cache (file pages get evicted and re-read), lengthens each fault by
> > > synchronously (de)compressing a cluster of neighbours, and adds
> > > compression traffic when those extra pages are reclaimed.
> > >
> > > Bypassing swap readahead for zswap therefore makes sense.
> > >
> >
> > The change makes sense to me. I am just curious whether setting those
> > configurations would result in the same performance gain.
> >
> > Maybe you can mention the `page-cluster` configuration in the changelog
> > as well.
>
> Sure, I'll do that.
>
> >
> > > Suggested-by: Usama Arif <usama.arif@linux.dev>
> > > Signed-off-by: Alexandre Ghiti <alex@ghiti.fr>
> > > ---
> >
> > Reviewed-by: Barry Song <baohua@kernel.org>
> >
> > This is RC6 now. You probably need to respin it after the new RC1.
> >
>
> Thanks Barry!
>
> Alex
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v3] mm: bypass swap readahead for zswap
2026-10-07 21:37 ` Yosry Ahmed
2026-10-09 6:21 ` Alexandre Ghiti
@ 2026-10-12 7:56 ` Barry Song
2026-10-13 18:55 ` Kairui Song
1 sibling, 1 reply; 15+ messages in thread
From: Barry Song @ 2026-10-12 7:56 UTC (permalink / raw)
To: Yosry Ahmed
Cc: Alexandre Ghiti, Kairui Song, akpm, hannes, nphamcs, shikemeng,
baoquan.he, youngjun.park, chengming.zhou, david, ljs, liam,
vbabka, rppt, surenb, mhocko, ryncsn, chrisl, usama.arif,
linux-mm, linux-kernel
On Thu, Oct 8, 2026 at 5:37 AM Yosry Ahmed <yosry@kernel.org> wrote:
>
> On Wed, Oct 7, 2026 at 8:39 AM Alexandre Ghiti <alex@ghiti.fr> wrote:
> >
> > Commit 0bcac06f27d7 ("mm, swap: skip swapcache for swapin of synchronous
> > device") made SWP_SYNCHRONOUS_IO devices (e.g. zram) skip swap readahead.
> >
> > zswap is the same kind of in-memory, synchronous backend as zram, not a
> > swap device flagged SWP_SYNCHRONOUS_IO so it still goes through
> > swapin_readahead().
> >
> > Here are the results from bypassing readahead for zswap too: it was
> > measured with a kernel build (make -j16) in a memcg, zswap=zstd, shrinker
> > off, on Sapphire Rapids and 3 iterations.
> >
> > 768M memcg (sustained swap thrash):
> > metric mm-new + bypass delta
> > build time (s) 405.0 341.7 -15.6%
> > zswap-in (GB) 79.5 53.0 -33%
> > zswap-out (GB) 144.8 115.6 -20%
> > swap readahead (pages) 6.79M 0.45M -93%
> > swap_ra hit (%) 72.1 89.9 +18pp
> >
> > 1G memcg (light pressure, build not memory-bound):
> > metric mm-new + bypass delta
> > build time (s) 177.7 176.0 ~same (no regression)
> > zswap-in (GB) 10.2 7.5 -26%
> > zswap-out (GB) 27.7 25.1 -9%
> > swap readahead (pages) 1.07M 0.08M -93%
> > swap_ra hit (%) 68.6 87.2 +19pp
> >
> > Similar gains were observed on an AMD EPYC 7D13 host.
> >
> > The gain is from no longer prefetching pages that are pointless for an
> > in-memory backend: readahead inflates anon residency and thrashes the
> > page cache (file pages get evicted and re-read), lengthens each fault by
> > synchronously (de)compressing a cluster of neighbours, and adds
> > compression traffic when those extra pages are reclaimed.
> >
> > Bypassing swap readahead for zswap therefore makes sense.
>
> No objection here, I am just thinking about Kairui's LPC presentation
> about how compressed swap can (in theory at least) still benefit from
> readahead: https://lpc.events/event/20/contributions/2421/attachments/2116/4689/Better%20Anon%20(Swap)%20Readahead%20(2).pdf.
>
> Seems like a big portion of the fault cost is still outside of
> decompression and can benefit from readahead, but that isn't the case
> today. I assume any future improvements to readahead and re-enabling
> for zram would also cover zswap?
>
In that case, if we can develop an accurate readahead algorithm with a
high hit rate for readahead folios, we will probably also need to map
them at the same time, as proposed in [1]. For example, if we readahead
4 folios, we should map them all together in `do_swap_page()`.
Otherwise, the cost of future minor faults can still be comparable to
the decompression cost. Readahead alone would therefore increase the
total work, which can be quite significant for synchronous I/O.
[1] https://lore.kernel.org/linux-mm/CAGsJ_4xM=WnFO-OuwVude-DQ_UkaCi_zF_bXBP9Li5+ZPNy0VQ@mail.gmail.com/
Best Regards
Barry
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v3] mm: bypass swap readahead for zswap
2026-10-12 6:59 ` Alexandre Ghiti
@ 2026-10-12 8:01 ` Barry Song
2026-10-12 14:21 ` Nhat Pham
0 siblings, 1 reply; 15+ messages in thread
From: Barry Song @ 2026-10-12 8:01 UTC (permalink / raw)
To: Alexandre Ghiti
Cc: Alexandre Ghiti, akpm, hannes, yosry, nphamcs, shikemeng,
baoquan.he, youngjun.park, chengming.zhou, david, ljs, liam,
vbabka, rppt, surenb, mhocko, kasong, ryncsn, chrisl, usama.arif,
linux-mm, linux-kernel
On Mon, Oct 12, 2026 at 2:59 PM Alexandre Ghiti <alexghiti@meta.com> wrote:
>
> Hi Barry,
>
> On Fri, Oct 9, 2026 at 8:22 AM Alexandre Ghiti <alexghiti@meta.com> wrote:
> >
> > Hi Barry,
> >
> >
> > On Wed, Oct 7, 2026 at 11:05 PM Barry Song <baohua@kernel.org> wrote:
> > >
> > > > >
> > > On Wed, Oct 7, 2026 at 11:39 PM Alexandre Ghiti <alex@ghiti.fr> wrote:
> > > >
> > > > Commit 0bcac06f27d7 ("mm, swap: skip swapcache for swapin of synchronous
> > > > device") made SWP_SYNCHRONOUS_IO devices (e.g. zram) skip swap readahead.
> > > >
> > > > zswap is the same kind of in-memory, synchronous backend as zram, not a
> > > > swap device flagged SWP_SYNCHRONOUS_IO so it still goes through
> > > > swapin_readahead().
> > > >
> > > > Here are the results from bypassing readahead for zswap too: it was
> > > > measured with a kernel build (make -j16) in a memcg, zswap=zstd, shrinker
> > > > off, on Sapphire Rapids and 3 iterations.
> > > >
> > > > 768M memcg (sustained swap thrash):
> > > > metric mm-new + bypass delta
> > > > build time (s) 405.0 341.7 -15.6%
> > > > zswap-in (GB) 79.5 53.0 -33%
> > > > zswap-out (GB) 144.8 115.6 -20%
> > > > swap readahead (pages) 6.79M 0.45M -93%
> > > > swap_ra hit (%) 72.1 89.9 +18pp
> > > >
> > > > 1G memcg (light pressure, build not memory-bound):
> > > > metric mm-new + bypass delta
> > > > build time (s) 177.7 176.0 ~same (no regression)
> > > > zswap-in (GB) 10.2 7.5 -26%
> > > > zswap-out (GB) 27.7 25.1 -9%
> > > > swap readahead (pages) 1.07M 0.08M -93%
> > > > swap_ra hit (%) 68.6 87.2 +19pp
> > >
> > > Hi Alexandre,
> > >
> > > I assume your configuration is as below?
> > >
> > > / # cat /proc/sys/vm/page-cluster
> > > 3
> > > / # cat /sys/kernel/mm/swap/vma_ra_enabled
> > > true
> >
> > Yes
> >
> > >
> > > I am curious whether setting `/proc/sys/vm/page-cluster` to either 0
> > > or 1 would result in the same performance gain on a zswap-enabled
> > > system?
> >
> > I'll test and report back here.
>
> So I ran the same workload (kbuild -j16, 768M memcg, zswap only, n=3 per arm):
>
> 768M memcg (sustained swap thrash):
> metric mm-new
> page-cluster=1 page-cluster=0 + bypass
> build time (s) 530.0 462.0
> -12.8% 452.3 -14.7% 452.7 -14.6%
> zswap-in (GB) 95.7 66.7
> -30% 64.2 -33% 64.4 -33%
> zswap-out (GB) 169.3 138.3
> -18% 135.2 -20% 135.6 -20%
> swap readahead (pages) 8.37M 1.93M -77%
> 0 -100% 0.53M -94%
> swap_ra hit (%) 73.6 86.5
> +13pp - - 90.7 +17pp
>
> Exact same results with page-cluster to 0, so I'll add that to the
> changelog as you suggested.
Thanks! This seems to be a strong indication that setting
`page-cluster = 0` can also resolve your problem, even without your
patch. :-)
But I'm still glad to see your auto-tuning patch!
Best Regards
Barry
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v3] mm: bypass swap readahead for zswap
2026-10-12 8:01 ` Barry Song
@ 2026-10-12 14:21 ` Nhat Pham
2026-10-12 14:56 ` Barry Song
2026-10-12 15:08 ` Barry Song
0 siblings, 2 replies; 15+ messages in thread
From: Nhat Pham @ 2026-10-12 14:21 UTC (permalink / raw)
To: Barry Song
Cc: Alexandre Ghiti, Alexandre Ghiti, akpm, hannes, yosry, shikemeng,
baoquan.he, youngjun.park, chengming.zhou, david, ljs, liam,
vbabka, rppt, surenb, mhocko, kasong, ryncsn, chrisl, usama.arif,
linux-mm, linux-kernel
On Mon, Oct 12, 2026 at 1:01 AM Barry Song <baohua@kernel.org> wrote:
>
>
> Thanks! This seems to be a strong indication that setting
> `page-cluster = 0` can also resolve your problem, even without your
> patch. :-)
If every tenants use zswap, yes. But we have tenants with different
kinds of swap backend in the same host (some using compressed
synchronous swap backend, some async IO swap backend).
>
> But I'm still glad to see your auto-tuning patch!
I think we need some smarter readahead work, for sure. I'm just not
quite sure what it'd look like in the end, though.
Doing synchronous readahead work in page fault feels wrong. I chatted
very briefly with Kairui about this. Kairui is trying to come up with
smarter readahead heuristics, but I don't know - even if it's 100%
hitrate, we're concentrating work of multiple pagefaults into a single
pagefault. That sounds off to me?
Crazy idea time - what if instead of skipping readahead for
zswap/zram, we offload it to asynchronous worker (only synchronously
loading the target page)? That'll cancel out the latency issue at
least...?
>
> Best Regards
> Barry
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v3] mm: bypass swap readahead for zswap
2026-10-12 14:21 ` Nhat Pham
@ 2026-10-12 14:56 ` Barry Song
2026-10-12 15:08 ` Barry Song
1 sibling, 0 replies; 15+ messages in thread
From: Barry Song @ 2026-10-12 14:56 UTC (permalink / raw)
To: Nhat Pham
Cc: Alexandre Ghiti, Alexandre Ghiti, akpm, hannes, yosry, shikemeng,
baoquan.he, youngjun.park, chengming.zhou, david, ljs, liam,
vbabka, rppt, surenb, mhocko, kasong, ryncsn, chrisl, usama.arif,
linux-mm, linux-kernel
On Mon, Oct 12, 2026 at 10:21 PM Nhat Pham <nphamcs@gmail.com> wrote:
>
> On Mon, Oct 12, 2026 at 1:01 AM Barry Song <baohua@kernel.org> wrote:
> >
> >
> > Thanks! This seems to be a strong indication that setting
> > `page-cluster = 0` can also resolve your problem, even without your
> > patch. :-)
>
> If every tenants use zswap, yes. But we have tenants with different
> kinds of swap backend in the same host (some using compressed
> synchronous swap backend, some async IO swap backend).
>
> >
> > But I'm still glad to see your auto-tuning patch!
>
> I think we need some smarter readahead work, for sure. I'm just not
> quite sure what it'd look like in the end, though.
>
> Doing synchronous readahead work in page fault feels wrong. I chatted
> very briefly with Kairui about this. Kairui is trying to come up with
> smarter readahead heuristics, but I don't know - even if it's 100%
> hitrate, we're concentrating work of multiple pagefaults into a single
> pagefault. That sounds off to me?
>
> Crazy idea time - what if instead of skipping readahead for
> zswap/zram, we offload it to asynchronous worker (only synchronously
> loading the target page)? That'll cancel out the latency issue at
> least...?
I am quite sure I tried this 3 or 4 months ago, and the
performance was very poor. The problem is that async wake-up
latency is highly unpredictable. For example, do_swap_page()
can complete all the mapping and decompression work in less
than 10 us. However, if we queue the work to an async thread,
its wake-up latency alone could be much longer than 10 us.
Based on my results, async zram readahead only works if we have
hardware queues rather than relying on async threads.
Best Regards
Barry
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v3] mm: bypass swap readahead for zswap
2026-10-12 14:21 ` Nhat Pham
2026-10-12 14:56 ` Barry Song
@ 2026-10-12 15:08 ` Barry Song
1 sibling, 0 replies; 15+ messages in thread
From: Barry Song @ 2026-10-12 15:08 UTC (permalink / raw)
To: Nhat Pham
Cc: Alexandre Ghiti, Alexandre Ghiti, akpm, hannes, yosry, shikemeng,
baoquan.he, youngjun.park, chengming.zhou, david, ljs, liam,
vbabka, rppt, surenb, mhocko, kasong, ryncsn, chrisl, usama.arif,
linux-mm, linux-kernel
On Mon, Oct 12, 2026 at 10:21 PM Nhat Pham <nphamcs@gmail.com> wrote:
>
> On Mon, Oct 12, 2026 at 1:01 AM Barry Song <baohua@kernel.org> wrote:
> >
> >
> > Thanks! This seems to be a strong indication that setting
> > `page-cluster = 0` can also resolve your problem, even without your
> > patch. :-)
>
> If every tenants use zswap, yes. But we have tenants with different
> kinds of swap backend in the same host (some using compressed
> synchronous swap backend, some async IO swap backend).
>
Yep, I have no objection to this patch. As I replied to
Alexandre, I was just curious about what would happen if we
set `page-cluster` to 0.
I think Alexandre's autotuning patch looks good overall. We
just have a lot of corner cases to consider. For example, we
might miss zswap and trigger readahead, even though some
folios in the readahead window are actually in zswap. The
opposite could also happen.
However, I don't think we need to handle all this complexity
at this point.
Best Regards
Barry
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v3] mm: bypass swap readahead for zswap
2026-10-07 15:39 [PATCH v3] mm: bypass swap readahead for zswap Alexandre Ghiti
2026-10-07 21:04 ` Barry Song
2026-10-07 21:37 ` Yosry Ahmed
@ 2026-10-12 15:08 ` Yosry Ahmed
2026-10-16 16:19 ` Nhat Pham
2026-10-23 21:16 ` Barry Song (Xiaomi)
4 siblings, 0 replies; 15+ messages in thread
From: Yosry Ahmed @ 2026-10-12 15:08 UTC (permalink / raw)
To: Alexandre Ghiti
Cc: akpm, hannes, nphamcs, shikemeng, baoquan.he, youngjun.park,
chengming.zhou, david, ljs, liam, vbabka, rppt, surenb, mhocko,
kasong, ryncsn, chrisl, baohua, usama.arif, linux-mm,
linux-kernel
On Wed, Oct 7, 2026 at 8:39 AM Alexandre Ghiti <alex@ghiti.fr> wrote:
>
> Commit 0bcac06f27d7 ("mm, swap: skip swapcache for swapin of synchronous
> device") made SWP_SYNCHRONOUS_IO devices (e.g. zram) skip swap readahead.
>
> zswap is the same kind of in-memory, synchronous backend as zram, not a
> swap device flagged SWP_SYNCHRONOUS_IO so it still goes through
> swapin_readahead().
>
> Here are the results from bypassing readahead for zswap too: it was
> measured with a kernel build (make -j16) in a memcg, zswap=zstd, shrinker
> off, on Sapphire Rapids and 3 iterations.
>
> 768M memcg (sustained swap thrash):
> metric mm-new + bypass delta
> build time (s) 405.0 341.7 -15.6%
> zswap-in (GB) 79.5 53.0 -33%
> zswap-out (GB) 144.8 115.6 -20%
> swap readahead (pages) 6.79M 0.45M -93%
> swap_ra hit (%) 72.1 89.9 +18pp
>
> 1G memcg (light pressure, build not memory-bound):
> metric mm-new + bypass delta
> build time (s) 177.7 176.0 ~same (no regression)
> zswap-in (GB) 10.2 7.5 -26%
> zswap-out (GB) 27.7 25.1 -9%
> swap readahead (pages) 1.07M 0.08M -93%
> swap_ra hit (%) 68.6 87.2 +19pp
>
> Similar gains were observed on an AMD EPYC 7D13 host.
>
> The gain is from no longer prefetching pages that are pointless for an
> in-memory backend: readahead inflates anon residency and thrashes the
> page cache (file pages get evicted and re-read), lengthens each fault by
> synchronously (de)compressing a cluster of neighbours, and adds
> compression traffic when those extra pages are reclaimed.
>
> Bypassing swap readahead for zswap therefore makes sense.
>
> Suggested-by: Usama Arif <usama.arif@linux.dev>
> Signed-off-by: Alexandre Ghiti <alex@ghiti.fr>
Acked-by: Yosry Ahmed <yosry@kernel.org>
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v3] mm: bypass swap readahead for zswap
2026-10-12 7:56 ` Barry Song
@ 2026-10-13 18:55 ` Kairui Song
0 siblings, 0 replies; 15+ messages in thread
From: Kairui Song @ 2026-10-13 18:55 UTC (permalink / raw)
To: Barry Song
Cc: Yosry Ahmed, Alexandre Ghiti, akpm, hannes, nphamcs, shikemeng,
baoquan.he, youngjun.park, chengming.zhou, david, ljs, liam,
vbabka, rppt, surenb, mhocko, chrisl, usama.arif, linux-mm,
linux-kernel
On Mon, Oct 12, 2026 at 3:57 PM Barry Song <baohua@kernel.org> wrote:
>
> On Thu, Oct 8, 2026 at 5:37 AM Yosry Ahmed <yosry@kernel.org> wrote:
> >
> > On Wed, Oct 7, 2026 at 8:39 AM Alexandre Ghiti <alex@ghiti.fr> wrote:
> > >
> > > Commit 0bcac06f27d7 ("mm, swap: skip swapcache for swapin of synchronous
> > > device") made SWP_SYNCHRONOUS_IO devices (e.g. zram) skip swap readahead.
> > >
> > > zswap is the same kind of in-memory, synchronous backend as zram, not a
> > > swap device flagged SWP_SYNCHRONOUS_IO so it still goes through
> > > swapin_readahead().
> > >
> > > Here are the results from bypassing readahead for zswap too: it was
> > > measured with a kernel build (make -j16) in a memcg, zswap=zstd, shrinker
> > > off, on Sapphire Rapids and 3 iterations.
> > >
> > > 768M memcg (sustained swap thrash):
> > > metric mm-new + bypass delta
> > > build time (s) 405.0 341.7 -15.6%
> > > zswap-in (GB) 79.5 53.0 -33%
> > > zswap-out (GB) 144.8 115.6 -20%
> > > swap readahead (pages) 6.79M 0.45M -93%
> > > swap_ra hit (%) 72.1 89.9 +18pp
> > >
> > > 1G memcg (light pressure, build not memory-bound):
> > > metric mm-new + bypass delta
> > > build time (s) 177.7 176.0 ~same (no regression)
> > > zswap-in (GB) 10.2 7.5 -26%
> > > zswap-out (GB) 27.7 25.1 -9%
> > > swap readahead (pages) 1.07M 0.08M -93%
> > > swap_ra hit (%) 68.6 87.2 +19pp
> > >
> > > Similar gains were observed on an AMD EPYC 7D13 host.
> > >
> > > The gain is from no longer prefetching pages that are pointless for an
> > > in-memory backend: readahead inflates anon residency and thrashes the
> > > page cache (file pages get evicted and re-read), lengthens each fault by
> > > synchronously (de)compressing a cluster of neighbours, and adds
> > > compression traffic when those extra pages are reclaimed.
> > >
> > > Bypassing swap readahead for zswap therefore makes sense.
> >
> > No objection here, I am just thinking about Kairui's LPC presentation
> > about how compressed swap can (in theory at least) still benefit from
> > readahead: https://lpc.events/event/20/contributions/2421/attachments/2116/4689/Better%20Anon%20(Swap)%20Readahead%20(2).pdf.
> >
> > Seems like a big portion of the fault cost is still outside of
> > decompression and can benefit from readahead, but that isn't the case
> > today. I assume any future improvements to readahead and re-enabling
> > for zram would also cover zswap?
> >
>
> In that case, if we can develop an accurate readahead algorithm with a
> high hit rate for readahead folios, we will probably also need to map
> them at the same time, as proposed in [1]. For example, if we readahead
> 4 folios, we should map them all together in `do_swap_page()`.
> Otherwise, the cost of future minor faults can still be comparable to
> the decompression cost. Readahead alone would therefore increase the
> total work, which can be quite significant for synchronous I/O.
>
> [1] https://lore.kernel.org/linux-mm/CAGsJ_4xM=WnFO-OuwVude-DQ_UkaCi_zF_bXBP9Li5+ZPNy0VQ@mail.gmail.com/
That's a really nice POC! We should do that IMO, maybe with better
support for (m)THP readahead too.
We first need a better readahead policy though, or else unmapping
these folios might cost even more.
For the readahead part I'm thinking about evaluating using shadow as a
stronger hint for readahead after MGLRU-FG which helps distribute the
folios based on frequencey more evenly among gens, also with a more
proactive aging (async aging, or maybe lru_gen_imbalanced as Barry
once suggested somewhere) so folios in the same gen with same freq
will be much more likely to be reused. Or something similiar to file
readahead's PG_readahead based readahead. Well more
experiments are needed first though.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v3] mm: bypass swap readahead for zswap
2026-10-07 15:39 [PATCH v3] mm: bypass swap readahead for zswap Alexandre Ghiti
` (2 preceding siblings ...)
2026-10-12 15:08 ` Yosry Ahmed
@ 2026-10-16 16:19 ` Nhat Pham
2026-10-23 21:16 ` Barry Song (Xiaomi)
4 siblings, 0 replies; 15+ messages in thread
From: Nhat Pham @ 2026-10-16 16:19 UTC (permalink / raw)
To: Alexandre Ghiti
Cc: akpm, hannes, yosry, shikemeng, baoquan.he, youngjun.park,
chengming.zhou, david, ljs, liam, vbabka, rppt, surenb, mhocko,
kasong, ryncsn, chrisl, baohua, usama.arif, linux-mm,
linux-kernel
On Wed, Oct 7, 2026 at 8:39 AM Alexandre Ghiti <alex@ghiti.fr> wrote:
>
> Commit 0bcac06f27d7 ("mm, swap: skip swapcache for swapin of synchronous
> device") made SWP_SYNCHRONOUS_IO devices (e.g. zram) skip swap readahead.
>
> zswap is the same kind of in-memory, synchronous backend as zram, not a
> swap device flagged SWP_SYNCHRONOUS_IO so it still goes through
> swapin_readahead().
>
> Here are the results from bypassing readahead for zswap too: it was
> measured with a kernel build (make -j16) in a memcg, zswap=zstd, shrinker
> off, on Sapphire Rapids and 3 iterations.
Would there be regression with shrinker on, when we have mixed zswap
and disk swap backends?
>
> 768M memcg (sustained swap thrash):
> metric mm-new + bypass delta
> build time (s) 405.0 341.7 -15.6%
> zswap-in (GB) 79.5 53.0 -33%
> zswap-out (GB) 144.8 115.6 -20%
> swap readahead (pages) 6.79M 0.45M -93%
> swap_ra hit (%) 72.1 89.9 +18pp
>
> 1G memcg (light pressure, build not memory-bound):
> metric mm-new + bypass delta
> build time (s) 177.7 176.0 ~same (no regression)
> zswap-in (GB) 10.2 7.5 -26%
> zswap-out (GB) 27.7 25.1 -9%
> swap readahead (pages) 1.07M 0.08M -93%
> swap_ra hit (%) 68.6 87.2 +19pp
>
> Similar gains were observed on an AMD EPYC 7D13 host.
>
> The gain is from no longer prefetching pages that are pointless for an
> in-memory backend: readahead inflates anon residency and thrashes the
> page cache (file pages get evicted and re-read), lengthens each fault by
> synchronously (de)compressing a cluster of neighbours, and adds
> compression traffic when those extra pages are reclaimed.
>
> Bypassing swap readahead for zswap therefore makes sense.
>
> Suggested-by: Usama Arif <usama.arif@linux.dev>
> Signed-off-by: Alexandre Ghiti <alex@ghiti.fr>
Code looks solid to me otherwise! Nice job.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v3] mm: bypass swap readahead for zswap
2026-10-07 15:39 [PATCH v3] mm: bypass swap readahead for zswap Alexandre Ghiti
` (3 preceding siblings ...)
2026-10-16 16:19 ` Nhat Pham
@ 2026-10-23 21:16 ` Barry Song (Xiaomi)
4 siblings, 0 replies; 15+ messages in thread
From: Barry Song (Xiaomi) @ 2026-10-23 21:16 UTC (permalink / raw)
To: alex
Cc: akpm, baohua, baoquan.he, chengming.zhou, chrisl, david, hannes,
kasong, liam, linux-kernel, linux-mm, ljs, mhocko, nphamcs, rppt,
ryncsn, shikemeng, surenb, usama.arif, vbabka, yosry,
youngjun.park
On Wed, Oct 7, 2026 at 11:39 PM Alexandre Ghiti <alex@ghiti.fr> wrote:
>
> Commit 0bcac06f27d7 ("mm, swap: skip swapcache for swapin of synchronous
> device") made SWP_SYNCHRONOUS_IO devices (e.g. zram) skip swap readahead.
>
> zswap is the same kind of in-memory, synchronous backend as zram, not a
> swap device flagged SWP_SYNCHRONOUS_IO so it still goes through
> swapin_readahead().
>
> Here are the results from bypassing readahead for zswap too: it was
> measured with a kernel build (make -j16) in a memcg, zswap=zstd, shrinker
> off, on Sapphire Rapids and 3 iterations.
>
> 768M memcg (sustained swap thrash):
> metric mm-new + bypass delta
> build time (s) 405.0 341.7 -15.6%
> zswap-in (GB) 79.5 53.0 -33%
> zswap-out (GB) 144.8 115.6 -20%
> swap readahead (pages) 6.79M 0.45M -93%
> swap_ra hit (%) 72.1 89.9 +18pp
>
> 1G memcg (light pressure, build not memory-bound):
> metric mm-new + bypass delta
> build time (s) 177.7 176.0 ~same (no regression)
> zswap-in (GB) 10.2 7.5 -26%
> zswap-out (GB) 27.7 25.1 -9%
> swap readahead (pages) 1.07M 0.08M -93%
> swap_ra hit (%) 68.6 87.2 +19pp
>
> Similar gains were observed on an AMD EPYC 7D13 host.
>
> The gain is from no longer prefetching pages that are pointless for an
> in-memory backend: readahead inflates anon residency and thrashes the
> page cache (file pages get evicted and re-read), lengthens each fault by
> synchronously (de)compressing a cluster of neighbours, and adds
> compression traffic when those extra pages are reclaimed.
>
> Bypassing swap readahead for zswap therefore makes sense.
>
> Suggested-by: Usama Arif <usama.arif@linux.dev>
> Signed-off-by: Alexandre Ghiti <alex@ghiti.fr>
> ---
Hi Alexandre,
One thing on my mind is that for synchronous devices, we always free
the swapcache during swap-in, thus releasing the compressed copy and
metadata as well. We could do something similar when a zswap entry
is found.
I wonder if we should do that as well. I don't have time to
investigate this further myself, but I wonder if you'd be interested
in looking into it.
mm: free swapcache on zswap hits in do_swap_page()
We already do this for zram and other synchronous devices. The same
should apply to zswap hits. Freeing the swapcache will allow the
compressed copy and metadata to be released earlier.
Suggested-by: Barry Song (Xiaomi) <baohua@kernel.org>
diff --git a/mm/memory.c b/mm/memory.c
index 55b5a84d1016..d22c779fda93 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -4711,17 +4711,18 @@ static inline bool should_try_to_free_swap(struct swap_info_struct *si,
struct folio *folio,
struct vm_area_struct *vma,
bool exclusive,
- unsigned int fault_flags)
+ unsigned int fault_flags,
+ bool sync_swap)
{
if (!folio_test_swapcache(folio))
return false;
/*
- * Always try to free swap cache for SWP_SYNCHRONOUS_IO devices. Swap
+ * Always try to free swap cache for synchronize swap entries. Swap
* cache can help save some IO or memory overhead, but these devices
* are fast, and meanwhile, swap cache pinning the slot deferring the
* release of metadata or fragmentation is a more critical issue.
*/
- if (data_race(si->flags & SWP_SYNCHRONOUS_IO))
+ if (sync_swap)
return true;
if (mem_cgroup_swap_full(folio) || (vma->vm_flags & VM_LOCKED) ||
folio_test_mlocked(folio))
@@ -4977,6 +4978,7 @@ vm_fault_t do_swap_page(struct vm_fault *vmf)
rmap_t rmap_flags = RMAP_NONE;
bool exclusive = false;
bool rwp_restore = false;
+ bool sync_swap = false;
softleaf_t entry;
pte_t pte;
vm_fault_t ret = 0;
@@ -5059,7 +5061,8 @@ vm_fault_t do_swap_page(struct vm_fault *vmf)
swap_update_readahead(folio, vma, vmf->address);
if (!folio) {
/* Swapin bypasses readahead for SWP_SYNCHRONOUS_IO devices and zswap */
- if (swap_entry_synchronous(si, entry))
+ sync_swap = swap_entry_synchronous(si, entry);
+ if (sync_swap)
folio = swapin_sync(entry, GFP_HIGHUSER_MOVABLE,
thp_swapin_suitable_orders(vmf) | BIT(0),
vmf, NULL, 0);
@@ -5325,7 +5328,7 @@ vm_fault_t do_swap_page(struct vm_fault *vmf)
* Do it after mapping, so raced page faults will likely see the folio
* in swap cache and wait on the folio lock.
*/
- if (should_try_to_free_swap(si, folio, vma, exclusive, vmf->flags))
+ if (should_try_to_free_swap(si, folio, vma, exclusive, vmf->flags, sync_swap))
folio_free_swap(folio);
folio_unlock(folio);
^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2026-10-23 21:16 UTC | newest]
Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-07 15:39 [PATCH v3] mm: bypass swap readahead for zswap Alexandre Ghiti
2026-10-07 21:04 ` Barry Song
2026-10-09 6:22 ` Alexandre Ghiti
2026-10-12 6:59 ` Alexandre Ghiti
2026-10-12 8:01 ` Barry Song
2026-10-12 14:21 ` Nhat Pham
2026-10-12 14:56 ` Barry Song
2026-10-12 15:08 ` Barry Song
2026-10-07 21:37 ` Yosry Ahmed
2026-10-09 6:21 ` Alexandre Ghiti
2026-10-12 7:56 ` Barry Song
2026-10-13 18:55 ` Kairui Song
2026-10-12 15:08 ` Yosry Ahmed
2026-10-16 16:19 ` Nhat Pham
2026-10-23 21:16 ` Barry Song (Xiaomi)
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®