mm/madvise: swap in CoW'd MAP_PRIVATE-file mappings on MADV_WILLNEED

Currently MADV_WILLNEED treats file-backed and pure anonymous mappings
entirely separately - using POSIX_FADV_WILLNEED (equivalent of a
readahead) for the former and a tree walk and swap in to swap cache for
the latter.

MAP_PRIVATE-file backed mappings straddle the two and currently get
treated as if they were purely file-backed, meaning any swapped out
private pages remain swapped out.

Resolve the issue by explicitly checking for CoW'd MAP_PRIVATE-file backed
mappings and performing both walks in this case.

Since the logic checks for vma->anon_vma this means un-CoW'd
MAP_PRIVATE-file backed mappings retain only the single file walk.

Link: https://lore.kernel.org/aprjOxDy3JCPb2oa@gremlin
Reported-by: Mike Kaplinskiy <mike@recall.ai>
Closes: https://lore.kernel.org/all/CABeknB_S2XJSHFgnHdgnN0rjzHhH4oQJs_APq9fvxHztQ_pgiA@mail.gmail.com/
Signed-off-by: Lorenzo Stoakes (ARM) <ljs@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Acked-by: Vlastimil Babka (SUSE) <vbabka@kernel.org>
Reviewed-by: Pedro Falcato <pfalcato@suse.de>
Cc: David Hildenbrand <david@kernel.org>
Cc: Jann Horn <jannh@google.com>
Cc: Liam R. Howlett <liam@infradead.org>
This commit is contained in:
Lorenzo Stoakes (ARM) 2026-09-03 20:08:39 +01:00 committed by Andrew Morton
parent ba99faaf67
commit 4cac0fd312

View File

@ -297,11 +297,12 @@ static long madvise_willneed(struct madvise_behavior *madv_behavior)
loff_t offset;
#ifdef CONFIG_SWAP
if (!file) {
if (vma_is_cow_mapping(vma) && vma->anon_vma) {
walk_page_range_vma(vma, start, end, &swapin_walk_ops, vma);
lru_add_drain(); /* Push any new pages onto the LRU now */
return 0;
}
if (!file)
return 0;
if (shmem_mapping(file->f_mapping)) {
shmem_swapin_range(vma, start, end, file->f_mapping);