Re: [PATCH v3 2/3] mm: swap: drop dropbehind swap cache folios on writeback completion

KunWu Chan <[email protected]>
Newsgroups org.kvack.linux-mm,org.kernel.vger.linux-fsdevel,org.kernel.vger.linux-kernel
Message-ID <CAN_Opa8Bp3udH2hVGMGwomncFRrZ11FpqkN7gogPgYJYNtAe0Q@mail.gmail.com>
On Mon, Aug 24, 2026 at 5:30 PM Alexandre Ghiti <[email protected]> wrote:
>
> On Fri, Aug 21, 2026 at 11:53 AM Alexandre Ghiti <[email protected]> wrote:
> >
> > Hi Kunwu,
> >
> > On Fri, Aug 21, 2026 at 10:35 AM Kunwu Chan <[email protected]> wrote:
> > >
> > > > >
> > >
> > > Hi Alexandre,
> > >
> > > I have a question about the reference ownership/lifetime model in the
> > > synchronous-IO case.
> > >
> > > Consider the synchronous writeback path in Patch 3:
> > >
> > >   zswap_writeback_entry()
> > >     folio = __swap_cache_alloc_folio(...);
> > >     ...
> > >     __swap_writepage(folio, NULL);
> > >     folio_put(folio);
> > >
> > > During writeback completion, Patch 2 does:
> > >
> > >   folio_end_writeback(folio)
> > >     folio_get(folio);
> > >     ...
> > >     swap_writeback_dropbehind_folio(folio);
> > >       llist_add()
> > >       queue_work()
> > >
> > > With synchronous I/O, is it possible for the dropbehind worker to run
> > > before zswap_writeback_entry() drops its reference after
> > > __swap_writepage() returns?
> >
> > Yes, totally! The window is short though and in that rare case, we
> > would fall back to the initial behaviour (almost, since we put the
> > folio back onto lru but not rotate it). And for async devices, I don't
> > think it can happen at all.
> >
> > >
> > > If so, the worker will attempt:
> > >
> > >   swap_dropbehind_drop_folio(folio)
> > >     ...
> > >     remove_mapping(swap_address_space(folio->swap), folio, true, memcg)
> > >
> > > while the caller's reference is still held.  Looking at
> > > __remove_mapping() in mm/vmscan.c, it expects a refcount of
> > > 1 + folio_nr_pages(folio), but the extra reference still held by
> > > zswap_writeback_entry() would make the actual refcount one higher.
> > > It therefore looks like folio_ref_freeze() will fail in this case.
> > > Is that the intended behavior here?
> > >
> > > If the removal can fail, the fallback is:
> > >
> > >     folio_clear_dropbehind(folio);
> > >     folio_add_lru(folio);
> > >
> > > This appears semantically safe, but it means that the dropbehind
> > > optimization is lost for that writeback: the cold folio goes back onto
> > > the LRU and has to be found by reclaim later.
> > >
> > > This seems particularly worth checking because v3 intentionally removes
> > > the synchronous-I/O special case from v2.  The cover letter describes
> > > that special case as an optimization that was not worth the extra code,
> > > which I agree is a reasonable direction if the generic path is
> > > sufficiently effective.
> > >
> > > Could you measure the corresponding fallback rate on a synchronous
> > > backend (e.g. zram)?  The 99.996% success rate in the cover letter is for
> > > asynchronous NVMe, so it does not tell us how often this particular race
> > > occurs with synchronous completion.
> > >
> > > More generally, I'd like the reference ownership across
> > > __swap_writepage(), folio_end_writeback(), and the deferred worker to be
> > > made explicit.  If the caller's reference can overlap with the worker's
> > > reference, I'd also like to understand whether that is an intentional
> > > and acceptable trade-off, or whether the overlap can be avoided without
> > > reintroducing the synchronous-I/O special case that v3 is trying to
> > > remove.
> >
> > I need to look into the patchset that Matthew and Barry pointed to and
> > check if it's still relevant with this patchset on top of it. If so,
> > I'll add a debug counter to confirm we don't fall back too much for
> > sync devices :)
> >
>

Hi Alex,

Thanks for checking this and for the detailed explanation.

> As you noted, in my implementation, a race was possible between the
> worker and the folio_put() in zswap_writeback_entry(). Now with Tal's
> patchset which *only* defers atomic context to a workqueue, the
> dropbehind folios on synchronous devices get freed inline which is not
> possible because of the reference held by the zswap writeback path.
>
> But it's cleanly fixable by dropping the zswap reference before
> __swap_writepage(): the folio is locked (when it is allocated) until
> writeback starts, then PG_writeback prevents the folio removal from
> the swapcache and right before clearing PG_writeback in
> folio_end_writeback_no_dropbehind(), folio_end_writeback() takes a
> reference! So remove_mapping() is happy with the refcount :)
>

That makes sense.
Dropping the zswap reference before __swap_writepage()  avoids the
overlap, while PG_writeback prevents the folio from being removed  from
the swap cache until writeback completion.
The reference taken before clearing PG_writeback then ensures that
remove_mapping() sees the expected refcount.

I agree this is cleaner than bringing back the synchronous-I/O special case.

> So there are no more races as you pointed out, since we drop the
> reference before the writeback. I'll post the v4 today.

I'll take a look at v4 after it is posted.

Thanks,
KunWu

>
> Thanks again for your comment.
>
> Alex
>
>
>
> > Thanks for your comment!
> >
> > Alex
> >
> > >
> > > Thanks,
> > > KunWu
> > >
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.