Re: [PATCH v2 3/3] mm/zswap: preserve the workingset shadow across writeback
Alexandre Ghiti <[email protected]>
| Newsgroups | org.kvack.linux-mm,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <CAEmasaV8tzcebVQNbV-UfBygPDbJPct6J_F-9kY8T9MxjRcjbw@mail.gmail.com> |
Hi David, On Fri, Aug 21, 2026 at 11:58 AM David Hildenbrand (Arm) <[email protected]> wrote: > > > > On 8/21/26 11:35, Alexandre Ghiti wrote: > > When zswap writes an entry back, allocating the buffer folio in the swap > > cache overwrites the swap slot's workingset shadow. It is lost and later > > re-minted inaccurately, which corrupts anon refault/workingset accounting > > for the written-back data. > > > > Preserve it within zswap itself: at writeback, park the shadow in the > > zswap tree in place of the freed entry, so it outlives the writeback > > buffer folio. The buffer then meets one of three fates, and the parked > > shadow is handled at whichever happens first: > > > > - swap-in: do_swap_page()/shmem_swapin_folio() find the buffer in the swap > > cache, take the shadow and feed it to workingset_refault() to account > > the refault of the now-resident folio, then drop it. > > - reclaim: __remove_mapping() skips minting a fresh shadow for the buffer > > (minting would double-count the eviction) and > > __swap_cache_do_del_folio() restores the parked shadow into the slot, so > > a later disk swap-in still refaults against the original eviction. > > - slot free (e.g. process exit): the shadow is not needed and is cleared. > > > > The last two both go through __swap_cache_do_del_folio(), the single point > > every swap-cache removal passes through, which is why the shadow is read > > and cleared there. > > > > Fixes: aae466b0052e ("mm/swap: implement workingset detection for anonymous LRU") > > Signed-off-by: Nhat Pham <[email protected]> > > Signed-off-by: Alexandre Ghiti <[email protected]> > > --- > > include/linux/swap.h | 2 +- > > include/linux/zswap.h | 12 ++++++ > > mm/filemap.c | 2 +- > > mm/memory.c | 5 +++ > > mm/shmem.c | 5 +++ > > mm/swap_state.c | 14 ++++++- > > mm/vmscan.c | 4 +- > > mm/workingset.c | 16 ++++++-- > > mm/zswap.c | 91 ++++++++++++++++++++++++++++++++++++++++++- > > 9 files changed, 142 insertions(+), 9 deletions(-) > > > > diff --git a/include/linux/swap.h b/include/linux/swap.h > > index 6d72778e6cc3..ef9b1612ac12 100644 > > --- a/include/linux/swap.h > > +++ b/include/linux/swap.h > > @@ -298,7 +298,7 @@ bool workingset_test_recent(void *shadow, bool file, bool *workingset, > > bool flush); > > void workingset_age_nonresident(struct lruvec *lruvec, unsigned long nr_pages); > > void *workingset_eviction(struct folio *folio, struct mem_cgroup *target_memcg); > > -void workingset_refault(struct folio *folio, void *shadow); > > +void workingset_refault(struct folio *folio, void *shadow, bool lru_managed); > > Passing booleans makes calling code hard to read. It's preferable to either use > > (a) Dedicated flags; only makes sense if there are really more flags expected > > (b) Having helpfer functions that spell it out for calling code > > workingset_refault_lru_managed > workingset_refault > > workingset_refault_lru > workingset_refault_nonlru > > Or something like that, which makes calling code easier to get. Ok, I'll see which of (a) or (b) is less intrusive. Thanks, Alex > > -- > Cheers, > > David >