Re: [PATCH v2 2/2] mm/zswap: Support batch writeback in shrink_memcg()

From: Yosry Ahmed

Date: Mon Jul 27 2026 - 12:28:26 EST


On Sat, Jul 25, 2026 at 7:39 PM Hao Jia <jiahao.kernel@xxxxxxxxx> wrote:
>
>
>
> On 2026/7/25 06:22, Yosry Ahmed wrote:
> > On Fri, Jul 24, 2026 at 12:35 PM Johannes Weiner <hannes@xxxxxxxxxxx> wrote:
> >>
> >> On Fri, Jul 24, 2026 at 11:39:41AM -0700, Yosry Ahmed wrote:
> >>> On Fri, Jul 24, 2026 at 11:37 AM Nhat Pham <nphamcs@xxxxxxxxx> wrote:
> >>>>
> >>>> On Fri, Jul 24, 2026 at 10:57 AM Yosry Ahmed <yosry@xxxxxxxxxx> wrote:
> >>>>>
> >>>>> The numbers generally look good, the store rejection rate is
> >>>>> decreasing and we are naturally doing more writeback which makes
> >>>>> sense. The store rejection rate actually increases when the batch size
> >>>>> increases to 64, maybe aggressive writeback is more likely to fail and
> >>>>> cause a store rejection, so that also makes sense.
> >>>>>
> >>>>> The only part I can't immediately reason about is pswpin. How are we
> >>>>> reading from physical swap more than we ever wrote to it?
> >>>>
> >>>> The same page can be swapped out once, and swap in multiple times :)
> >>>>
> >>>> Unlike zswap, physical swap in is not necessarily exclusive. You load
> >>>> the page in memory, but if the swapfile is not full, etc. etc. you
> >>>> don't invalidate the copy of the data on the swapfile. At reclaim
> >>>> time, we notice the page is not dirty, so we just skip (z)swapout.
> >>>
> >>> Oh right, I forgot about that, thanks.
> >>>
> >>> I still don't fully understand why pswpin is significantly increased
> >>> with batching here. I would hope that with less LRU inversion we end
> >>> up with less disk swapin. Probably the test access patterns are just
> >>> too random compared to real workloads?
> >>
> >> Maybe more time in zswap for those tail entries left behind with the
> >> smaller batch?
> >>
> >> That's a 32 entry window that could zswpin with the smaller batch but
> >> pswpin with the larger one.
> >
> > Oh I was talking about going from no batching to batch=32. pspwin
> > increased by 387,400 (~30%). We are writing back 272,880 more pages
> > but we also have 209,837 less store rejections. So overall ~ 63,043
> > more pages should end up on disk, which doesn't explain the increase
> > in pspwin.
> >
>
> In my previous runs, I also collected zswpin. could the following
> explanation help account for what we are seeing?
>
> Assuming stress-ng maintains a roughly similar memory access rate,
> batching flushes significantly more pages back to disk (meaning pages in
> the zswap pool are evicted faster, keeping zswap pool residency lower).
> As a result, when stress-ng accesses swapped memory, it is more likely
> to fault pages in from disk (pswpin) rather than hit zswap (zswpin).

We are not just writing back more pages to disk, we are also accepting
more pages into zswap. The whole point of writeback here is to evict
colder pages to disk and accept (relatively) hotter pages into zswap,
as they are more likely to be swapped in again.

>
> In fact, zswpin in batch-32 dropped by 143,425 compared to
> baseline—which aligns with batching having a lower zswpin and a higher
> pswpin.
>
> To be frank, under the Test Case 2 workload, we cannot strictly
> guarantee that the total volume of page reads and writes remains
> consistent across all three runs within the same time window.
>
> baseline-cgroup batch-all-32-cgroup
> batch-all-64-cgroup
> shrink_worker wakeups 7,238 766 367
> shrink_memcg calls 12,059,142 1,961,194 983,878
> written_back 28,277 301,157 327,997
> zswap_store calls 1,349,572 1,168,190 1,114,549
> store succeeded 492,861 521,315 459,246
> store rejected 856,712 646,875 655,303
> store reject rate ~63% ~55% ~58%
> pool_limit_hit 510,130 50,096 57,715
> pswpout 884,989 948,032 983,300
> pswpin 1,251,268 1,638,668 1,878,453
> zswpout 492,861 521,314 459,245
> zswpin 309,631 166,206 84,977 <-

One possible explanation is that we are shrinking too aggressively due
to concurrent shrinkers. That's one difference between hitting the
global limit and the memcg limit. There's a single global shrinker
that all store failures will just wake up, while all concurrent stores
hitting the memcg limit will start shrinking.

The other explanation is that, as I mentioned earlier, the access
pattern is just too random and we are consistently refaulting cold
memory. I am honestly a bit concerned about these results.

Hao, would you be able to try re-running the test with a single
stressor? It would help us figure out if concurrent shrinking is a
problem. It would also be useful to increase the global limit, to
isolate writeback from hitting the memcg limit. I don't think both
limits will often be used in conjunction, at least not in a way where
both limits are hit simultaneously.

It may also be useful to run an actual workload if you are able to,
like a kernel build or something, to see if the access patterns change
how much we end up swapping in from zswap vs. disk.