* [PATCH v5] mm/memcg: clear folio memcg after changing per memcg stats
@ 2026-09-10 3:46 Bingfang Guo via B4 Relay
2026-09-10 5:59 ` Andrew Morton
0 siblings, 1 reply; 3+ messages in thread
From: Bingfang Guo via B4 Relay @ 2026-09-10 3:46 UTC (permalink / raw)
To: Andrew Morton, Chris Li, Kairui Song, Kemeng Shi, Nhat Pham,
Baoquan He, Barry Song, Youngjun Park, Qi Zheng, Shakeel Butt,
Axel Rasmussen, Yuanchu Xie, Wei Xu, Johannes Weiner,
David Hildenbrand, Michal Hocko, Lorenzo Stoakes, Bingfang Guo
Cc: linux-mm, linux-kernel, stable, Bingfang Guo
From: Bingfang Guo <bingfangguo@tencent.com>
I notice extremely high swapcached count in the per memcg level
memory.stat when running tests with cgroupv1 setup by swapping pages in
and out. It seems that the counter never gets decreased so the value is
rather useless and confusing to users reading it. So I think fixing it
so that the value can reflect the actual swapcache usage correctly could
be helpful.
__memcg1_swapout() transfers the memsw charge of a folio to its swap
entry and clears folio->memcg_data as part of that. In the vmscan
swapout path it runs before __swap_cache_del_folio(), which then
decrements the swapcache stats through lruvec_stat_mod_folio(). Since
folio->memcg_data has already been cleared, folio_memcg() returns NULL
and the NR_SWAPCACHE decrement only updates the node-level counter
instead of the memcg's lruvec, leaking the per-memcg swapcache count.
Move the __memcg1_swapout() call into __swap_cache_del_folio(), after
the NR_FILE_PAGES and NR_SWAPCACHE updates but before
__swap_cache_do_del_folio() removes the folio from the swap cache. This
keeps the stats attributed to the folio's memcg while still recording
the swap cgroup with a valid folio->swap. Add a swapout parameter so
the plain swap_cache_del_folio() path is left unchanged.
Fixes: 2732acda82c9 ("mm, swap: use swap cache as the swap in synchronize layer")
Signed-off-by: Bingfang Guo <bingfangguo@tencent.com>
Acked-by: Kairui Song <kasong@tencent.com>
---
** Testing **
The problem can be reproduced by populating 256MB of shmem pages in a
memcg and then swapping them out using pageout. After the operation, we
can still see those pages in the swapcached counter by reading
memory.stats. After applying the given patch, the value is correctly
decreased to 0.
The test script and source can be found from v4 (below).
** Test Results **
before the patch
```
== before reclaim ==
swapcached 0
allocated and dirtied 256 MiB, paging out...
paged out; sleeping so memory.stat can be read. pid=4778
== after reclaim (swap cache should drain to ~0) ==
swapcached 268435456
LEAK DETECTED: swapcached = 268435456 bytes (expected ~0) [BUGGY kernel]
```
after the patch
```
== before reclaim ==
swapcached 0
allocated and dirtied 256 MiB, paging out...
paged out; sleeping so memory.stat can be read. pid=2601
== after reclaim (swap cache should drain to ~0) ==
swapcached 0
OK: swapcached = 0 bytes [FIXED kernel]
```
---
Changes in v5:
- Refine the param description. (Kairui)
- Collect Acked-by tags.
- Link to v4: https://lore.kernel.org/r/20260905-memcg-swapcache-stats-fix-v4-1-d3626d305d4f@tencent.com
Changes in v4:
- Update and use the correct fixes tag. (Kairui)
- Briefly describe the context for @swapout param in the kdoc. (Kairui)
- Cc the stable list. (Kairui)
- Link to v3: https://lore.kernel.org/r/20260902-memcg-swapcache-stats-fix-v3-1-795f5d455f7b@tencent.com
Changes in v3:
- Add doc for the new parameter.
- Link to v2: https://lore.kernel.org/r/20260901-memcg-swapcache-stats-fix-v2-1-9caad330459b@tencent.com
Changes in v2:
- Update the commit message to describe the problem in the beginnning.
- Change function declaration for !CONFIG_SWAP as well.
- Link to v1: https://lore.kernel.org/r/20260831-memcg-swapcache-stats-fix-v1-1-1c0819ebdb86@tencent.com
---
mm/swap.h | 6 ++++--
mm/swap_state.c | 13 ++++++++++---
mm/vmscan.c | 3 +--
3 files changed, 15 insertions(+), 7 deletions(-)
diff --git a/mm/swap.h b/mm/swap.h
index 0b5d507739bcb..b3b54c28929a1 100644
--- a/mm/swap.h
+++ b/mm/swap.h
@@ -319,7 +319,8 @@ struct folio *swap_cache_alloc_folio(swp_entry_t target_entry, gfp_t gfp_mask,
void __swap_cache_add_folio(struct swap_cluster_info *ci,
struct folio *folio, swp_entry_t entry);
void __swap_cache_del_folio(struct swap_cluster_info *ci,
- struct folio *folio, swp_entry_t entry, void *shadow);
+ struct folio *folio, swp_entry_t entry, void *shadow,
+ bool swapout);
void __swap_cache_replace_folio(struct swap_cluster_info *ci,
struct folio *old, struct folio *new);
@@ -452,7 +453,8 @@ static inline void swap_cache_del_folio(struct folio *folio)
}
static inline void __swap_cache_del_folio(struct swap_cluster_info *ci,
- struct folio *folio, swp_entry_t entry, void *shadow)
+ struct folio *folio, swp_entry_t entry, void *shadow,
+ bool swapout)
{
}
diff --git a/mm/swap_state.c b/mm/swap_state.c
index 305877e1f4d7b..625c185a1ca4d 100644
--- a/mm/swap_state.c
+++ b/mm/swap_state.c
@@ -306,21 +306,28 @@ static void __swap_cache_do_del_folio(struct swap_cluster_info *ci,
* @folio: The folio.
* @entry: The first swap entry that the folio corresponds to.
* @shadow: shadow value to be filled in the swap cache.
+ * @swapout: whether this folio is being reclaimed after swapout.
*
* Removes a folio from the swap cache and fills a shadow in place.
* This won't put the folio's refcount. The caller has to do that.
*
* Context: Caller must ensure the folio is locked and in the swap cache
* using the index of @entry, and lock the cluster that holds the entries.
+ * If @swapout is set, the folio should be in reclaim path and IRQs
+ * should be disabled.
*/
void __swap_cache_del_folio(struct swap_cluster_info *ci, struct folio *folio,
- swp_entry_t entry, void *shadow)
+ swp_entry_t entry, void *shadow, bool swapout)
{
unsigned long nr_pages = folio_nr_pages(folio);
- __swap_cache_do_del_folio(ci, folio, entry, shadow);
node_stat_mod_folio(folio, NR_FILE_PAGES, -nr_pages);
lruvec_stat_mod_folio(folio, NR_SWAPCACHE, -nr_pages);
+
+ if (swapout)
+ __memcg1_swapout(folio, ci);
+
+ __swap_cache_do_del_folio(ci, folio, entry, shadow);
}
/**
@@ -339,7 +346,7 @@ void swap_cache_del_folio(struct folio *folio)
swp_entry_t entry = folio->swap;
ci = swap_cluster_lock(__swap_entry_to_info(entry), swp_offset(entry));
- __swap_cache_del_folio(ci, folio, entry, NULL);
+ __swap_cache_del_folio(ci, folio, entry, NULL, false);
swap_cluster_unlock(ci);
folio_ref_sub(folio, folio_nr_pages(folio));
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 245f68c75b289..29bced43b0ce6 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -755,8 +755,7 @@ static int __remove_mapping(struct address_space *mapping, struct folio *folio,
if (reclaimed && !mapping_exiting(mapping))
shadow = workingset_eviction(folio, target_memcg);
- __memcg1_swapout(folio, ci);
- __swap_cache_del_folio(ci, folio, swap, shadow);
+ __swap_cache_del_folio(ci, folio, swap, shadow, true);
swap_cluster_unlock_irq(ci);
} else {
void (*free_folio)(struct folio *);
---
base-commit: b5529903123d9535bcf74386c1f4e185e8632dd9
change-id: 20260828-memcg-swapcache-stats-fix-1beef3dc3afb
Best regards,
--
Bingfang Guo <bingfangguo@tencent.com>
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH v5] mm/memcg: clear folio memcg after changing per memcg stats
2026-09-10 3:46 [PATCH v5] mm/memcg: clear folio memcg after changing per memcg stats Bingfang Guo via B4 Relay
@ 2026-09-10 5:59 ` Andrew Morton
2026-09-10 7:40 ` Bingfang Guo
0 siblings, 1 reply; 3+ messages in thread
From: Andrew Morton @ 2026-09-10 5:59 UTC (permalink / raw)
To: bingfangguo
Cc: Bingfang Guo via B4 Relay, Chris Li, Kairui Song, Kemeng Shi,
Nhat Pham, Baoquan He, Barry Song, Youngjun Park, Qi Zheng,
Shakeel Butt, Axel Rasmussen, Yuanchu Xie, Wei Xu,
Johannes Weiner, David Hildenbrand, Michal Hocko,
Lorenzo Stoakes, Bingfang Guo, linux-mm, linux-kernel, stable
On Thu, 10 Sep 2026 11:46:58 +0800 Bingfang Guo via B4 Relay <devnull+bingfangguo.tencent.com@kernel.org> wrote:
> I notice extremely high swapcached count in the per memcg level
> memory.stat when running tests with cgroupv1 setup by swapping pages in
> and out. It seems that the counter never gets decreased so the value is
> rather useless and confusing to users reading it. So I think fixing it
> so that the value can reflect the actual swapcache usage correctly could
> be helpful.
>
> __memcg1_swapout() transfers the memsw charge of a folio to its swap
> entry and clears folio->memcg_data as part of that. In the vmscan
> swapout path it runs before __swap_cache_del_folio(), which then
> decrements the swapcache stats through lruvec_stat_mod_folio(). Since
> folio->memcg_data has already been cleared, folio_memcg() returns NULL
> and the NR_SWAPCACHE decrement only updates the node-level counter
> instead of the memcg's lruvec, leaking the per-memcg swapcache count.
>
> Move the __memcg1_swapout() call into __swap_cache_del_folio(), after
> the NR_FILE_PAGES and NR_SWAPCACHE updates but before
> __swap_cache_do_del_folio() removes the folio from the swap cache. This
> keeps the stats attributed to the folio's memcg while still recording
> the swap cgroup with a valid folio->swap. Add a swapout parameter so
> the plain swap_cache_del_folio() path is left unchanged.
Thanks.
> Fixes: 2732acda82c9 ("mm, swap: use swap cache as the swap in synchronize layer")
January this year.
How does this affect users? Should we backport the fix into earlier
kernels? If so, why?
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH v5] mm/memcg: clear folio memcg after changing per memcg stats
2026-09-10 5:59 ` Andrew Morton
@ 2026-09-10 7:40 ` Bingfang Guo
0 siblings, 0 replies; 3+ messages in thread
From: Bingfang Guo @ 2026-09-10 7:40 UTC (permalink / raw)
To: Andrew Morton
Cc: bingfangguo, Bingfang Guo via B4 Relay, Chris Li, Kairui Song,
Kemeng Shi, Nhat Pham, Baoquan He, Barry Song, Youngjun Park,
Qi Zheng, Shakeel Butt, Axel Rasmussen, Yuanchu Xie, Wei Xu,
Johannes Weiner, David Hildenbrand, Michal Hocko,
Lorenzo Stoakes, linux-mm, linux-kernel, stable
On Wed, Sep 09, 2026 at 10:59:04PM -0700, Andrew Morton wrote:
> On Thu, 10 Sep 2026 11:46:58 +0800 Bingfang Guo via B4 Relay <devnull+bingfangguo.tencent.com@kernel.org> wrote:
>
>> I notice extremely high swapcached count in the per memcg level
>> memory.stat when running tests with cgroupv1 setup by swapping pages in
>> and out. It seems that the counter never gets decreased so the value is
>> rather useless and confusing to users reading it. So I think fixing it
>> so that the value can reflect the actual swapcache usage correctly could
>> be helpful.
>>
>> __memcg1_swapout() transfers the memsw charge of a folio to its swap
>> entry and clears folio->memcg_data as part of that. In the vmscan
>> swapout path it runs before __swap_cache_del_folio(), which then
>> decrements the swapcache stats through lruvec_stat_mod_folio(). Since
>> folio->memcg_data has already been cleared, folio_memcg() returns NULL
>> and the NR_SWAPCACHE decrement only updates the node-level counter
>> instead of the memcg's lruvec, leaking the per-memcg swapcache count.
>>
>> Move the __memcg1_swapout() call into __swap_cache_del_folio(), after
>> the NR_FILE_PAGES and NR_SWAPCACHE updates but before
>> __swap_cache_do_del_folio() removes the folio from the swap cache. This
>> keeps the stats attributed to the folio's memcg while still recording
>> the swap cgroup with a valid folio->swap. Add a swapout parameter so
>> the plain swap_cache_del_folio() path is left unchanged.
>
> Thanks.
>
>> Fixes: 2732acda82c9 ("mm, swap: use swap cache as the swap in synchronize layer")
>
> January this year.
>
> How does this affect users? Should we backport the fix into earlier
> kernels? If so, why?
>
Thanks.
Users using swaps will read totally meaningless swapcache value
from per memcg memory.stat, like 200GB of swapcache on a 64GB
setup, which is quite confusing and may trigger monitoring
alerts, if any.
So I think we should backport this patch. Although I don't
really know user space programs relying on this value to work, I
think it's always good to make sure that values provided by the
kernel are helpful and mean something.
Regards,
Bingfang
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-09-10 7:40 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-10 3:46 [PATCH v5] mm/memcg: clear folio memcg after changing per memcg stats Bingfang Guo via B4 Relay
2026-09-10 5:59 ` Andrew Morton
2026-09-10 7:40 ` Bingfang Guo
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®