* FAILED: patch "[PATCH] mm/madvise: atomically clear the young bit on THP MADV_COLD" failed to apply to 6.6-stable tree
@ 2026-11-07 10:38 Sasha Levin
2026-11-10 22:47 ` [PATCH 6.6.y 1/2] mm: madvise: pageout: ignore references rather than clearing young Sasha Levin
0 siblings, 1 reply; 3+ messages in thread
From: Sasha Levin @ 2026-11-07 10:38 UTC (permalink / raw)
To: pfalcato, akpm, samarth.colleges, gourry, ziy, ljs, david, sj,
liam, vbabka, jannh, minchan, mhocko, hannes
Cc: stable
The patch below does not apply to the 6.6-stable tree.
If someone wants it applied there, or to any other stable or longterm
tree, then please email the backport, including the original git commit
id to <stable@vger.kernel.org>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.6.y
git checkout FETCH_HEAD
git cherry-pick -x 246c8cc33438a71c28f9b86baa492d9a0a130316
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<stable@vger.kernel.org>' --in-reply-to '20261107053858-failed-6.6-246c8cc33438@kernel.org' --subject-prefix 'PATCH 6.6.y' 'HEAD^..'
Thanks,
Sasha
------------------ original commit in Linus's tree ------------------
From 246c8cc33438a71c28f9b86baa492d9a0a130316 Mon Sep 17 00:00:00 2001
From: Pedro Falcato <pfalcato@suse.de>
Date: Tue, 13 Oct 2026 20:17:19 +0100
Subject: [PATCH] mm/madvise: atomically clear the young bit on THP MADV_COLD
As-is, madvise_cold_or_pageout_pte_range() discards the hardware young bit
by invalidating the PMD, doing pmd_mkold() and subsequently writing to the
pmd again. However, it does so in a racy manner, looking at a
possibly-stale snapshot. It's entirely possible that a write elsewhere
set the hardware D bit, which this logic now loses.
Fix it by using pmdp_test_and_clear_young(). This is an atomic
modification of the PMD entry, and thus safe against any possible racing
writes. As a (nice) side-effect, this elides one non-required TLB flush
in pmdp_invalidate().
This bug can interact with the swapping system in complicated ways,
leading to data loss. For instance, if a folio is marked lazyfree, but
was later read from, and then MADV_COLD'd while racing a write to that
same THP, dropping the dirty bit may lead to data loss as
reclaim/MADV_PAGEOUT drops the "clean" anon folio:
T0 T1
================================================================
MADV_FREE(thp) |
folio marked lazyfree |
read(thp) |
PMD marked young by hardware |
| MADV_COLD(thp)
write(thp) | if (pmd_young(pmd)) {
PMD marked dirty by hardware | set(pmdp, pmd_mkold(pmd));
MADV_PAGEOUT/reclaim |
dirty bit may or may not be |
set |
reclaim/pageout may drop |
the folio |
Link: https://lore.kernel.org/20261013191719.95502-1-pfalcato@suse.de
Fixes: 9c276cc65a58 ("mm: introduce MADV_COLD")
Signed-off-by: Pedro Falcato <pfalcato@suse.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Reported-by: Samarth Narang <samarth.colleges@gmail.com>
Closes: https://lore.kernel.org/linux-mm/20261011132227.413217-1-samarth.colleges@gmail.com/
Reviewed-by: Gregory Price (Meta) <gourry@gourry.net>
Acked-by: Zi Yan <ziy@nvidia.com>
Reviewed-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
Acked-by: David Hildenbrand (Arm) <david@kernel.org>
Reviewed-by: SJ Park <sj@kernel.org>
Cc: Liam R. Howlett <liam@infradead.org>
Cc: Vlastimil Babka <vbabka@kernel.org>
Cc: Jann Horn <jannh@google.com>
Cc: Minchan Kim <minchan@kernel.org>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: <stable@vger.kernel.org>
---
mm/madvise.c | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/mm/madvise.c b/mm/madvise.c
index 8702b082a02d8..75b409e97dbf2 100644
--- a/mm/madvise.c
+++ b/mm/madvise.c
@@ -430,10 +430,7 @@ static int madvise_cold_or_pageout_pte_range(pmd_t *pmd,
}
if (!pageout && pmd_young(orig_pmd)) {
- pmdp_invalidate(vma, addr, pmd);
- orig_pmd = pmd_mkold(orig_pmd);
-
- set_pmd_at(mm, addr, pmd, orig_pmd);
+ pmdp_test_and_clear_young(vma, addr, pmd);
tlb_remove_pmd_tlb_entry(tlb, pmd, addr);
}
--
2.53.0
^ permalink raw reply related [flat|nested] 3+ messages in thread* [PATCH 6.6.y 1/2] mm: madvise: pageout: ignore references rather than clearing young 2026-11-07 10:38 FAILED: patch "[PATCH] mm/madvise: atomically clear the young bit on THP MADV_COLD" failed to apply to 6.6-stable tree Sasha Levin @ 2026-11-10 22:47 ` Sasha Levin 2026-11-10 22:47 ` [PATCH 6.6.y 2/2] mm/madvise: atomically clear the young bit on THP MADV_COLD Sasha Levin 0 siblings, 1 reply; 3+ messages in thread From: Sasha Levin @ 2026-11-10 22:47 UTC (permalink / raw) To: stable Cc: Barry Song, Minchan Kim, SeongJae Park, Michal Hocko, Johannes Weiner, Andrew Morton, Sasha Levin From: Barry Song <v-songbaohua@oppo.com> [ Upstream commit 2864f3d0f5831a50253befc5d4583868268b7153 ] While doing MADV_PAGEOUT, the current code will clear PTE young so that vmscan won't read young flags to allow the reclamation of madvised folios to go ahead. It seems we can do it by directly ignoring references, thus we can remove tlb flush in madvise and rmap overhead in vmscan. Regarding the side effect, in the original code, if a parallel thread runs side by side to access the madvised memory with the thread doing madvise, folios will get a chance to be re-activated by vmscan (though the time gap is actually quite small since checking PTEs is done immediately after clearing PTEs young). But with this patch, they will still be reclaimed. But this behaviour doing PAGEOUT and doing access at the same time is quite silly like DoS. So probably, we don't need to care. Or ignoring the new access during the quite small time gap is even better. For DAMON's DAMOS_PAGEOUT based on physical address region, we still keep its behaviour as is since a physical address might be mapped by multiple processes. MADV_PAGEOUT based on virtual address is actually much more aggressive on reclamation. To untouch paddr's DAMOS_PAGEOUT, we simply pass ignore_references as false in reclaim_pages(). A microbench as below has shown 6% decrement on the latency of MADV_PAGEOUT, #define PGSIZE 4096 main() { int i; #define SIZE 512*1024*1024 volatile long *p = mmap(NULL, SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); for (i = 0; i < SIZE/sizeof(long); i += PGSIZE / sizeof(long)) p[i] = 0x11; madvise(p, SIZE, MADV_PAGEOUT); } w/o patch w/ patch root@10:~# time ./a.out root@10:~# time ./a.out real 0m49.634s real 0m46.334s user 0m0.637s user 0m0.648s sys 0m47.434s sys 0m44.265s Link: https://lkml.kernel.org/r/20240226005739.24350-1-21cnbao@gmail.com Signed-off-by: Barry Song <v-songbaohua@oppo.com> Acked-by: Minchan Kim <minchan@kernel.org> Cc: SeongJae Park <sj@kernel.org> Cc: Michal Hocko <mhocko@suse.com> Cc: Johannes Weiner <hannes@cmpxchg.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Stable-dep-of: 246c8cc33438 ("mm/madvise: atomically clear the young bit on THP MADV_COLD") Signed-off-by: Sasha Levin <sashal@kernel.org> --- mm/damon/paddr.c | 2 +- mm/internal.h | 2 +- mm/madvise.c | 8 ++++---- mm/vmscan.c | 12 +++++++----- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/mm/damon/paddr.c b/mm/damon/paddr.c index fe481ae64f37e..200bb7e64fd88 100644 --- a/mm/damon/paddr.c +++ b/mm/damon/paddr.c @@ -237,7 +237,7 @@ static unsigned long damon_pa_pageout(struct damon_region *r, struct damos *s) put_folio: folio_put(folio); } - applied = reclaim_pages(&folio_list); + applied = reclaim_pages(&folio_list, false); cond_resched(); return applied * PAGE_SIZE; } diff --git a/mm/internal.h b/mm/internal.h index 03cfd9f736597..5e1642a8e5553 100644 --- a/mm/internal.h +++ b/mm/internal.h @@ -874,7 +874,7 @@ extern unsigned long __must_check vm_mmap_pgoff(struct file *, unsigned long, unsigned long, unsigned long); extern void set_pageblock_order(void); -unsigned long reclaim_pages(struct list_head *folio_list); +unsigned long reclaim_pages(struct list_head *folio_list, bool ignore_references); unsigned int reclaim_clean_pages_from_list(struct zone *zone, struct list_head *folio_list); /* The ALLOC_WMARK bits are used as an index to zone->watermark */ diff --git a/mm/madvise.c b/mm/madvise.c index 209d0cc950139..7d3a9c4b993f0 100644 --- a/mm/madvise.c +++ b/mm/madvise.c @@ -403,7 +403,7 @@ static int madvise_cold_or_pageout_pte_range(pmd_t *pmd, return 0; } - if (pmd_young(orig_pmd)) { + if (!pageout && pmd_young(orig_pmd)) { pmdp_invalidate(vma, addr, pmd); orig_pmd = pmd_mkold(orig_pmd); @@ -427,7 +427,7 @@ static int madvise_cold_or_pageout_pte_range(pmd_t *pmd, huge_unlock: spin_unlock(ptl); if (pageout) - reclaim_pages(&folio_list); + reclaim_pages(&folio_list, true); cond_resched(); return 0; } @@ -497,7 +497,7 @@ static int madvise_cold_or_pageout_pte_range(pmd_t *pmd, VM_BUG_ON_FOLIO(folio_test_large(folio), folio); - if (pte_young(ptent)) { + if (!pageout && pte_young(ptent)) { ptent = ptep_get_and_clear_full(mm, addr, pte, tlb->fullmm); ptent = pte_mkold(ptent); @@ -531,7 +531,7 @@ static int madvise_cold_or_pageout_pte_range(pmd_t *pmd, pte_unmap_unlock(start_pte, ptl); } if (pageout) - reclaim_pages(&folio_list); + reclaim_pages(&folio_list, true); cond_resched(); return 0; diff --git a/mm/vmscan.c b/mm/vmscan.c index cbc2de77db3ed..32058c20e12d8 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -2775,7 +2775,8 @@ static void shrink_active_list(unsigned long nr_to_scan, } static unsigned int reclaim_folio_list(struct list_head *folio_list, - struct pglist_data *pgdat) + struct pglist_data *pgdat, + bool ignore_references) { struct reclaim_stat dummy_stat; unsigned int nr_reclaimed; @@ -2788,7 +2789,7 @@ static unsigned int reclaim_folio_list(struct list_head *folio_list, .no_demotion = 1, }; - nr_reclaimed = shrink_folio_list(folio_list, pgdat, &sc, &dummy_stat, false); + nr_reclaimed = shrink_folio_list(folio_list, pgdat, &sc, &dummy_stat, ignore_references); while (!list_empty(folio_list)) { folio = lru_to_folio(folio_list); list_del(&folio->lru); @@ -2798,7 +2799,7 @@ static unsigned int reclaim_folio_list(struct list_head *folio_list, return nr_reclaimed; } -unsigned long reclaim_pages(struct list_head *folio_list) +unsigned long reclaim_pages(struct list_head *folio_list, bool ignore_references) { int nid; unsigned int nr_reclaimed = 0; @@ -2820,11 +2821,12 @@ unsigned long reclaim_pages(struct list_head *folio_list) continue; } - nr_reclaimed += reclaim_folio_list(&node_folio_list, NODE_DATA(nid)); + nr_reclaimed += reclaim_folio_list(&node_folio_list, NODE_DATA(nid), + ignore_references); nid = folio_nid(lru_to_folio(folio_list)); } while (!list_empty(folio_list)); - nr_reclaimed += reclaim_folio_list(&node_folio_list, NODE_DATA(nid)); + nr_reclaimed += reclaim_folio_list(&node_folio_list, NODE_DATA(nid), ignore_references); memalloc_noreclaim_restore(noreclaim_flag); -- 2.53.0 ^ permalink raw reply related [flat|nested] 3+ messages in thread
* [PATCH 6.6.y 2/2] mm/madvise: atomically clear the young bit on THP MADV_COLD 2026-11-10 22:47 ` [PATCH 6.6.y 1/2] mm: madvise: pageout: ignore references rather than clearing young Sasha Levin @ 2026-11-10 22:47 ` Sasha Levin 0 siblings, 0 replies; 3+ messages in thread From: Sasha Levin @ 2026-11-10 22:47 UTC (permalink / raw) To: stable Cc: Pedro Falcato, Andrew Morton, Samarth Narang, Gregory Price (Meta), Zi Yan, Lorenzo Stoakes (ARM), David Hildenbrand (Arm), SJ Park, Liam R. Howlett, Vlastimil Babka, Jann Horn, Minchan Kim, Michal Hocko, Johannes Weiner, Sasha Levin From: Pedro Falcato <pfalcato@suse.de> [ Upstream commit 246c8cc33438a71c28f9b86baa492d9a0a130316 ] As-is, madvise_cold_or_pageout_pte_range() discards the hardware young bit by invalidating the PMD, doing pmd_mkold() and subsequently writing to the pmd again. However, it does so in a racy manner, looking at a possibly-stale snapshot. It's entirely possible that a write elsewhere set the hardware D bit, which this logic now loses. Fix it by using pmdp_test_and_clear_young(). This is an atomic modification of the PMD entry, and thus safe against any possible racing writes. As a (nice) side-effect, this elides one non-required TLB flush in pmdp_invalidate(). This bug can interact with the swapping system in complicated ways, leading to data loss. For instance, if a folio is marked lazyfree, but was later read from, and then MADV_COLD'd while racing a write to that same THP, dropping the dirty bit may lead to data loss as reclaim/MADV_PAGEOUT drops the "clean" anon folio: T0 T1 ================================================================ MADV_FREE(thp) | folio marked lazyfree | read(thp) | PMD marked young by hardware | | MADV_COLD(thp) write(thp) | if (pmd_young(pmd)) { PMD marked dirty by hardware | set(pmdp, pmd_mkold(pmd)); MADV_PAGEOUT/reclaim | dirty bit may or may not be | set | reclaim/pageout may drop | the folio | Link: https://lore.kernel.org/20261013191719.95502-1-pfalcato@suse.de Fixes: 9c276cc65a58 ("mm: introduce MADV_COLD") Signed-off-by: Pedro Falcato <pfalcato@suse.de> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Reported-by: Samarth Narang <samarth.colleges@gmail.com> Closes: https://lore.kernel.org/linux-mm/20261011132227.413217-1-samarth.colleges@gmail.com/ Reviewed-by: Gregory Price (Meta) <gourry@gourry.net> Acked-by: Zi Yan <ziy@nvidia.com> Reviewed-by: Lorenzo Stoakes (ARM) <ljs@kernel.org> Acked-by: David Hildenbrand (Arm) <david@kernel.org> Reviewed-by: SJ Park <sj@kernel.org> Cc: Liam R. Howlett <liam@infradead.org> Cc: Vlastimil Babka <vbabka@kernel.org> Cc: Jann Horn <jannh@google.com> Cc: Minchan Kim <minchan@kernel.org> Cc: Michal Hocko <mhocko@suse.com> Cc: Johannes Weiner <hannes@cmpxchg.org> Cc: <stable@vger.kernel.org> Signed-off-by: Sasha Levin <sashal@kernel.org> --- mm/madvise.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/mm/madvise.c b/mm/madvise.c index 7d3a9c4b993f0..d9f55db95a9b1 100644 --- a/mm/madvise.c +++ b/mm/madvise.c @@ -404,10 +404,7 @@ static int madvise_cold_or_pageout_pte_range(pmd_t *pmd, } if (!pageout && pmd_young(orig_pmd)) { - pmdp_invalidate(vma, addr, pmd); - orig_pmd = pmd_mkold(orig_pmd); - - set_pmd_at(mm, addr, pmd, orig_pmd); + pmdp_test_and_clear_young(vma, addr, pmd); tlb_remove_pmd_tlb_entry(tlb, pmd, addr); } -- 2.53.0 ^ permalink raw reply related [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-11-10 22:47 UTC | newest] Thread overview: 3+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-11-07 10:38 FAILED: patch "[PATCH] mm/madvise: atomically clear the young bit on THP MADV_COLD" failed to apply to 6.6-stable tree Sasha Levin 2026-11-10 22:47 ` [PATCH 6.6.y 1/2] mm: madvise: pageout: ignore references rather than clearing young Sasha Levin 2026-11-10 22:47 ` [PATCH 6.6.y 2/2] mm/madvise: atomically clear the young bit on THP MADV_COLD Sasha Levin
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox