From 854e5ff8b3ce2a776586cabebaeae645c992345b Mon Sep 17 00:00:00 2001 From: Kyle Zeng Date: Tue, 6 Oct 2026 21:10:01 -0700 Subject: [PATCH 01/43] mm/khugepaged: flush deferred unmaps before dropping a failed folio collapse_file() can fail its reference-count or dirty-folio check after unmapping with TTU_BATCH_FLUSH. Both paths put back the isolated folio, then unlock it and drop the lookup reference before reaching the common try_to_unmap_flush(). The page-cache reference does not keep the folio stable once the lock is released. Another collapse can replace and free it. With its PTEs already gone, that collapse cannot flush the first task's per-task TLB batch, and retract_page_tables() skips short or unaligned VMAs. A CPU can therefore retain a user translation to the freed folio. This has been reproduced with unprivileged MADV_COLLAPSE on a memfd. Flush at out_unlock while the lookup reference and folio lock are still held. The common flush continues to cover the accumulated pagelist on both success and rollback, preserving batching on successful collapses. Link: https://lore.kernel.org/20261007041001.43181-1-kylebot@openai.com Fixes: 6d9df8a5889c ("mm/thp: collapse_file() do try_to_unmap(TTU_BATCH_FLUSH)") Signed-off-by: Kyle Zeng Signed-off-by: Andrew Morton Acked-by: David Hildenbrand (Arm) Reviewed-by: Zi Yan Reviewed-by: Baolin Wang Acked-by: Hugh Dickins Assisted-by: LLM Cc: Barry Song Cc: Dev Jain Cc: Kiryl Shutsemau Cc: Lance Yang Cc: "Liam R. Howlett" Cc: Nico Pache Cc: Ryan Roberts Cc: Usama Arif Cc: Matthew Wilcox (Oracle) Cc: --- mm/khugepaged.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/mm/khugepaged.c b/mm/khugepaged.c index 4b71d010b2af..da91762f6358 100644 --- a/mm/khugepaged.c +++ b/mm/khugepaged.c @@ -2467,6 +2467,11 @@ static enum scan_result collapse_file(struct mm_struct *mm, unsigned long addr, index += folio_nr_pages(folio); continue; out_unlock: + /* + * The folio may have been unmapped with TTU_BATCH_FLUSH. + * Flush before releasing the lock and our last reference. + */ + try_to_unmap_flush(); folio_unlock(folio); folio_put(folio); goto xa_unlocked; @@ -2477,9 +2482,8 @@ static enum scan_result collapse_file(struct mm_struct *mm, unsigned long addr, xa_unlocked: /* - * If collapse is successful, flush must be done now before copying. - * If collapse is unsuccessful, does flush actually need to be done? - * Do it anyway, to clear the state. + * Flush before copying the folios, or releasing them in rollback. + * This is a no-op if out_unlock already flushed the batch. */ try_to_unmap_flush(); From f681d4580d5c3c749f623cda722a21aefd3147b3 Mon Sep 17 00:00:00 2001 From: "David Hildenbrand (Arm)" Date: Wed, 14 Oct 2026 20:34:28 +0200 Subject: [PATCH 02/43] mm: fix address wraparound when scanning large folio PTEs numa_rebuild_large_mapping() and do_swap_page() can be tricked into generating an address wraparound, crashing the kernel. All that's required is to map a tail page of a large folio close enough to the start/end of the address space to e.g., make unsigned long addr_start = addr - (nr << PAGE_SHIFT); to wrap around. Let's fix it by providing a better helper, folio_scan_range(), that takes care of staying within VMA and page table boundaries while handling possible wraparounds. There are some nasty details, like not being able to use pmd_addr_end() in this context. Add some helpful comments for these cases. Link: https://lore.kernel.org/20261014-folio_pte_scan_range-v2-1-00cf9bbaa364@kernel.org Fixes: d2136d749d76 ("mm: support multi-size THP numa balancing") Fixes: 508758960b8d ("mm: swap: entirely map large folios found in swapcache") Signed-off-by: David Hildenbrand (Arm) Signed-off-by: Andrew Morton Reported-by: Kyle Zeng Reviewed-by: Lorenzo Stoakes (ARM) Reviewed-by: Kiryl Shutsemau (Meta) Reviewed-by: Baolin Wang Reviewed-by: Lance Yang Reviewed-by: Barry Song Cc: Liam R. Howlett Cc: Vlastimil Babka Cc: Mike Rapoport Cc: Suren Baghdasaryan Cc: Michal Hocko Cc: Ying Huang Cc: Ryan Roberts Cc: Chuanhua Han Cc: --- mm/memory.c | 50 ++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/mm/memory.c b/mm/memory.c index 0fb676dd2bd6..591592bc5a94 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -4933,6 +4933,34 @@ static void check_swap_exclusive(struct folio *folio, swp_entry_t entry, } while (--nr_pages); } +/* + * Get the address range in which a folio might be mapped, assuming its pages + * are mapped consecutively and the folio page at the given idx is mapped at + * the given address. + */ +static void folio_pte_range(const struct folio *folio, + const struct vm_area_struct *vma, unsigned long addr, + unsigned long idx, unsigned long *start, unsigned long *end) +{ + /* Stay within the page table and VMA boundaries. */ + const unsigned long pmd_start = addr & PMD_MASK; + const unsigned long pmd_end = pmd_start + PMD_SIZE; + const unsigned long lower = max(pmd_start, vma->vm_start); + /* + * We cannot use pmd_addr_end(), as it does not clamp reliably. To + * avoid wraparounds, compute using the inclusive end. + */ + const unsigned long upper = min(pmd_end - 1, vma->vm_end - 1) + 1; + const unsigned long offset = idx << PAGE_SHIFT; + + /* + * Avoid start/end addresses wrapping by not adding/subtracting more + * than the lower/upper limits would allow. + */ + *start = addr - min(addr - lower, offset); + *end = addr + min(upper - addr, folio_size(folio) - offset); +} + /* * We enter with either the VMA lock or the mmap_lock held (see * FAULT_FLAG_VMA_LOCK), and pte mapped but not yet locked. @@ -5121,16 +5149,15 @@ vm_fault_t do_swap_page(struct vm_fault *vmf) address = vmf->address; ptep = vmf->pte; if (folio_test_large(folio) && folio_test_swapcache(folio)) { - int nr = folio_nr_pages(folio); - unsigned long idx = folio_page_idx(folio, page); - unsigned long folio_start = address - idx * PAGE_SIZE; - unsigned long folio_end = folio_start + nr * PAGE_SIZE; + const int nr = folio_nr_pages(folio); + const unsigned long idx = folio_page_idx(folio, page); + unsigned long folio_start, folio_end; pte_t *folio_ptep; pte_t folio_pte; - if (unlikely(folio_start < max(address & PMD_MASK, vma->vm_start))) - goto check_folio; - if (unlikely(folio_end > pmd_addr_end(address, vma->vm_end))) + folio_pte_range(folio, vma, address, idx, &folio_start, + &folio_end); + if (unlikely((folio_end - folio_start) >> PAGE_SHIFT != nr)) goto check_folio; folio_ptep = vmf->pte - idx; @@ -6280,16 +6307,11 @@ static void numa_rebuild_large_mapping(struct vm_fault *vmf, struct vm_area_stru struct folio *folio, pte_t fault_pte, bool ignore_writable, bool pte_write_upgrade) { - int nr = pte_pfn(fault_pte) - folio_pfn(folio); + const unsigned long idx = pte_pfn(fault_pte) - folio_pfn(folio); unsigned long start, end, addr = vmf->address; - unsigned long addr_start = addr - (nr << PAGE_SHIFT); - unsigned long pt_start = ALIGN_DOWN(addr, PMD_SIZE); pte_t *start_ptep; - /* Stay within the VMA and within the page table. */ - start = max3(addr_start, pt_start, vma->vm_start); - end = min3(addr_start + folio_size(folio), pt_start + PMD_SIZE, - vma->vm_end); + folio_pte_range(folio, vma, addr, idx, &start, &end); start_ptep = vmf->pte - ((addr - start) >> PAGE_SHIFT); /* Restore all PTEs' mapping of the large folio */ From 243cf1f67b365668389832463fc31cbb9fec47af Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Wed, 28 Oct 2026 08:52:57 +0800 Subject: [PATCH 03/43] mm: memory: fix truncation removing mapped folios Ayush reported an issue that shmem/tmpfs folios are still mapped when truncating them on production: " BUG: Bad page cache in process ... pfn:... page dumped because: still mapped when deleted " I reproduced the issue with Ayush's reproducer [1] with the latest mm-unstable branch, and I got the following crash info. From the dump message, we can see that truncate_inode_folio() is really trying to remove mapped folios, which is incorrect. " [ 189.171977] page: refcount:3 mapcount:1 mapping:00000000fe168ee1 index:0x3880 pfn:0x191647 [ 189.171995] memcg:ffff0000cc073d40 [ 189.171997] aops:shmem_aops ino:3c01 dentry name(?):"memfd:runsc-memory" [ 189.172006] flags: 0x17fffef0002022d(locked|referenced|uptodate|lru| workingset|swapbacked|node=0|zone=2|lastcpupid=0x3ffff) ....... [ 189.172017] page dumped because: VM_BUG_ON_FOLIO(folio_mapped(folio)) [ 189.172026] ------------[ cut here ]------------ [ 189.172027] kernel BUG at mm/filemap.c:155! [ 189.172057] Internal error: Oops - BUG: 00000000f2000800 [#1] SMP ....... [ 189.177721] CPU: 3 UID: 0 PID: 5521 Comm: repro Kdump: loaded Tainted: 7.3.0-rc4+ #277 PREEMPT(lazy) ....... [ 189.183986] Call trace: [ 189.184124] filemap_unaccount_folio+0xf0/0x1e8 (P) [ 189.184391] __filemap_remove_folio+0x34/0x160 [ 189.184633] filemap_remove_folio+0x4c/0xb0 [ 189.184859] truncate_inode_folio+0x34/0x58 [ 189.185087] shmem_undo_range+0x220/0x658 [ 189.185308] shmem_fallocate+0x2f0/0x470 [ 189.185527] vfs_fallocate+0x128/0x328 ...... [ 189.187078] el0t_64_sync+0x184/0x188 " After analysis, I believe the race exists between truncation and MADV_DONTNEED, and shmem's ->map_pages() merely makes the issue easier to reproduce. Since MADV_DONTNEED synchronously releases the pagetable page before calling tlb_flush_rmaps(), this could cause another thread's truncation to skip zap_pte_range() (due to pmd is none) but still observe the folio's mapcount as non-zero. A possible race scenario is as follows: CPU 0 CPU 1 shmem_fallocate madvise_dontneed_single_vma unmap_mapping_range ....... ...... (filemap_map_pages() remap folios) zap_pte_range truncate_inode_folio zap_empty_pte_table(pmd clear) unmap_mapping_folio ...... zap_pmd_range (saw pmd none, skip) filemap_remove_folio BUG_ON(folio_mapped) tlb_flush_rmaps(remove rmaps) To fix this issue, we should move the pmd clear operation (via zap_empty_pte_table()) to after tlb_flush_rmaps() and use sync_with_folio_pmd_zap() to sync with concurrent zapping, so that when we find a pmd_none() while unmapping a folio without holding the PTL, the folio's mappings are guaranteed to have been removed via tlb_flush_rmaps(). Meanwhile move the second pmd_none() check to where it belongs: if splitting zapped the PMD. Link: https://lore.kernel.org/14ebbd556cc4941f0ebc43da59fa6a2fdca25dc8.1793148609.git.baolin.wang@linux.alibaba.com Link: https://lore.kernel.org/all/20260925065013.3682431-1-ayushr@modal.com/ [1] Fixes: 6375e95f381e ("mm: pgtable: reclaim empty PTE page in madvise(MADV_DONTNEED)") Signed-off-by: Baolin Wang Signed-off-by: Andrew Morton Reported-by: Ayush Ranjan Closes: https://lore.kernel.org/all/20260924061708.1645968-1-ayushr@modal.com/ Acked-by: Qi Zheng Reviewed-by: Pedro Falcato Reviewed-by: Lorenzo Stoakes (ARM) Acked-by: David Hildenbrand (Arm) Cc: Liam R. Howlett Cc: Vlastimil Babka Cc: Mike Rapoport Cc: Suren Baghdasaryan Cc: Michal Hocko Cc: Jan Kara Cc: --- mm/internal.h | 10 ++++++---- mm/memory.c | 39 ++++++++++++++++++++++----------------- 2 files changed, 28 insertions(+), 21 deletions(-) diff --git a/mm/internal.h b/mm/internal.h index 566cbd2dce48..8b1263e81054 100644 --- a/mm/internal.h +++ b/mm/internal.h @@ -589,14 +589,16 @@ void free_pgtables(struct mmu_gather *tlb, struct unmap_desc *desc); void pmd_install(struct mm_struct *mm, pmd_t *pmd, pgtable_t *pte); /** - * sync_with_folio_pmd_zap - sync with concurrent zapping of a folio PMD + * sync_with_folio_pmd_zap - sync with concurrent zapping of folios * @mm: The mm_struct. * @pmdp: Pointer to the pmd that was found to be pmd_none(). * * When we find a pmd_none() while unmapping a folio without holding the PTL, - * zap_huge_pmd() may have cleared the PMD but not yet modified the folio to - * indicate that it's unmapped. Skipping the PMD without synchronization could - * make folio unmapping code assume that unmapping failed. + * zap_huge_pmd() may have cleared the PMD but not yet updated the folio + * mapcount, and zap_empty_pte_table() clears the PMD only after the rmaps + * for the table have been removed, but nothing orders the pmd_none() read + * against later mapcount reads. Skipping the PMD without synchronization + * could make folio unmapping code assume that unmapping failed. * * Wait for concurrent zapping to complete by grabbing the PTL. */ diff --git a/mm/memory.c b/mm/memory.c index 591592bc5a94..916637e8381d 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -2036,17 +2036,6 @@ static unsigned long zap_pte_range(struct mmu_gather *tlb, } } while (pte += nr, addr += PAGE_SIZE * nr, addr != end); - /* - * Fast path: try to hold the pmd lock and unmap the PTE page. - * - * If the pte lock was released midway (retry case), or if the attempt - * to hold the pmd lock failed, then we need to recheck all pte entries - * to ensure they are still none, thereby preventing the pte entries - * from being repopulated by another thread. - */ - if (can_reclaim_pt && direct_reclaim && addr == end) - direct_reclaim = zap_empty_pte_table(mm, pmd, ptl, &pmdval); - add_mm_rss_vec(mm, rss); lazy_mmu_mode_disable(); @@ -2055,6 +2044,20 @@ static unsigned long zap_pte_range(struct mmu_gather *tlb, tlb_flush_mmu_tlbonly(tlb); tlb_flush_rmaps(tlb, vma); } + + /* + * Fast path: try to hold the pmd lock and unmap the PTE page. + * + * If the pte lock was released midway (retry case), or if the attempt + * to hold the pmd lock failed, then we need to recheck all pte entries + * to ensure they are still none, thereby preventing the pte entries + * from being repopulated by another thread. + * + * Clear the PMD after the rmaps for the table have been removed, in case + * an incorrect mapcount is observed after unmapping the folio. + */ + if (can_reclaim_pt && direct_reclaim && addr == end) + direct_reclaim = zap_empty_pte_table(mm, pmd, ptl, &pmdval); pte_unmap_unlock(start_pte, ptl); /* @@ -2101,13 +2104,15 @@ static inline unsigned long zap_pmd_range(struct mmu_gather *tlb, addr = next; continue; } + /* We already synchronized with concurrent zapping. */ + if (pmd_none(*pmd)) { + addr = next; + continue; + } /* fall through */ - } else if (details && details->single_folio && - folio_test_pmd_mappable(details->single_folio) && - next - addr == HPAGE_PMD_SIZE && pmd_none(*pmd)) { - sync_with_folio_pmd_zap(tlb->mm, pmd); - } - if (pmd_none(*pmd)) { + } else if (pmd_none(*pmd)) { + if (details && details->single_folio) + sync_with_folio_pmd_zap(tlb->mm, pmd); addr = next; continue; } From 9597c2056e05bd79df9d343a9b42e84a54de1c52 Mon Sep 17 00:00:00 2001 From: Hungyu Lin Date: Wed, 21 Oct 2026 06:06:54 +0000 Subject: [PATCH 04/43] mm/page-writeback: avoid 32-bit overflow in dirty threshold shares The ratios used by __wb_calc_thresh() are scaled to parts per million, but the threshold and ratio operands are unsigned long. On 32-bit kernels, their multiplication can wrap before the result is assigned to u64. For example, with a threshold of 25600 pages and the default maximum ratio of 1000000, wb_max_thresh becomes 4125 pages instead of 25600. The shared threshold and minimum share calculations can overflow in the same way, producing incorrect per-writeback dirty limits. Use a u64 threshold for the three ratio multiplications and div_u64() to retain the existing division order and rounding. Link: https://lore.kernel.org/20261021060654.27855-1-dennylin0707@gmail.com Fixes: ae82291e9ca4 ("mm: use part per 1000000 for bdi ratios") Signed-off-by: Hungyu Lin Signed-off-by: Andrew Morton Reviewed-by: Jan Kara Reviewed-by: Gregory Price (Meta) Cc: Matthew Wilcox (Oracle) Cc: Stefan Roesch Cc: --- mm/page-writeback.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/mm/page-writeback.c b/mm/page-writeback.c index 499a35473e4f..0694ec4098f6 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -883,6 +883,7 @@ static unsigned long __wb_calc_thresh(struct dirty_throttle_control *dtc, { struct wb_domain *dom = dtc_dom(dtc); struct bdi_writeback *wb = dtc->wb; + u64 threshold = thresh; u64 wb_thresh; u64 wb_max_thresh; unsigned long numerator, denominator; @@ -894,13 +895,14 @@ static unsigned long __wb_calc_thresh(struct dirty_throttle_control *dtc, fprop_fraction_percpu(&dom->completions, dtc->wb_completions, &numerator, &denominator); - wb_thresh = (thresh * (100 * BDI_RATIO_SCALE - bdi_min_ratio)) / (100 * BDI_RATIO_SCALE); + wb_thresh = div_u64(threshold * (100 * BDI_RATIO_SCALE - bdi_min_ratio), + 100 * BDI_RATIO_SCALE); wb_thresh *= numerator; wb_thresh = div64_ul(wb_thresh, denominator); wb_min_max_ratio(wb, &wb_min_ratio, &wb_max_ratio); - wb_thresh += (thresh * wb_min_ratio) / (100 * BDI_RATIO_SCALE); + wb_thresh += div_u64(threshold * wb_min_ratio, 100 * BDI_RATIO_SCALE); /* * It's very possible that wb_thresh is close to 0 not because the @@ -916,7 +918,7 @@ static unsigned long __wb_calc_thresh(struct dirty_throttle_control *dtc, wb_thresh = max(wb_thresh, (thresh - dtc->dirty) / 8); } - wb_max_thresh = thresh * wb_max_ratio / (100 * BDI_RATIO_SCALE); + wb_max_thresh = div_u64(threshold * wb_max_ratio, 100 * BDI_RATIO_SCALE); if (wb_thresh > wb_max_thresh) wb_thresh = wb_max_thresh; From 332c1bd17422c1aa59e18513042e56ea5aac4051 Mon Sep 17 00:00:00 2001 From: Danish Khateeb Date: Thu, 22 Oct 2026 07:18:37 -0500 Subject: [PATCH 05/43] selftests/mm: merge: keep the far mremap() targets in the carveout The merge tests map their areas at fixed addresses within a 30-page PROT_NONE carveout. Several mremap() tests move an area 1000 or 2000 pages away to separate it from the others, but those targets are outside the carveout, and MREMAP_FIXED unmaps whatever is mapped there. Under virtme-ng, the shared libraries are packed right above the carveout and the dynamic linker starts about 4 MiB above it. The moves then replace part of its text with an anonymous mapping, and the test crashes in its next lazily bound call: merge[6980]: segfault at 7f7ebb3c1e60 ip 00007f7ebb3c1e60 sp 00007ffd35fbaec8 error 14 Two to four of the mremap() tests then fail with "Test terminated unexpectedly by signal 11", depending on the exact layout. On a host where libc is mapped at a 2 MiB aligned address, the targets happen to land above the dynamic linker and the tests pass. Since the areas are all within the first 30 pages, moving one by 30 pages is enough to keep it apart from the others. Add FAR_PAGES for that distance, move areas by FAR_PAGES or 2 * FAR_PAGES pages, and make the carveout 3 * FAR_PAGES pages so that it covers every target. Link: https://lore.kernel.org/20261022121837.28723-1-danishkhateeb03@gmail.com Fixes: 4e25f85b9f85 ("tools/testing/selftests: add mremap() unfaulted/faulted test cases") Signed-off-by: Danish Khateeb Signed-off-by: Andrew Morton Reviewed-by: Lorenzo Stoakes (ARM) Reviewed-by: Gregory Price (Meta) Cc: Liam R. Howlett Cc: Vlastimil Babka Cc: Jann Horn Cc: Pedro Falcato Cc: David Hildenbrand Cc: Mike Rapoport Cc: Suren Baghdasaryan Cc: Michal Hocko Cc: Shuah Khan --- tools/testing/selftests/mm/merge.c | 33 ++++++++++++++++-------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/tools/testing/selftests/mm/merge.c b/tools/testing/selftests/mm/merge.c index 7dd2933417c1..6def490186a9 100644 --- a/tools/testing/selftests/mm/merge.c +++ b/tools/testing/selftests/mm/merge.c @@ -22,9 +22,12 @@ FIXTURE(merge) struct procmap_fd procmap; }; +#define FAR_PAGES 30 +#define CARVEOUT_PAGES (3 * FAR_PAGES) + static char *map_carveout(unsigned int page_size) { - return mmap(NULL, 30 * page_size, PROT_NONE, + return mmap(NULL, CARVEOUT_PAGES * page_size, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0); } @@ -73,7 +76,7 @@ FIXTURE_SETUP(merge) FIXTURE_TEARDOWN(merge) { - ASSERT_EQ(munmap(self->carveout, 30 * self->page_size), 0); + ASSERT_EQ(munmap(self->carveout, CARVEOUT_PAGES * self->page_size), 0); /* May fail for parent of forked process. */ close_procmap(&self->procmap); /* @@ -115,7 +118,7 @@ FIXTURE_SETUP(merge_with_fork) FIXTURE_TEARDOWN(merge_with_fork) { - ASSERT_EQ(munmap(self->carveout, 30 * self->page_size), 0); + ASSERT_EQ(munmap(self->carveout, CARVEOUT_PAGES * self->page_size), 0); ASSERT_EQ(close_procmap(&self->procmap), 0); /* See above. */ prctl(PR_SET_MEMORY_MERGE, 0, 0, 0, 0); @@ -660,7 +663,7 @@ TEST_F(merge, mremap_unfaulted_to_faulted) /* Offset ptr2 further away. */ ptr2 = sys_mremap(ptr2, 5 * page_size, 5 * page_size, - MREMAP_MAYMOVE | MREMAP_FIXED, ptr2 + page_size * 1000); + MREMAP_MAYMOVE | MREMAP_FIXED, ptr2 + FAR_PAGES * page_size); ASSERT_NE(ptr2, MAP_FAILED); /* @@ -721,7 +724,7 @@ TEST_F(merge, mremap_unfaulted_behind_faulted) /* Offset ptr2 further away. */ ptr2 = sys_mremap(ptr2, 5 * page_size, 5 * page_size, - MREMAP_MAYMOVE | MREMAP_FIXED, ptr2 + page_size * 1000); + MREMAP_MAYMOVE | MREMAP_FIXED, ptr2 + FAR_PAGES * page_size); ASSERT_NE(ptr2, MAP_FAILED); /* @@ -785,12 +788,12 @@ TEST_F(merge, mremap_unfaulted_between_faulted) /* Offset ptr3 further away. */ ptr3 = sys_mremap(ptr3, 5 * page_size, 5 * page_size, - MREMAP_MAYMOVE | MREMAP_FIXED, ptr3 + page_size * 2000); + MREMAP_MAYMOVE | MREMAP_FIXED, ptr3 + 2 * FAR_PAGES * page_size); ASSERT_NE(ptr3, MAP_FAILED); /* Offset ptr2 further away. */ ptr2 = sys_mremap(ptr2, 5 * page_size, 5 * page_size, - MREMAP_MAYMOVE | MREMAP_FIXED, ptr2 + page_size * 1000); + MREMAP_MAYMOVE | MREMAP_FIXED, ptr2 + FAR_PAGES * page_size); ASSERT_NE(ptr2, MAP_FAILED); /* @@ -870,13 +873,13 @@ TEST_F(merge, mremap_unfaulted_between_faulted_unfaulted) /* Offset ptr3 further away. */ ptr3 = sys_mremap(ptr3, 5 * page_size, 5 * page_size, - MREMAP_MAYMOVE | MREMAP_FIXED, ptr3 + page_size * 2000); + MREMAP_MAYMOVE | MREMAP_FIXED, ptr3 + 2 * FAR_PAGES * page_size); ASSERT_NE(ptr3, MAP_FAILED); /* Offset ptr2 further away. */ ptr2 = sys_mremap(ptr2, 5 * page_size, 5 * page_size, - MREMAP_MAYMOVE | MREMAP_FIXED, ptr2 + page_size * 1000); + MREMAP_MAYMOVE | MREMAP_FIXED, ptr2 + FAR_PAGES * page_size); ASSERT_NE(ptr2, MAP_FAILED); /* @@ -1035,11 +1038,11 @@ TEST_F(merge, mremap_correct_placed_faulted) */ ptr3 = &ptr[10 * page_size]; ptr3 = sys_mremap(ptr3, 5 * page_size, 5 * page_size, - MREMAP_MAYMOVE | MREMAP_FIXED, ptr3 + page_size * 2000); + MREMAP_MAYMOVE | MREMAP_FIXED, ptr3 + 2 * FAR_PAGES * page_size); ASSERT_NE(ptr3, MAP_FAILED); ptr2 = &ptr[5 * page_size]; ptr2 = sys_mremap(ptr2, 5 * page_size, 5 * page_size, - MREMAP_MAYMOVE | MREMAP_FIXED, ptr2 + page_size * 1000); + MREMAP_MAYMOVE | MREMAP_FIXED, ptr2 + FAR_PAGES * page_size); ASSERT_NE(ptr2, MAP_FAILED); /* @@ -1075,7 +1078,7 @@ TEST_F(merge, mremap_correct_placed_faulted) * ptr2 \ ptr \ ptr3 */ ptr = sys_mremap(ptr, 5 * page_size, 5 * page_size, - MREMAP_MAYMOVE | MREMAP_FIXED, ptr + page_size * 1000); + MREMAP_MAYMOVE | MREMAP_FIXED, ptr + FAR_PAGES * page_size); ASSERT_NE(ptr, MAP_FAILED); /* @@ -1110,7 +1113,7 @@ TEST_F(merge, mremap_correct_placed_faulted) * ptr2 \ ptr \ ptr3 */ ptr = sys_mremap(ptr, 5 * page_size, 5 * page_size, - MREMAP_MAYMOVE | MREMAP_FIXED, ptr + page_size * 1000); + MREMAP_MAYMOVE | MREMAP_FIXED, ptr + FAR_PAGES * page_size); ASSERT_NE(ptr, MAP_FAILED); /* @@ -1168,7 +1171,7 @@ TEST_F(merge, mremap_correct_placed_faulted) * ptr ptr3 \ ptr2 */ ptr2 = sys_mremap(ptr2, 5 * page_size, 5 * page_size, - MREMAP_MAYMOVE | MREMAP_FIXED, ptr2 + page_size * 1000); + MREMAP_MAYMOVE | MREMAP_FIXED, ptr2 + FAR_PAGES * page_size); ASSERT_NE(ptr2, MAP_FAILED); /* @@ -1203,7 +1206,7 @@ TEST_F(merge, mremap_correct_placed_faulted) * ptr \ ptr3 */ ptr3 = sys_mremap(ptr3, 5 * page_size, 5 * page_size, - MREMAP_MAYMOVE | MREMAP_FIXED, ptr3 + page_size * 1000); + MREMAP_MAYMOVE | MREMAP_FIXED, ptr3 + FAR_PAGES * page_size); ASSERT_NE(ptr3, MAP_FAILED); /* From 83367658a01087375ede31ae343c99da03f72ac8 Mon Sep 17 00:00:00 2001 From: "Gregory Price (Meta)" Date: Mon, 19 Oct 2026 14:48:50 -0400 Subject: [PATCH 06/43] mm/page_alloc: let the highest memory node recover from hopeless state free_frozen_page_commit() clears a hopeless node's kswapd failures once its zone recovers above the high watermark, but only if another memory node exists for allocations to fall back to. The test `next_memory_node(nid) < MAX_NUMNODES` finds only memory nodes with a higher id. The highest-numbered memory node therefore never recovers on this path, and its kswapd stays disabled until reclaim on that node makes progress and balances it, or demotion is re-enabled. In many cases, direct reclaim many never fully run against a lower node (CXL/kmem), and that means its kswapd can end up full disabled in rare situations. To reproduce, fill the highest memory node with bound, non-THP anonymous memory and no swap until kswapd gives up, then free it: /proc/zoneinfo keeps showing node_unreclaimable 1 for that node. A lower node recovers. Count the memory nodes instead. Adding first_memory_node != nid to the existing test would also work, but a count says directly what the check means: some other memory node exists. Link: https://lore.kernel.org/20261019184850.3427905-1-gourry@gourry.net Fixes: e7a5f249e6db ("mm: re-enable kswapd when memory pressure subsides or demotion is toggled") Signed-off-by: Gregory Price (Meta) Signed-off-by: Andrew Morton Acked-by: Zi Yan Acked-by: Vlastimil Babka (SUSE) Cc: Suren Baghdasaryan Cc: Michal Hocko Cc: Brendan Jackman Cc: Johannes Weiner Cc: Chanwon Park Cc: --- mm/page_alloc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index a8cac5e01066..31ba24411d36 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -2929,7 +2929,7 @@ static bool free_frozen_page_commit(struct zone *zone, * kswapd work again by resetting kswapd_failures. */ if (kswapd_test_hopeless(pgdat) && - next_memory_node(pgdat->node_id) < MAX_NUMNODES) + num_node_state(N_MEMORY) > 1) kswapd_clear_hopeless(pgdat, KSWAPD_CLEAR_HOPELESS_PCP); } return ret; From dfb190302d2b4af86c6ae48af0d3b09f28b35460 Mon Sep 17 00:00:00 2001 From: Li Zhe Date: Thu, 22 Oct 2026 11:45:41 +0800 Subject: [PATCH 07/43] mm/hugetlb: fix mremap move length truncation Patch series "mm/hugetlb: fix mremap bugs", v3. This series fixes a few hugetlb mremap bugs. Patch 1 fixes the return type of move_hugetlb_page_tables(), so large hugetlb moves cannot be truncated when reported back to the generic mremap code. Patch 2 disables hugetlb PMD sharing for UFFD missing mode, so UFFDIO_POISON markers cannot become visible through shared PMD page tables. Patch 3 fixes the recursive i_mmap_rwsem deadlock in the hugetlb mremap path by avoiding the destination PMD sharing allocation path while the mapping lock is already held for write. This patch (of 3): move_hugetlb_page_tables() returns the number of bytes moved, but its return type is int while the generic move_page_tables() path uses unsigned long lengths. A hugetlb mremap() move length above INT_MAX can therefore be misreported to copy_vma_and_data(). The out-of-range conversion to int is implementation-defined; on common 64-bit builds, for example, a 4 GiB move can be reported as zero, making the caller treat the move as a failure and use the wrong length for rollback. Return unsigned long from move_hugetlb_page_tables(), matching move_page_tables() and preserving the full byte count. Link: https://lore.kernel.org/20261022034543.51226-1-lizhe.67@bytedance.com Link: https://lore.kernel.org/20261022034543.51226-2-lizhe.67@bytedance.com Fixes: 550a7d60bd5e ("mm, hugepages: add mremap() support for hugepage backed vma") Signed-off-by: Li Zhe Signed-off-by: Andrew Morton Acked-by: David Hildenbrand (Arm) Cc: Muchun Song Cc: Oscar Salvador Cc: Peter Xu Cc: Mike Rapoport Cc: Jose A. Perez de Azpillaga Cc: --- include/linux/hugetlb.h | 15 ++++++--------- mm/hugetlb.c | 7 +++---- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h index 5029c7241863..58f755e70e96 100644 --- a/include/linux/hugetlb.h +++ b/include/linux/hugetlb.h @@ -121,10 +121,9 @@ void hugepage_put_subpool(struct hugepage_subpool *spool); void hugetlb_dup_vma_private(struct vm_area_struct *vma); void clear_vma_resv_huge_pages(struct vm_area_struct *vma); -int move_hugetlb_page_tables(struct vm_area_struct *vma, - struct vm_area_struct *new_vma, - unsigned long old_addr, unsigned long new_addr, - unsigned long len); +unsigned long move_hugetlb_page_tables(struct vm_area_struct *vma, + struct vm_area_struct *new_vma, unsigned long old_addr, + unsigned long new_addr, unsigned long len); int copy_hugetlb_page_range(struct mm_struct *, struct mm_struct *, struct vm_area_struct *, struct vm_area_struct *); void unmap_hugepage_range(struct vm_area_struct *, @@ -338,11 +337,9 @@ static inline int copy_hugetlb_page_range(struct mm_struct *dst, return 0; } -static inline int move_hugetlb_page_tables(struct vm_area_struct *vma, - struct vm_area_struct *new_vma, - unsigned long old_addr, - unsigned long new_addr, - unsigned long len) +static inline unsigned long move_hugetlb_page_tables(struct vm_area_struct *vma, + struct vm_area_struct *new_vma, unsigned long old_addr, + unsigned long new_addr, unsigned long len) { BUG(); return 0; diff --git a/mm/hugetlb.c b/mm/hugetlb.c index da5d04edf7c1..506729ebaee7 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -5195,10 +5195,9 @@ static void move_huge_pte(struct vm_area_struct *vma, unsigned long old_addr, spin_unlock(dst_ptl); } -int move_hugetlb_page_tables(struct vm_area_struct *vma, - struct vm_area_struct *new_vma, - unsigned long old_addr, unsigned long new_addr, - unsigned long len) +unsigned long move_hugetlb_page_tables(struct vm_area_struct *vma, + struct vm_area_struct *new_vma, unsigned long old_addr, + unsigned long new_addr, unsigned long len) { struct hstate *h = hstate_vma(vma); struct address_space *mapping = vma->vm_file->f_mapping; From 809cbe72ace890f75a9b126eb6a8b533081666d8 Mon Sep 17 00:00:00 2001 From: "David Hildenbrand (Arm)" Date: Thu, 22 Oct 2026 11:45:42 +0800 Subject: [PATCH 08/43] mm/hugetlb: disable PMD sharing for UFFD missing mode For UFFDIO_POISON to install a poison marker in a hugetlb mapping, it is sufficient to have the VMA registered for missing mode. However, UFFD missing mode does currently not block PMD table sharing, so installing the marker will be visible to everyone using the shared PMD table, which is wrong as UFFDIO_POISON must stay local to the process. To fix it, make uffd_disable_huge_pmd_share() cover VM_UFFD_MISSING as well. With missing mode covered, hugetlb PMD sharing is disabled for all userfaultfd registered VMAs, so the helper can simply return whether the VMA has a userfaultfd context instead of testing individual UFFD mode flags. Add a debug check on the marker installation path. The existing registration path will then unshare PMD tables, and subsequent faults will not share them while the mapping remains registered. The alternative would be to unshare PMD tables selectively when installing poison markers, which results in way more complexity. Or alternatively, simply failing the marker installation, which might break some use cases. This also disables PMD sharing for userfaultfd registrations that never use UFFDIO_POISON. Link: https://lore.kernel.org/20261022034543.51226-3-lizhe.67@bytedance.com Fixes: 8a13897fb0da ("mm: userfaultfd: support UFFDIO_POISON for hugetlbfs") Signed-off-by: David Hildenbrand (Arm) Signed-off-by: Li Zhe Signed-off-by: Andrew Morton Suggested-by: Li Zhe Suggested-by: Mike Rapoport Acked-by: Mike Rapoport (Microsoft) Cc: Muchun Song Cc: Oscar Salvador Cc: Peter Xu Cc: Jose A. Perez de Azpillaga --- include/linux/userfaultfd_k.h | 16 +++------------- mm/hugetlb.c | 1 + 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/include/linux/userfaultfd_k.h b/include/linux/userfaultfd_k.h index a14b8a9ffb7b..ebd86677b052 100644 --- a/include/linux/userfaultfd_k.h +++ b/include/linux/userfaultfd_k.h @@ -168,22 +168,12 @@ static inline bool is_mergeable_vm_userfaultfd_ctx(struct vm_area_struct *vma, } /* - * Never enable huge pmd sharing on some uffd registered vmas: - * - * - VM_UFFD_WP and VM_UFFD_RWP VMAs, because the write protect / access - * tracking information is per pgtable entry. - * - * - VM_UFFD_MINOR VMAs, because otherwise we would never get minor faults for - * VMAs which share huge pmds. (If you have two mappings to the same - * underlying pages, and fault in the non-UFFD-registered one with a write, - * with huge pmd sharing this would *also* setup the second UFFD-registered - * mapping, and we'd not get minor faults.) + * Never enable huge pmd sharing on userfaultfd registered VMAs, because + * userfaultfd state can be tracked per VMA or per page table entry. */ static inline bool uffd_disable_huge_pmd_share(struct vm_area_struct *vma) { - return vma_test_any_mask(vma, - mk_vma_flags_from_masks(VMA_UFFD_WP, VMA_UFFD_RWP, - VMA_UFFD_MINOR)); + return vma->vm_userfaultfd_ctx.ctx; } /* diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 506729ebaee7..8995f4cc4e65 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -6388,6 +6388,7 @@ int hugetlb_mfill_atomic_pte(pte_t *dst_pte, return -EEXIST; } + VM_WARN_ON_ONCE(size == PMD_SIZE && hugetlb_pmd_shared(dst_pte)); _dst_pte = make_pte_marker(PTE_MARKER_POISONED); set_huge_pte_at(dst_mm, dst_addr, dst_pte, _dst_pte, size); From 92daa8652f88c8cfc05f4899a8408a04b02f211e Mon Sep 17 00:00:00 2001 From: Li Zhe Date: Thu, 22 Oct 2026 11:45:43 +0800 Subject: [PATCH 09/43] mm/hugetlb: fix mremap deadlock when PMD sharing is possible move_hugetlb_page_tables() holds the mapping's i_mmap_rwsem for write while moving hugetlb PTEs. This prevents races with truncation and protects huge_pmd_unshare(), but the move loop must not call helpers that can acquire the same rwsem for read. huge_pte_alloc() can do that when the destination can use PMD sharing: it may call huge_pmd_share(), which takes i_mmap_rwsem for read while searching for a shareable PMD table. Calling it while the move path already holds i_mmap_rwsem for write can deadlock. Avoid that path for PMD-sized hugetlb mappings that can use PMD sharing. Instead of allocating or sharing the destination page table while moving, zap the source range and let the destination fault path populate or share the PMD lazily. Reuse the hugetlb unmap logic for the source-side zap by splitting the core loop out of __unmap_hugepage_range(). The wrapper keeps the existing tlb_start_vma(), tlb_end_vma() and huge_pmd_unshare_flush() behavior, while move_hugetlb_page_tables() can reuse the unmap loop with its existing mmu_gather. Link: https://lore.kernel.org/20261022034543.51226-4-lizhe.67@bytedance.com Fixes: 550a7d60bd5e ("mm, hugepages: add mremap() support for hugepage backed vma") Signed-off-by: Li Zhe Co-developed-by: David Hildenbrand (Arm) Signed-off-by: David Hildenbrand (Arm) Signed-off-by: Andrew Morton Cc: Muchun Song Cc: Oscar Salvador Cc: Peter Xu Cc: Mike Rapoport Cc: Jose A. Perez de Azpillaga Cc: --- mm/hugetlb.c | 47 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 8995f4cc4e65..ec05e8c1d1f8 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -137,6 +137,10 @@ static int __huge_pmd_unshare(struct mmu_gather *tlb, bool check_locks); static void hugetlb_unshare_pmds(struct vm_area_struct *vma, unsigned long start, unsigned long end, bool take_locks); +static void hugetlb_unmap_range(struct mmu_gather *tlb, + struct vm_area_struct *vma, unsigned long start, + unsigned long end, struct folio *folio, + zap_flags_t zap_flags); static struct resv_map *vma_resv_map(struct vm_area_struct *vma); static inline bool subpool_is_free(struct hugepage_subpool *spool) @@ -5227,6 +5231,8 @@ unsigned long move_hugetlb_page_tables(struct vm_area_struct *vma, for (; old_addr < old_end; old_addr += sz, new_addr += sz) { const unsigned long offset_to_last_entry = (old_addr | last_addr_mask) - old_addr; + const unsigned long offset_to_last_dst_entry = + (new_addr | last_addr_mask) - new_addr; src_pte = hugetlb_walk(vma, old_addr, sz); if (!src_pte) { @@ -5243,6 +5249,24 @@ unsigned long move_hugetlb_page_tables(struct vm_area_struct *vma, continue; } + /* + * Avoid entering the destination PMD sharing path while holding + * i_mmap_rwsem for write. Leave destination page table + * population to the fault path, which can share the PMD lazily. + */ + if (sz == PMD_SIZE && want_pmd_share(new_vma, new_addr)) { + const unsigned long unmap_size = + min(old_end - old_addr, + offset_to_last_dst_entry + sz); + + hugetlb_unmap_range(&tlb, vma, old_addr, + old_addr + unmap_size, NULL, + ZAP_FLAG_DROP_MARKER); + old_addr += unmap_size - sz; + new_addr += unmap_size - sz; + continue; + } + dst_pte = huge_pte_alloc(mm, new_vma, new_addr, sz); if (!dst_pte) break; @@ -5262,9 +5286,10 @@ unsigned long move_hugetlb_page_tables(struct vm_area_struct *vma, return len + old_addr - old_end; } -void __unmap_hugepage_range(struct mmu_gather *tlb, struct vm_area_struct *vma, - unsigned long start, unsigned long end, - struct folio *folio, zap_flags_t zap_flags) +static void hugetlb_unmap_range(struct mmu_gather *tlb, + struct vm_area_struct *vma, unsigned long start, + unsigned long end, struct folio *folio, + zap_flags_t zap_flags) { struct mm_struct *mm = vma->vm_mm; const bool folio_provided = !!folio; @@ -5282,13 +5307,6 @@ void __unmap_hugepage_range(struct mmu_gather *tlb, struct vm_area_struct *vma, BUG_ON(start & ~huge_page_mask(h)); BUG_ON(end & ~huge_page_mask(h)); - /* - * This is a hugetlb vma, all the pte entries should point - * to huge page. - */ - tlb_change_page_size(tlb, sz); - tlb_start_vma(tlb, vma); - last_addr_mask = hugetlb_mask_last_page(h); address = start; for (; address < end; address += sz) { @@ -5417,6 +5435,15 @@ void __unmap_hugepage_range(struct mmu_gather *tlb, struct vm_area_struct *vma, if (folio_provided) break; } +} + +void __unmap_hugepage_range(struct mmu_gather *tlb, struct vm_area_struct *vma, + unsigned long start, unsigned long end, + struct folio *folio, zap_flags_t zap_flags) +{ + tlb_change_page_size(tlb, huge_page_size(hstate_vma(vma))); + tlb_start_vma(tlb, vma); + hugetlb_unmap_range(tlb, vma, start, end, folio, zap_flags); tlb_end_vma(tlb, vma); huge_pmd_unshare_flush(tlb, vma); From b6732c6d43139ea56de8bc62e87ace8543ef784f Mon Sep 17 00:00:00 2001 From: "Zenghui Yu (Huawei)" Date: Thu, 22 Oct 2026 20:27:52 +0800 Subject: [PATCH 10/43] mm/page_alloc: mark pages freed by __free_pages_core() as not tagged Onlining hotplugged memory (e.g., a dev_dax device bound to kmem) with CONFIG_MEM_ALLOC_PROFILING_DEBUG triggers: alloc_tag was not set WARNING: ./include/linux/alloc_tag.h:166 at __pgalloc_tag_sub+0x1fc/0x254 Call trace: __pgalloc_tag_sub+0x1fc/0x254 (P) __free_pages_ok+0x14c/0x6ac __free_pages_core+0xa8/0x188 generic_online_page+0x14/0x20 online_pages+0x220/0x3b0 memory_subsys_online+0xfc/0x198 device_online+0x58/0xe0 state_store+0xf0/0xf8 Pages entering the buddy through __free_pages_core() have never been allocated, so their allocation tag references are NULL. The free path, __free_pages_prepare() -> pgalloc_tag_sub(), expects a valid tag or a CODETAG_EMPTY mark and warns otherwise. Paths feeding never-allocated pages into the buddy are therefore expected to mark them with clear_page_tag_ref() first, like init_cma_reserved_pageblock() and free_reserved_pages() do; of __free_pages_core()'s callers only memblock_free_pages() clears the tag ref itself, and the hotplug onlining path was missed. Since all __free_pages_core() callers free never-allocated pages, mark the head page as not tagged in __free_pages_core() itself instead of relying on each caller, and drop the now-redundant clearing from memblock_free_pages(). Link: https://lore.kernel.org/20261022122752.74774-1-zenghui.yu@linux.dev Fixes: dcfe378c81f7 ("lib: introduce support for page allocation tagging") Signed-off-by: Zenghui Yu (Huawei) Signed-off-by: Andrew Morton Reviewed-by: Johannes Weiner Cc: Mike Rapoport Cc: Vlastimil Babka Cc: Suren Baghdasaryan Cc: Michal Hocko Cc: Brendan Jackman Cc: Zi Yan Cc: --- mm/mm_init.c | 2 -- mm/page_alloc.c | 3 +++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/mm/mm_init.c b/mm/mm_init.c index 2a0e782a151d..d99af80ec752 100644 --- a/mm/mm_init.c +++ b/mm/mm_init.c @@ -2478,8 +2478,6 @@ void __init memblock_free_pages(unsigned long pfn, unsigned int order) return; } - /* pages were reserved and not allocated */ - clear_page_tag_ref(page); __free_pages_core(page, order, MEMINIT_EARLY); } diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 31ba24411d36..2b2855300217 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -1616,6 +1616,9 @@ void __meminit __free_pages_core(struct page *page, unsigned int order, atomic_long_add(nr_pages, &page_zone(page)->managed_pages); } + /* pages were never allocated */ + clear_page_tag_ref(page); + if (page_contains_unaccepted(page, order)) { if (order == MAX_PAGE_ORDER && __free_unaccepted(page)) return; From 8146c6478224d8f6395c6b08a19c344a56d79867 Mon Sep 17 00:00:00 2001 From: "Lorenzo Stoakes (ARM)" Date: Fri, 23 Oct 2026 16:17:31 +0100 Subject: [PATCH 11/43] mm/vma: fix expansion of VMA_MIXEDMAP_BIT VMAs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit ca3d76b0aa80 ("mm: add merging after mremap resize") combined merging with expansion of mremap()'d VMAs, however it also unintentionally disallowed the expansion of unmergeable VMAs. Previously it was possible to expand a mixed mapping which mremap() had already ensured was a legal expansion. This is clearly seen in the mremap() logic, as it checks vma_is_fixed_mapping() to determine legality of an expansion rather than vma_can_merge(). Additionally, copy_vma() allows for a failed merge to result in a new VMA being instantiated if MREMAP_MAYMOVE is specified. However, this is not attempted, as a failure in vma_merge_extend() is assumed to be an -ENOMEM failure. Since vma_merge_extend() is used exclusively by mremap() to perform a merge and/or expansion, resolve the issue by invoking vma_expand() for unmergeable VMAs there. Explicitly assert that the VMA is both non-fixed and a mixed mapping. This provides double insurance against broken changes in future. Note that khugepaged_enter_vma() (which vma_merge_new_range() runs) is redundant in this case, as a mixed mapping is not subject to khugepaged. Also update the VMA userland tests to add a required predicate. Link: https://lore.kernel.org/20261023-vma-expansion-fix-v1-1-6142baf1f46b@kernel.org Fixes: ca3d76b0aa80 ("mm: add merging after mremap resize") Signed-off-by: Lorenzo Stoakes (ARM) Signed-off-by: Andrew Morton Reported-by: Luiz Capitulino Closes: https://lore.kernel.org/all/20261023023106.515365-1-luizcap@redhat.com/ Acked-by: Vlastimil Babka (SUSE) Reviewed-by: Pedro Falcato Cc: Liam R. Howlett Cc: Jann Horn Cc: Jakub Matěna Cc: --- mm/vma.c | 19 ++++++++++++++++--- tools/testing/vma/include/dup.h | 5 +++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/mm/vma.c b/mm/vma.c index 1be613044b4a..88ef92ab24ec 100644 --- a/mm/vma.c +++ b/mm/vma.c @@ -1851,12 +1851,25 @@ struct vm_area_struct *vma_merge_extend(struct vma_iterator *vmi, struct vm_area_struct *vma, unsigned long delta) { - VMG_VMA_STATE(vmg, vmi, vma, vma, vma->vm_end, vma->vm_end + delta); + const unsigned long start = + vma_can_merge(vma) ? vma->vm_end : vma->vm_start; + VMG_VMA_STATE(vmg, vmi, vma, vma, start, vma->vm_end + delta); - vmg.next = vma_iter_next_rewind(vmi, NULL); vmg.middle = NULL; /* We use the VMA to populate VMG fields only. */ - return vma_merge_new_range(&vmg); + if (vma_can_merge(vma)) { + vmg.next = vma_iter_next_rewind(vmi, NULL); + return vma_merge_new_range(&vmg); + } + + /* Mixed mappings are permitted to be expanded, not merged. */ + VM_WARN_ON_ONCE(vma_is_fixed_mapping(vma)); + VM_WARN_ON_ONCE(!vma_test(vma, VMA_MIXEDMAP_BIT)); + vma_prev(vmi); /* Reposition to start of VMA. */ + vmg.target = vma; + if (vma_expand(&vmg)) + return NULL; + return vma; } void unlink_file_vma_batch_init(struct unlink_vma_file_batch *vb) diff --git a/tools/testing/vma/include/dup.h b/tools/testing/vma/include/dup.h index f449129ea97a..e0eab4eca998 100644 --- a/tools/testing/vma/include/dup.h +++ b/tools/testing/vma/include/dup.h @@ -1727,3 +1727,8 @@ static inline bool vma_flags_can_merge(const vma_flags_t *flags) */ return !vma_flags_is_hugetlb(flags); } + +static inline bool vma_can_merge(const struct vm_area_struct *vma) +{ + return vma_flags_can_merge(&vma->flags); +} From 13fc99f46399c9d38d1b54ecf0a3f387ba04d05d Mon Sep 17 00:00:00 2001 From: "Lorenzo Stoakes (ARM)" Date: Fri, 23 Oct 2026 14:47:27 +0100 Subject: [PATCH 12/43] mm: do not unintentionally overwrite atomic VMA flags Patch series "mm: avoid losing atomic VMA flags", v1. Since commit 2ab7f1bbafc9 ("mm/madvise: allow guard page install/remove under VMA lock"), VMA_MAYBE_GUARD_BIT can be set atomically under the VMA read lock. However, a number of places wrongly assume the flags are stable under the read lock, which can cause stale atomic VMA flags to overwrite existing valid ones. Additionally, one case in which VMA_SOFTDIRTY_BIT was overwritten is also addressed. This series addresses each of these, while keeping the logic inline with existing conventions. Each patch in this series is a hotfix. This patch (of 4): madvise, mlock and mprotect take a copy of a VMA's flags holding only the VMA read lock, later writing it back under the write lock (in madvise_update_vma(), mlock_fixup(), mlock_vma_pages_range() and mprotect_fixup()). This was done on the assumption that VMA flags cannot change with a read lock held. However this changed in commit 2ab7f1bbafc9 ("mm/madvise: allow guard page install/remove under VMA lock"), which allowed VM_ATOMIC_SET_ALLOWED flags (currently only VMA_MAYBE_GUARD_BIT) to be written concurrently under a VMA read lock. The actual write itself is safe as this is done under the VMA write lock at which point no further concurrent writes can occur, however at this point the stale flags mean that these callers can unintentionally overwrite atomic flags incorrectly. The consequences of this are significant - a missing VMA_MAYBE_GUARD_BIT could result in page tables not being copied on fork (the vma_needs_copy() check can return false) or being overwritten on file-backed THP collapse (the file_backed_vma_is_retractable() can return true). Resolve the issue by always taking care to retain existing atomic flags when overwriting VMA flags, replacing the inconsistently named vma_flags_reset_once() with vma_overwrite_flags() to do so, and always invoking it when flags are overwritten like this. Reads are a problem too - madvise, mlock and mprotect all compare VMA flags without a VMA write lock held via vma_flags_same_pair(). This has more serious impact, as concurrent changes to atomic VMA flags can cause two sets of VMA flags to be wrongly considered different. For mprotect and madvise this causes a useless split, but mlock is impacted more seriously as it can result in mm->locked_vm underflow. Fix this by updating vma_flags_same_pair() to disregard VM_ATOMIC_SET_ALLOWED VMAs - VMA equality is the same as VMA mergeability, and atomic VMA flags are disregarded when comparing VMA flags for merge - so this is justified semantically. Additionally, remove the redundant vma_flags_same_mask() and the unused vma_flags_same() as part of the change, and replace VM_ATOMIC_SET_ALLOWED with VMA_ATOMIC_SET_ALLOWED, using the new VMA flag API. Finally, update userland VMA tests to reflect the changes. Link: https://lore.kernel.org/20261023-fix-vma-atomic-v1-0-8ff4fae1fc85@kernel.org Link: https://lore.kernel.org/20261023-fix-vma-atomic-v1-1-8ff4fae1fc85@kernel.org Fixes: 2ab7f1bbafc9 ("mm/madvise: allow guard page install/remove under VMA lock") Signed-off-by: Lorenzo Stoakes (ARM) Signed-off-by: Andrew Morton Suggested-by: David Hildenbrand (Arm) Acked-by: David Hildenbrand (Arm) Reviewed-by: Pedro Falcato Tested-by: Gregory Price (Meta) Cc: Liam R. Howlett Cc: Vlastimil Babka Cc: Mike Rapoport Cc: Suren Baghdasaryan Cc: Michal Hocko Cc: Jann Horn Cc: Davidlohr Bueso Cc: SJ Park Cc: Kees Cook Cc: Colin Cross Cc: --- include/linux/mm.h | 172 ++++++++++++++++---------------- mm/madvise.c | 4 +- mm/mlock.c | 6 +- mm/mprotect.c | 2 +- tools/testing/vma/include/dup.h | 78 +++++++++------ tools/testing/vma/shared.h | 16 +-- 6 files changed, 150 insertions(+), 128 deletions(-) diff --git a/include/linux/mm.h b/include/linux/mm.h index 9f167950926a..6af3b7008f1a 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -602,7 +602,7 @@ enum { #define VMA_LOCKED_MASK mk_vma_flags(VMA_LOCKED_BIT, VMA_LOCKONFAULT_BIT) /* These flags can be updated atomically via VMA/mmap read lock. */ -#define VM_ATOMIC_SET_ALLOWED VM_MAYBE_GUARD +#define VMA_ATOMIC_SET_ALLOWED mk_vma_flags(VMA_MAYBE_GUARD_BIT) /* Arch-specific flags to clear when updating VM flags on protection change */ #ifndef VM_ARCH_CLEAR @@ -987,22 +987,6 @@ static inline void vm_flags_reset(struct vm_area_struct *vma, vm_flags_init(vma, flags); } -static inline void vma_flags_reset_once(struct vm_area_struct *vma, - vma_flags_t *flags) -{ - const unsigned long word = flags->__vma_flags[0]; - - /* It is assumed only the first system word must be written once. */ - vma_flags_overwrite_word_once(&vma->flags, word); - /* The remainder can be copied normally. */ - if (NUM_VMA_FLAG_BITS > BITS_PER_LONG) { - unsigned long *dst = &vma->flags.__vma_flags[1]; - const unsigned long *src = &flags->__vma_flags[1]; - - bitmap_copy(dst, src, NUM_VMA_FLAG_BITS - BITS_PER_LONG); - } -} - static inline void vm_flags_set(struct vm_area_struct *vma, vm_flags_t flags) { @@ -1039,48 +1023,6 @@ static inline void vm_flags_mod(struct vm_area_struct *vma, __vm_flags_mod(vma, set, clear); } -static __always_inline bool __vma_atomic_valid_flag(struct vm_area_struct *vma, - vma_flag_t bit) -{ - const vm_flags_t mask = BIT((__force int)bit); - - /* Only specific flags are permitted */ - if (WARN_ON_ONCE(!(mask & VM_ATOMIC_SET_ALLOWED))) - return false; - - return true; -} - -/* - * Set VMA flag atomically. Requires only VMA/mmap read lock. Only specific - * valid flags are allowed to do this. - */ -static __always_inline void vma_set_atomic_flag(struct vm_area_struct *vma, - vma_flag_t bit) -{ - unsigned long *bitmap = vma->flags.__vma_flags; - - vma_assert_stabilised(vma); - if (__vma_atomic_valid_flag(vma, bit)) - set_bit((__force int)bit, bitmap); -} - -/* - * Test for VMA flag atomically. Requires no locks. Only specific valid flags - * are allowed to do this. - * - * This is necessarily racey, so callers must ensure that serialisation is - * achieved through some other means, or that races are permissible. - */ -static __always_inline bool vma_test_atomic_flag(struct vm_area_struct *vma, - vma_flag_t bit) -{ - if (__vma_atomic_valid_flag(vma, bit)) - return test_bit((__force int)bit, &vma->vm_flags); - - return false; -} - /* Set an individual VMA flag in flags, non-atomically. */ static __always_inline void vma_flags_set_flag(vma_flags_t *flags, vma_flag_t bit) @@ -1302,33 +1244,23 @@ static __always_inline vma_flags_t vma_flags_diff_pair(const vma_flags_t *flags, return dst; } -/* Determine if flags and flags_other have precisely the same flags set. */ -static __always_inline bool vma_flags_same_pair(const vma_flags_t *flags, - const vma_flags_t *flags_other) -{ - const unsigned long *bitmap = flags->__vma_flags; - const unsigned long *bitmap_other = flags_other->__vma_flags; - - return bitmap_equal(bitmap, bitmap_other, NUM_VMA_FLAG_BITS); -} - -/* Determine if flags and flags_other have precisely the same flags set. */ -static __always_inline bool vma_flags_same_mask(const vma_flags_t *flags, - vma_flags_t flags_other) -{ - const unsigned long *bitmap = flags->__vma_flags; - const unsigned long *bitmap_other = flags_other.__vma_flags; - - return bitmap_equal(bitmap, bitmap_other, NUM_VMA_FLAG_BITS); -} - /* - * Helper macro to determine if only the specific flags are set, e.g.: - * - * if (vma_flags_same(&flags, VMA_WRITE_BIT) { ... } + * Determine if flags and flags_other have precisely the same flags set, + * excluding atomic VMA flags. */ -#define vma_flags_same(flags, ...) \ - vma_flags_same_mask(flags, mk_vma_flags(__VA_ARGS__)) +static __always_inline bool vma_flags_same_pair(const vma_flags_t *flags, + const vma_flags_t *flags_other) +{ + vma_flags_t diff = vma_flags_diff_pair(flags, flags_other); + + /* + * Atomic VMA flags may be set concurrently under the VMA read lock, so + * are disregarded for equality testing purposes. + */ + vma_flags_clear_mask(&diff, VMA_ATOMIC_SET_ALLOWED); + + return vma_flags_empty(&diff); +} /* * Test whether a specific flag in the VMA is set, e.g.: @@ -1436,6 +1368,78 @@ static __always_inline void vma_clear_flags_mask(struct vm_area_struct *vma, #define vma_clear_flags(vma, ...) \ vma_clear_flags_mask(vma, mk_vma_flags(__VA_ARGS__)) +static __always_inline bool __vma_atomic_valid_flag(struct vm_area_struct *vma, + vma_flag_t bit) +{ + const vma_flags_t mask = VMA_ATOMIC_SET_ALLOWED; + + /* Is this flag allowed to be set atomically? */ + if (WARN_ON_ONCE(!vma_flags_test(&mask, bit))) + return false; + + return true; +} + +/* + * Set VMA flag atomically. Requires only VMA/mmap read lock. Only specific + * valid flags are allowed to do this. + */ +static __always_inline void vma_set_atomic_flag(struct vm_area_struct *vma, + vma_flag_t bit) +{ + unsigned long *bitmap = vma->flags.__vma_flags; + + vma_assert_stabilised(vma); + if (__vma_atomic_valid_flag(vma, bit)) + set_bit((__force int)bit, bitmap); +} + +/* + * Test for VMA flag atomically. Requires no locks. Only specific valid flags + * are allowed to do this. + * + * This is necessarily racey, so callers must ensure that serialisation is + * achieved through some other means, or that races are permissible. + */ +static __always_inline bool vma_test_atomic_flag(struct vm_area_struct *vma, + vma_flag_t bit) +{ + if (__vma_atomic_valid_flag(vma, bit)) + return test_bit((__force int)bit, &vma->vm_flags); + + return false; +} + +/* + * Overwrite the VMA's flags with new ones, preserving atomic VMA flags in the + * process. The write is done once and once only. + */ +static inline void vma_overwrite_flags(struct vm_area_struct *vma, + vma_flags_t new_flags) +{ + vma_flags_t atomic; + + vma_assert_write_locked(vma); + + /* + * Preserve the VMA's atomic flags, which are kept stable by the VMA + * write lock. + */ + atomic = vma_flags_and_mask(&vma->flags, VMA_ATOMIC_SET_ALLOWED); + vma_flags_clear_mask(&new_flags, VMA_ATOMIC_SET_ALLOWED); + vma_flags_set_mask(&new_flags, atomic); + + /* It is assumed only the first system word must be written once. */ + vma_flags_overwrite_word_once(&vma->flags, new_flags.__vma_flags[0]); + /* The remainder can be copied normally. */ + if (NUM_VMA_FLAG_BITS > BITS_PER_LONG) { + unsigned long *dst = &vma->flags.__vma_flags[1]; + const unsigned long *src = &new_flags.__vma_flags[1]; + + bitmap_copy(dst, src, NUM_VMA_FLAG_BITS - BITS_PER_LONG); + } +} + /* * Test whether a specific VMA flag is set in a VMA descriptor, e.g.: * diff --git a/mm/madvise.c b/mm/madvise.c index 75b409e97dbf..b2e00efcaa54 100644 --- a/mm/madvise.c +++ b/mm/madvise.c @@ -158,7 +158,7 @@ static int madvise_update_vma(vm_flags_t new_flags, bool set_new_anon_name = madv_behavior->behavior == __MADV_SET_ANON_VMA_NAME; VMA_ITERATOR(vmi, madv_behavior->mm, range->start); - if (vma_flags_same_mask(&vma->flags, new_vma_flags) && + if (vma_flags_same_pair(&vma->flags, &new_vma_flags) && (!set_new_anon_name || anon_vma_name_eq(anon_vma_name(vma), anon_name))) return 0; @@ -177,7 +177,7 @@ static int madvise_update_vma(vm_flags_t new_flags, /* vm_flags is protected by the mmap_lock held in write mode. */ vma_start_write(vma); - vma->flags = new_vma_flags; + vma_overwrite_flags(vma, new_vma_flags); /* * If the vma become good for khugepaged to scan, * register it here without waiting a page fault that diff --git a/mm/mlock.c b/mm/mlock.c index 67ae9b18b923..1c70a7829394 100644 --- a/mm/mlock.c +++ b/mm/mlock.c @@ -432,14 +432,14 @@ static void mlock_vma_pages_range(struct vm_area_struct *vma, } vma_start_write(vma); - vma_flags_reset_once(vma, &walk_flags); + vma_overwrite_flags(vma, walk_flags); lru_add_drain(); walk_page_range_vma(vma, start, end, &mlock_walk_ops, (void *)lock); lru_add_drain(); if (lock) - vma_flags_reset_once(vma, new_vma_flags); + vma_overwrite_flags(vma, *new_vma_flags); } /* @@ -494,7 +494,7 @@ static int mlock_fixup(struct vma_iterator *vmi, struct vm_area_struct *vma, vma_flags_test(&old_vma_flags, VMA_LOCKED_BIT)) { /* No work to do, and mlocking twice would be wrong */ vma_start_write(vma); - vma->flags = *new_vma_flags; + vma_overwrite_flags(vma, *new_vma_flags); } else { mlock_vma_pages_range(vma, start, end, new_vma_flags); } diff --git a/mm/mprotect.c b/mm/mprotect.c index 754333698c1e..e18d4e12fc34 100644 --- a/mm/mprotect.c +++ b/mm/mprotect.c @@ -833,7 +833,7 @@ mprotect_fixup(struct vma_iterator *vmi, struct mmu_gather *tlb, * held in write mode. */ vma_start_write(vma); - vma_flags_reset_once(vma, &new_vma_flags); + vma_overwrite_flags(vma, new_vma_flags); if (vma_wants_manual_pte_write_upgrade(vma)) mm_cp_flags |= MM_CP_TRY_CHANGE_WRITABLE; vma_set_page_prot(vma); diff --git a/tools/testing/vma/include/dup.h b/tools/testing/vma/include/dup.h index e0eab4eca998..a13977405482 100644 --- a/tools/testing/vma/include/dup.h +++ b/tools/testing/vma/include/dup.h @@ -366,6 +366,9 @@ enum { #define VMA_LOCKED_MASK mk_vma_flags(VMA_LOCKED_BIT, VMA_LOCKONFAULT_BIT) +/* These flags can be updated atomically via VMA/mmap read lock. */ +#define VMA_ATOMIC_SET_ALLOWED mk_vma_flags(VMA_MAYBE_GUARD_BIT) + #define RLIMIT_STACK 3 /* max stack size */ #define RLIMIT_MEMLOCK 8 /* max locked-in-memory address space */ @@ -895,22 +898,6 @@ static inline void vm_flags_reset(struct vm_area_struct *vma, vm_flags_init(vma, flags); } -static inline void vma_flags_reset_once(struct vm_area_struct *vma, - vma_flags_t *flags) -{ - const unsigned long word = flags->__vma_flags[0]; - - /* It is assumed only the first system word must be written once. */ - vma_flags_overwrite_word_once(&vma->flags, word); - /* The remainder can be copied normally. */ - if (NUM_VMA_FLAG_BITS > BITS_PER_LONG) { - unsigned long *dst = &vma->flags.__vma_flags[1]; - const unsigned long *src = &flags->__vma_flags[1]; - - bitmap_copy(dst, src, NUM_VMA_FLAG_BITS - BITS_PER_LONG); - } -} - static inline void vm_flags_set(struct vm_area_struct *vma, vm_flags_t flags) { @@ -1037,27 +1024,24 @@ static __always_inline vma_flags_t vma_flags_diff_pair(const vma_flags_t *flags, return dst; } +/* + * Determine if flags and flags_other have precisely the same flags set, + * excluding atomic VMA flags. + */ static __always_inline bool vma_flags_same_pair(const vma_flags_t *flags, - const vma_flags_t *flags_other) + const vma_flags_t *flags_other) { - const unsigned long *bitmap = flags->__vma_flags; - const unsigned long *bitmap_other = flags_other->__vma_flags; + vma_flags_t diff = vma_flags_diff_pair(flags, flags_other); - return bitmap_equal(bitmap, bitmap_other, NUM_VMA_FLAG_BITS); + /* + * Atomic VMA flags may be set concurrently under the VMA read lock, so + * are disregarded for equality testing purposes. + */ + vma_flags_clear_mask(&diff, VMA_ATOMIC_SET_ALLOWED); + + return vma_flags_empty(&diff); } -static __always_inline bool vma_flags_same_mask(const vma_flags_t *flags, - vma_flags_t flags_other) -{ - const unsigned long *bitmap = flags->__vma_flags; - const unsigned long *bitmap_other = flags_other.__vma_flags; - - return bitmap_equal(bitmap, bitmap_other, NUM_VMA_FLAG_BITS); -} - -#define vma_flags_same(flags, ...) \ - vma_flags_same_mask(flags, mk_vma_flags(__VA_ARGS__)) - static __always_inline bool vma_test(const struct vm_area_struct *vma, vma_flag_t bit) { @@ -1106,6 +1090,36 @@ static __always_inline void vma_clear_flags_mask(struct vm_area_struct *vma, #define vma_clear_flags(vma, ...) \ vma_clear_flags_mask(vma, mk_vma_flags(__VA_ARGS__)) +/* + * Overwrite the VMA's flags with new ones, preserving atomic VMA flags in the + * process. The write is done once and once only. + */ +static inline void vma_overwrite_flags(struct vm_area_struct *vma, + vma_flags_t new_flags) +{ + vma_flags_t atomic; + + vma_assert_write_locked(vma); + + /* + * Preserve the VMA's atomic flags, which are kept stable by the VMA + * write lock. + */ + atomic = vma_flags_and_mask(&vma->flags, VMA_ATOMIC_SET_ALLOWED); + vma_flags_clear_mask(&new_flags, VMA_ATOMIC_SET_ALLOWED); + vma_flags_set_mask(&new_flags, atomic); + + /* It is assumed only the first system word must be written once. */ + vma_flags_overwrite_word_once(&vma->flags, new_flags.__vma_flags[0]); + /* The remainder can be copied normally. */ + if (NUM_VMA_FLAG_BITS > BITS_PER_LONG) { + unsigned long *dst = &vma->flags.__vma_flags[1]; + const unsigned long *src = &new_flags.__vma_flags[1]; + + bitmap_copy(dst, src, NUM_VMA_FLAG_BITS - BITS_PER_LONG); + } +} + static __always_inline bool vma_desc_test(const struct vm_area_desc *desc, vma_flag_t bit) { diff --git a/tools/testing/vma/shared.h b/tools/testing/vma/shared.h index 97cd7a679dc1..646edf367d7c 100644 --- a/tools/testing/vma/shared.h +++ b/tools/testing/vma/shared.h @@ -51,17 +51,21 @@ __TO_SCALAR(__val1), __TO_SCALAR(__val2)); \ } while (0) -#define ASSERT_FLAGS_SAME_MASK(_flags, _flags_other) \ - ASSERT_TRUE(vma_flags_same_mask((_flags), (_flags_other))) +#define ASSERT_FLAGS_SAME_MASK(_flags, _flags_other) do { \ + const vma_flags_t __flags_other = (_flags_other); \ + ASSERT_TRUE(vma_flags_same_pair((_flags), &__flags_other)); \ + } while (0) -#define ASSERT_FLAGS_NOT_SAME_MASK(_flags, _flags_other) \ - ASSERT_FALSE(vma_flags_same_mask((_flags), (_flags_other))) +#define ASSERT_FLAGS_NOT_SAME_MASK(_flags, _flags_other) do { \ + const vma_flags_t __flags_other = (_flags_other); \ + ASSERT_FALSE(vma_flags_same_pair((_flags), &__flags_other)); \ + } while (0) #define ASSERT_FLAGS_SAME(_flags, ...) \ - ASSERT_TRUE(vma_flags_same(_flags, __VA_ARGS__)) + ASSERT_FLAGS_SAME_MASK(_flags, mk_vma_flags(__VA_ARGS__)) #define ASSERT_FLAGS_NOT_SAME(_flags, ...) \ - ASSERT_FALSE(vma_flags_same(_flags, __VA_ARGS__)) + ASSERT_FLAGS_NOT_SAME_MASK(_flags, mk_vma_flags(__VA_ARGS__)) #define ASSERT_FLAGS_EMPTY(_flags) \ ASSERT_TRUE(vma_flags_empty(_flags)) From 769417e410dd19b2a438c6addb61b194b7a4c387 Mon Sep 17 00:00:00 2001 From: "David Hildenbrand (Arm)" Date: Fri, 23 Oct 2026 14:47:28 +0100 Subject: [PATCH 13/43] mm/vma: write lock source VMA before duplicating it in __split_vma() __split_vma() duplicates the source VMA before taking its VMA write lock, and until the VMA is write-locked, a concurrent thread might set VMA_MAYBE_GUARD_BIT, resulting in the duplication missing the flag. If this happens, then fork may fail to duplicate guard markers and, if a file-backed mapping, THP collapse may also discard the markers. Resolve this by taking the VMA write lock before vm_area_dup() and, since all callers of vm_area_dup() now hold the VMA write lock, assert that this is the case there, and add a comment to explain why it's needed. [ljs@kernel.org: add VMA write assert in vm_area_dup(), commit message touchup] Link: https://lore.kernel.org/20261023-fix-vma-atomic-v1-2-8ff4fae1fc85@kernel.org Fixes: 2ab7f1bbafc9 ("mm/madvise: allow guard page install/remove under VMA lock") Signed-off-by: David Hildenbrand (Arm) Signed-off-by: Lorenzo Stoakes (ARM) Signed-off-by: Andrew Morton Reviewed-by: Lorenzo Stoakes (ARM) Reviewed-by: Davidlohr Bueso Reviewed-by: Pedro Falcato Tested-by: Gregory Price (Meta) Cc: Liam R. Howlett Cc: Vlastimil Babka Cc: Mike Rapoport Cc: Suren Baghdasaryan Cc: Michal Hocko Cc: Jann Horn Cc: SJ Park Cc: Kees Cook Cc: Colin Cross Cc: --- mm/vma.c | 2 +- mm/vma_init.c | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/mm/vma.c b/mm/vma.c index 88ef92ab24ec..0325b3979313 100644 --- a/mm/vma.c +++ b/mm/vma.c @@ -538,6 +538,7 @@ __split_vma(struct vma_iterator *vmi, struct vm_area_struct *vma, return err; } + vma_start_write(vma); new = vm_area_dup(vma); if (!new) return -ENOMEM; @@ -568,7 +569,6 @@ __split_vma(struct vma_iterator *vmi, struct vm_area_struct *vma, if (new->vm_ops && new->vm_ops->open) new->vm_ops->open(new); - vma_start_write(vma); vma_start_write(new); init_vma_prep(&vp, vma); diff --git a/mm/vma_init.c b/mm/vma_init.c index baa7e82f47e3..d661f229a734 100644 --- a/mm/vma_init.c +++ b/mm/vma_init.c @@ -127,6 +127,9 @@ struct vm_area_struct *vm_area_dup(struct vm_area_struct *orig) { struct vm_area_struct *new = kmem_cache_alloc(vm_area_cachep, GFP_KERNEL); + /* Must hold a write lock to stabilise atomic VMA flags. */ + vma_assert_write_locked(orig); + if (!new) return NULL; From 652f15be3c11415725694ccea317c51e35c0b582 Mon Sep 17 00:00:00 2001 From: "David Hildenbrand (Arm)" Date: Fri, 23 Oct 2026 14:47:29 +0100 Subject: [PATCH 14/43] mm/madvise: preserve VMA_SOFTDIRTY_BIT when changing anonymous names When changing the anonymous VMA name, madvise_update_vma() calls vma_modify_name() to possibly merge with adjacent VMAs. We don't call vma_modify_flags() and consequently don't obtain the new VMA flags. Later in the function we update the VMA flags to the stale new_vma_flags we obtained before. This can result in us losing the VMA_SOFTDIRTY_BIT flag set on a VMA we just merged with. There is no need to update the VMA flags at all, so just exit early. This skips calling khugepaged_enter_vma(), but since the flags didn't change this is fine. [ljs@kernel.org: minor edit of commit message, rebased] Link: https://lore.kernel.org/20261023-fix-vma-atomic-v1-3-8ff4fae1fc85@kernel.org Fixes: 9a10064f5625 ("mm: add a field to store names for private anonymous memory") Signed-off-by: David Hildenbrand (Arm) Signed-off-by: Lorenzo Stoakes (ARM) Signed-off-by: Andrew Morton Reviewed-by: Lorenzo Stoakes (ARM) Reviewed-by: Pedro Falcato Tested-by: Gregory Price (Meta) Cc: Liam R. Howlett Cc: Vlastimil Babka Cc: Mike Rapoport Cc: Suren Baghdasaryan Cc: Michal Hocko Cc: Jann Horn Cc: Davidlohr Bueso Cc: SJ Park Cc: Kees Cook Cc: Colin Cross Cc: --- mm/madvise.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mm/madvise.c b/mm/madvise.c index b2e00efcaa54..e9fa4f71efea 100644 --- a/mm/madvise.c +++ b/mm/madvise.c @@ -177,6 +177,9 @@ static int madvise_update_vma(vm_flags_t new_flags, /* vm_flags is protected by the mmap_lock held in write mode. */ vma_start_write(vma); + if (set_new_anon_name) + return replace_anon_vma_name(vma, anon_name); + vma_overwrite_flags(vma, new_vma_flags); /* * If the vma become good for khugepaged to scan, @@ -186,9 +189,6 @@ static int madvise_update_vma(vm_flags_t new_flags, if (vma_flags_test(&new_vma_flags, VMA_HUGEPAGE_BIT)) khugepaged_enter_vma(vma, vma_flags_to_legacy(new_vma_flags)); - if (set_new_anon_name) - return replace_anon_vma_name(vma, anon_name); - return 0; } From c386d799f423685952138cab8d89f0331825bdee Mon Sep 17 00:00:00 2001 From: "Lorenzo Stoakes (ARM)" Date: Fri, 23 Oct 2026 14:47:30 +0100 Subject: [PATCH 15/43] mm/vma: avoid losing atomic VMA flags when setting sticky VMA flags vma_merge_existing_range() merges an existing VMA (middle) when its attributes have changed to match those of one or both adjacent VMAs. Sticky flags are propagated if they are set in any of the merged VMAs. They are initialised from vmg->vma_flags, which is assumed to contain middle's sticky flags when the function is invoked. However, the VMA write lock is not held for middle when they are first populated, so concurrent updates to atomic VMA flags may occur, which will be lost upon merge. This can result in fork failing to propagate guard region markers and (if file-backed) THP collapse discarding them. Resolve this by propagating the sticky flags after middle is locked. Make life easier by factoring this into propagate_sticky_flags(), which treats vmg->vma_flags as its base (in case any sticky flags are set there), updating it as sticky flags are accumulated from merged VMAs. Also update vma_expand() to use propagate_sticky_flags() so all merge paths utilise it. propagate_sticky_flags() also asserts that the VMA write lock is held for the VMA whose sticky flags are being read, with a comment explaining why. Another means by which atomic VMA flags can fail to be observed is when the merge is finalised in commit_merge(). rmap locks are dropped in vma_complete() before the sticky flags are written, meaning an rmap walker can observe the VMA without sticky flags set, which can also result in retract_page_tables() discarding guard markers. Resolve this by assigning the sticky flags before dropping rmap locks, factored into commit_sticky_flags() with a write assert on the VMA being written to. Also remove the vma_set_flags_mask() invocation at the end of vma_merge_existing_range() as this was introduced by commit 64212ba02e66 ("mm: implement sticky VMA flags") to propagate sticky flags, which is now redundant with the introduction of commit_sticky_flags(). The vma_set_flags_mask() invocation at the end of vma_expand() setting sticky flags only is also removed for the same reason. Also update vma_merge_existing_range()'s comment to reflect the fact that vmg->vma_flags gets updated. Finally, update the userland VMA tests to reflect a previously missed const-ification in vma_assert_write_locked() which would otherwise cause a warning when building them. Link: https://lore.kernel.org/20261023-fix-vma-atomic-v1-4-8ff4fae1fc85@kernel.org Fixes: 2ab7f1bbafc9 ("mm/madvise: allow guard page install/remove under VMA lock") Fixes: 49e14dabed7a ("mm: set the VM_MAYBE_GUARD flag on guard region install") Signed-off-by: Lorenzo Stoakes (ARM) Signed-off-by: Andrew Morton Suggested-by: David Hildenbrand (Arm) Acked-by: David Hildenbrand (Arm) Reviewed-by: Pedro Falcato Tested-by: Gregory Price (Meta) Cc: Liam R. Howlett Cc: Vlastimil Babka Cc: Mike Rapoport Cc: Suren Baghdasaryan Cc: Michal Hocko Cc: Jann Horn Cc: Davidlohr Bueso Cc: SJ Park Cc: Kees Cook Cc: Colin Cross Cc: --- mm/vma.c | 55 +++++++++++++++++-------------- tools/testing/vma/include/dup.h | 2 +- tools/testing/vma/include/stubs.h | 2 +- 3 files changed, 32 insertions(+), 27 deletions(-) diff --git a/mm/vma.c b/mm/vma.c index 0325b3979313..af8e65ada23b 100644 --- a/mm/vma.c +++ b/mm/vma.c @@ -783,6 +783,27 @@ static void vmg_adjust_set_range(struct vma_merge_struct *vmg) } } +static void propagate_sticky_flags(struct vma_merge_struct *vmg, + const struct vm_area_struct *vma) +{ + const vma_flags_t sticky = + vma_flags_and_mask(&vma->flags, VMA_STICKY_FLAGS); + + /* Write lock prevents concurrent atomic VMA flag updates. */ + vma_assert_write_locked(vma); + vma_flags_set_mask(&vmg->vma_flags, sticky); +} + +static void commit_sticky_flags(struct vma_merge_struct *vmg) +{ + const vma_flags_t sticky = + vma_flags_and_mask(&vmg->vma_flags, VMA_STICKY_FLAGS); + struct vm_area_struct *target = vmg->target; + + vma_assert_write_locked(target); + vma_set_flags_mask(target, sticky); +} + /* * Actually perform the VMA merge operation. * @@ -818,6 +839,8 @@ static int commit_merge(struct vma_merge_struct *vmg) return -ENOMEM; vma_prepare(&vp); + /* Must be made visible before vma_complete() for rmap walkers. */ + commit_sticky_flags(vmg); /* * THP pages may need to do additional splits if we increase * middle->vm_start. @@ -857,8 +880,8 @@ static bool can_merge_remove_vma(struct vm_area_struct *vma) * merge. * * As part of this operation, if a merge occurs, the @vmg object will have its - * vma, start, end, and pgoff fields modified to execute the merge. Subsequent - * calls to this function should reset these fields. + * vma, start, end, pgoff and vma_flags fields modified to execute the merge. + * Subsequent calls to this function should reset these fields. * * Returns: The merged VMA if merge succeeds, or NULL otherwise. * @@ -872,8 +895,6 @@ static bool can_merge_remove_vma(struct vm_area_struct *vma) static __must_check struct vm_area_struct *vma_merge_existing_range( struct vma_merge_struct *vmg) { - vma_flags_t sticky_flags = vma_flags_and_mask(&vmg->vma_flags, - VMA_STICKY_FLAGS); struct vm_area_struct *middle = vmg->middle; struct vm_area_struct *prev = vmg->prev; struct vm_area_struct *next; @@ -966,24 +987,18 @@ static __must_check struct vm_area_struct *vma_merge_existing_range( /* No matter what happens, we will be adjusting middle. */ vma_start_write(middle); + propagate_sticky_flags(vmg, middle); if (merge_right) { - vma_flags_t next_sticky; - vma_start_write(next); vmg->target = next; - next_sticky = vma_flags_and_mask(&next->flags, VMA_STICKY_FLAGS); - vma_flags_set_mask(&sticky_flags, next_sticky); + propagate_sticky_flags(vmg, next); } if (merge_left) { - vma_flags_t prev_sticky; - vma_start_write(prev); vmg->target = prev; - - prev_sticky = vma_flags_and_mask(&prev->flags, VMA_STICKY_FLAGS); - vma_flags_set_mask(&sticky_flags, prev_sticky); + propagate_sticky_flags(vmg, prev); } if (merge_both) { @@ -1054,7 +1069,6 @@ static __must_check struct vm_area_struct *vma_merge_existing_range( if (err || commit_merge(vmg)) goto abort; - vma_set_flags_mask(vmg->target, sticky_flags); khugepaged_enter_vma(vmg->target, vmg->vm_flags); vmg->state = VMA_MERGE_SUCCESS; return vmg->target; @@ -1241,16 +1255,11 @@ int vma_expand(struct vma_merge_struct *vmg) struct vm_area_struct *target = vmg->target; struct vm_area_struct *next = vmg->next; bool remove_next = false; - vma_flags_t sticky_flags = - vma_flags_and_mask(&vmg->vma_flags, VMA_STICKY_FLAGS); - vma_flags_t target_sticky; int ret = 0; mmap_assert_write_locked(vmg->mm); vma_start_write(target); - target_sticky = vma_flags_and_mask(&target->flags, VMA_STICKY_FLAGS); - if (next && target != next && vmg->end == next->vm_end) remove_next = true; @@ -1265,7 +1274,7 @@ int vma_expand(struct vma_merge_struct *vmg) VM_WARN_ON_VMG(target->vm_start < vmg->start || target->vm_end > vmg->end, vmg); - vma_flags_set_mask(&sticky_flags, target_sticky); + propagate_sticky_flags(vmg, target); /* * If we are removing the next VMA or copying from a VMA @@ -1282,18 +1291,14 @@ int vma_expand(struct vma_merge_struct *vmg) return ret; if (remove_next) { - vma_flags_t next_sticky; - vma_start_write(next); vmg->__remove_next = true; - next_sticky = vma_flags_and_mask(&next->flags, VMA_STICKY_FLAGS); - vma_flags_set_mask(&sticky_flags, next_sticky); + propagate_sticky_flags(vmg, next); } if (commit_merge(vmg)) goto nomem; - vma_set_flags_mask(target, sticky_flags); return 0; nomem: diff --git a/tools/testing/vma/include/dup.h b/tools/testing/vma/include/dup.h index a13977405482..dc4927d26296 100644 --- a/tools/testing/vma/include/dup.h +++ b/tools/testing/vma/include/dup.h @@ -1212,7 +1212,7 @@ static inline void vma_assert_detached(struct vm_area_struct *vma) WARN_ON_ONCE(vma_is_attached(vma)); } -static inline void vma_assert_write_locked(struct vm_area_struct *); +static inline void vma_assert_write_locked(const struct vm_area_struct *); static inline void vma_mark_attached(struct vm_area_struct *vma) { vma_assert_write_locked(vma); diff --git a/tools/testing/vma/include/stubs.h b/tools/testing/vma/include/stubs.h index f786a585caf0..8d030a3ace84 100644 --- a/tools/testing/vma/include/stubs.h +++ b/tools/testing/vma/include/stubs.h @@ -320,7 +320,7 @@ static inline void anon_vma_lock_write(struct anon_vma *anon_vma) { } -static inline void vma_assert_write_locked(struct vm_area_struct *vma) +static inline void vma_assert_write_locked(const struct vm_area_struct *vma) { } From 2efc6332f18bfd848f74f3d5ba45b31450601faa Mon Sep 17 00:00:00 2001 From: Samarth Narang Date: Wed, 21 Oct 2026 11:21:57 -0700 Subject: [PATCH 16/43] mm/ksm: keep soft-dirty when merging a page replace_page() maps the KSM page with a PTE built from scratch, so the soft-dirty bit of the PTE it replaces is lost. A page written since "clear_refs 4" then no longer shows as soft-dirty once it is merged, and a soft-dirty based dumper treats it as unchanged. Copy the soft-dirty bit from the original PTE to the new PTE. Link: https://lore.kernel.org/20261021182157.1873726-1-samarth.colleges@gmail.com Fixes: 0f8975ec4db2 ("mm: soft-dirty bits for user memory changes tracking") Signed-off-by: Samarth Narang Signed-off-by: Andrew Morton Reviewed-by: Lance Yang Acked-by: David Hildenbrand (Arm) Assisted-by: LLM Cc: Xu Xin Cc: Chengming Zhou Cc: Peter Xu Cc: Kiryl Shutsemau Cc: Mike Rapoport Cc: --- mm/ksm.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mm/ksm.c b/mm/ksm.c index dbfb8f98b189..910a1307ac9f 100644 --- a/mm/ksm.c +++ b/mm/ksm.c @@ -1463,6 +1463,10 @@ static int replace_page(struct vm_area_struct *vma, struct page *page, dec_mm_counter(mm, MM_ANONPAGES); } + /* Preserve soft-dirty when replacing the page. */ + if (pte_soft_dirty(orig_pte)) + newpte = pte_mksoft_dirty(newpte); + flush_cache_page(vma, addr, pte_pfn(ptep_get(ptep))); /* * No need to notify as we are replacing a read only page with another From bfc1d9724c78aa7570dbeb17207a21089f2d1b1a Mon Sep 17 00:00:00 2001 From: Hengyu Liang Date: Fri, 23 Oct 2026 14:13:00 -0400 Subject: [PATCH 17/43] fs/proc/task_mmu: return -ENOTTY for unknown pagemap ioctls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 52526ca7fdb9 ("fs/proc/task_mmu: implement IOCTL to get and optionally clear info about PTEs") made do_pagemap_cmd() the ioctl handler of /proc//pagemap. However, do_pagemap_cmd() returns -EINVAL for every command other than PAGEMAP_SCAN. Before that commit, pagemap had no ioctl handler, and the VFS returned -ENOTTY for these commands. As of now, they fail with EINVAL, so tcgetattr() and isatty() on pagemap report EINVAL instead of ENOTTY. The issue can be reproduced with a simple C program: #include #include #include int main(void) { struct termios t; int fd = open("/proc/self/pagemap", O_RDONLY); if (tcgetattr(fd, &t) < 0) perror("tcgetattr"); return 0; } Before that commit, the result is: tcgetattr: Inappropriate ioctl for device After that commit, the result is: tcgetattr: Invalid argument This patch will return -ENOIOCTLCMD for unknown commands, as procfs_procmap_ioctl() in the same file does, so that the VFS turns it into -ENOTTY. Link: https://lore.kernel.org/20261023181300.3366487-1-hengyul@cs.unc.edu Fixes: 52526ca7fdb9 ("fs/proc/task_mmu: implement IOCTL to get and optionally clear info about PTEs") Signed-off-by: Hengyu Liang Signed-off-by: Andrew Morton Acked-by: David Hildenbrand (Arm) Reviewed-by: Pedro Falcato Reviewed-by: Lorenzo Stoakes (ARM) Cc: Liam R. Howlett Cc: Vlastimil Babka Cc: Jann Horn Cc: Muhammad Usama Anjum Cc: Andrei Vagin Cc: Michał Mirosław Cc: Arnd Bergmann Cc: --- fs/proc/task_mmu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 7452b695fdae..f68673a91756 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -3201,7 +3201,7 @@ static long do_pagemap_cmd(struct file *file, unsigned int cmd, return do_pagemap_scan(mm, arg); default: - return -EINVAL; + return -ENOIOCTLCMD; } } From a76adcace3550b16e5735ba4a5e95a9c2664e113 Mon Sep 17 00:00:00 2001 From: SJ Park Date: Sun, 25 Oct 2026 09:48:04 -0700 Subject: [PATCH 18/43] mm/damon/sysfs-schemes: hold damon_sysfs_lock in target_metric_store() Patch series "mm/damon: fix three race bugs in sysfs and core", v1. There are three race condition bugs in DAMON sysfs interface and the core layer. User impacts are not critical but better to fix sooner than later. Patch 1 fixes a toctou bug in DAMON sysfs interface that can mess up DAMOS auto-tuning. Patches 2 and 3 move static variables that can be wrongly shared with multiple kdamonds and therefore make DAMON monitoring results be a little more indeterministic. This patch (of 3): damon_sysfs_add_quota_score() reads damos_sysfs_quota_goal->metric twice, once to set damos_quota_goal->metric to the value, and once again to set the metric-dependent damos_quota_goal unions based on the value. target_metrics_score() can update damos_sysfs_quota_goal->metric between the two reads. This time-of-check to time-of-use (toctou) race can make the union fields incorrectly set. The incorrect damos_quota_goal will be passed to the core layer and make DAMOS incorrectly behave. Users could reproduce the race by writing a new value to the target_metric sysfs file while DAMON is starting, or online parameters update is ongoing. The issue will only rarely happen in the real use cases. It requires sysfs write permission. The race window is short. Sane users wouldn't do such writes while DAMON enablement and/or online parameters update is ongoing. The user impact is not very critical. The race is not easy to unintentionally trigger. No leak or crash happens. Nonetheless, DAMOS could be unexpectedly more or less aggressive and does not achieve the auto-tune goal. Because of the nature of races, investigating will be difficult. Users can control the impact from unexpected aggressiveness using additional quotas and resolve the issue in runtime by re-installing the quota goal, though. Avoid the race by protecting damos_sysfs_quota_goal->metric write in target_metric_store() with damon_sysfs_lock. Because damon_sysfs_add_quota_score() is called while holding the same lock, the race is removed. The issue was discovered as a part of a bug report [1] from Sashiko, which is partially outdated. In more detail, the Sashiko report [1] was mainly for an outdated bug [2]. But it was also mentioning the issue of this patch as a similar class bug. Link: https://lore.kernel.org/20261025164808.152048-1-sj@kernel.org Link: https://lore.kernel.org/20261025164808.152048-2-sj@kernel.org Link: https://sashiko.dev/#/bug/linux-7f814c9a-986f-4103-a55a-ef9537dcf62b [1] Link: https://lore.kernel.org/20261021005234.94107-1-sj@kernel.org [2] Fixes: 85fcf0ffc460 ("mm/damon/sysfs-schemes: connect damos_quota_goal nid with core layer") Signed-off-by: SJ Park Signed-off-by: Andrew Morton Cc: # 6.16.x --- mm/damon/sysfs-schemes.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mm/damon/sysfs-schemes.c b/mm/damon/sysfs-schemes.c index 8f083611741f..637f1bd0c950 100644 --- a/mm/damon/sysfs-schemes.c +++ b/mm/damon/sysfs-schemes.c @@ -1310,7 +1310,10 @@ static ssize_t target_metric_store(struct kobject *kobj, metric_name = &damos_sysfs_qgoal_metric_names[i]; if (sysfs_streq(buf, metric_name->name)) { + if (!mutex_trylock(&damon_sysfs_lock)) + return -EBUSY; goal->metric = metric_name->metric; + mutex_unlock(&damon_sysfs_lock); return count; } } From 26ae50a0901a44f7d50053f87dc38abd9f9a269c Mon Sep 17 00:00:00 2001 From: SJ Park Date: Sun, 25 Oct 2026 09:48:05 -0700 Subject: [PATCH 19/43] mm/damon/core: move static variable last_nr_regions to damon_ctx kdamond_split_regions() uses a static variable last_nr_regions, to infer if the region boundaries are too stable. If it looks too stable, it does a more aggressive region split because it might be a fake stability coming from non-aggressive split. Because the variable is static, the inference can be racy and wrong when multiple kdamonds are running in parallel. The user impact is not critical. No crash, no leak. DAMON's best-effort monitoring accuracy can be degraded when multiple kdamonds are used in parallel and the workload has certain access patterns. Still it is a bug that is better to fix. Move the variable into the damon_ctx struct to fix the problem. Link: https://lore.kernel.org/20261025164808.152048-3-sj@kernel.org Fixes: 75c1c2b53c78 ("mm/damon/dbgfs: support multiple contexts") Signed-off-by: SJ Park Signed-off-by: Andrew Morton Closes: https://sashiko.dev/#/bug/linux-eebfcdde-2881-48ef-8df1-e2019d2a47f2 Cc: Fernand Sieber Cc: SeongJae Park Cc: # 5.15.x --- include/linux/damon.h | 5 +++++ mm/damon/core.c | 5 ++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/include/linux/damon.h b/include/linux/damon.h index 63050eb2206a..cb6a50032a1e 100644 --- a/include/linux/damon.h +++ b/include/linux/damon.h @@ -958,6 +958,11 @@ struct damon_ctx { /* @rnd_state: Per-ctx PRNG state for damon_rand(). */ struct rnd_state rnd_state; + /* + * number of regions after last merge. Read kdamond_split_regions() + * for more details. + */ + unsigned long last_nr_regions; }; /* Get a random number in [@l, @r) using @ctx's lockless PRNG. */ diff --git a/mm/damon/core.c b/mm/damon/core.c index eb1fcdabc007..09722257ce73 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -3795,7 +3795,6 @@ static noinline_for_stack void kdamond_split_regions(struct damon_ctx *ctx) struct damon_target *t; unsigned long nr_regions = 0; unsigned long max_nr_regions = ctx->attrs.max_nr_regions; - static unsigned long last_nr_regions; int nr_subregions = 2; damon_for_each_target(t, ctx) @@ -3811,7 +3810,7 @@ static noinline_for_stack void kdamond_split_regions(struct damon_ctx *ctx) } /* Maybe the middle of the region has different access frequency */ - if (last_nr_regions == nr_regions && + if (ctx->last_nr_regions == nr_regions && nr_regions < max_nr_regions / 3) nr_subregions = 3; @@ -3820,7 +3819,7 @@ static noinline_for_stack void kdamond_split_regions(struct damon_ctx *ctx) ctx->min_region_sz); done: - last_nr_regions = nr_regions; + ctx->last_nr_regions = nr_regions; } /* From c4ff3ea30640b394df4156d7ddf60ad8ffe765f1 Mon Sep 17 00:00:00 2001 From: SJ Park Date: Sun, 25 Oct 2026 09:48:06 -0700 Subject: [PATCH 20/43] mm/damon/core: move static variable rotation to damon_ctx damon_split_some_reigons() uses a static variable, rotation, to remember how many times it was called. It is again used to determine fair selection of regions to split. Since it is a static variable, it can be indeterministic and therefore less fair. The user impact is not critical. No crash, no leak. DAMON's best-effort and indeterministic monitoring accuracy could be slightly more indeterministic when multiple kdamonds are used in parallel. Fix the problem by moving the static variable into the damon_ctx struct. Link: https://lore.kernel.org/20261025164808.152048-4-sj@kernel.org Fixes: 1a582e6e0174 ("mm/damon/core: split a fraction of regions when nr_regions exceeds max/2") Signed-off-by: SJ Park Signed-off-by: Andrew Morton Closes: https://sashiko.dev/#/bug/linux-eebfcdde-2881-48ef-8df1-e2019d2a47f2 Cc: Jiayuan Chen Cc: # 7.3.x --- include/linux/damon.h | 5 +++++ mm/damon/core.c | 5 +++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/include/linux/damon.h b/include/linux/damon.h index cb6a50032a1e..380f9cf6ddd8 100644 --- a/include/linux/damon.h +++ b/include/linux/damon.h @@ -963,6 +963,11 @@ struct damon_ctx { * for more details. */ unsigned long last_nr_regions; + /* + * number of damon_split_some_regions() invocations for this context. + * Read damon_split_some_regions() for more details. + */ + unsigned long region_split_rotation; }; /* Get a random number in [@l, @r) using @ctx's lockless PRNG. */ diff --git a/mm/damon/core.c b/mm/damon/core.c index 09722257ce73..73b571b429cb 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -3753,12 +3753,13 @@ static void damon_split_regions_of(struct damon_ctx *ctx, static void damon_split_some_regions(struct damon_ctx *ctx, unsigned long split_step) { - static unsigned long rotation; struct damon_target *t; struct damon_region *r, *next; - unsigned long offset = rotation++ % split_step; + unsigned long offset; unsigned long idx = 0; + offset = ctx->region_split_rotation++ % split_step; + damon_for_each_target(t, ctx) { damon_for_each_region_safe(r, next, t) { unsigned long sz_region, sz_sub; From 740128dd1a3f2bd81a80314d079b6b03c39ba3af Mon Sep 17 00:00:00 2001 From: Chris Down Date: Mon, 26 Oct 2026 22:31:03 +0800 Subject: [PATCH 21/43] mm: make swapoff interruptible when unusing mms/shmem try_to_unuse() only checks for a pending signal between mms, and shmem_unuse() doesn't check at all. That means that once swapoff gets to a process or a shmem file with a lot swapped out, nothing can interrupt it until every last page of it has been read back in. Just as one example of where this can concretely show up, freezing tasks for suspend or hibernation has to wait for swapoff to notice the freezer's fake signal, and gives up after freeze_timeout_msecs (20 seconds by default). Here's a facetious example where one swaps out 2GiB of one process to a swap file on ext4, starts swapoff, and half a second later tries to freeze with pm_test=freezer. Writing to /sys/power/state then fails with EBUSY and this in dmesg: Freezing user space processes failed after 20.003 seconds (1 tasks refusing to freeze, wq_busy=0): task:swapoff state:D stack:0 pid:3175 tgid:3175 ppid:2955 task_flags:0x400100 flags:0x00000419 Call trace: [...] io_schedule+0x44/0x70 folio_wait_bit_common+0x1ec/0x3d0 __folio_lock+0x24/0x40 unuse_pte_range+0x2d0/0x348 unuse_vma+0x158/0x248 unuse_mm+0xfc/0x150 try_to_unuse+0x104/0x3f8 __do_sys_swapoff+0x220/0x5d8 [...] The same goes for anything else that wants swapoff to stop, like an admin hitting ^C in a panic, of course. Prior to commit b56a2d8af914 ("mm: rid swapoff of quadratic complexity") try_to_unuse() was driven by find_next_to_unuse() which checks for a signal before every entry, so let's restore that behaviour. Just as an example of the improvements, here's how long freezing takes in the same test while swapoff is happening on my computer: before after 400MiB anon 8.925s 0.028s 400MiB shmem 1.639s 0.003s 2GiB anon failed after 20.003s 0.011s Link: https://lore.kernel.org/at9kJwdGkHPsWwgG@chrisdown.name Fixes: b56a2d8af914 ("mm: rid swapoff of quadratic complexity") Signed-off-by: Chris Down Signed-off-by: Andrew Morton Acked-by: Vineeth Pillai (Google) Reviewed-by: Baolin Wang Reviewed-by: Kairui Song Cc: Hugh Dickins Cc: Chris Li Cc: Nhat Pham Cc: Baoquan He Cc: Barry Song Cc: Youngjun Park Cc: Ying Huang Cc: Kelley Nielsen Cc: Rafael J. Wysocki Cc: --- mm/shmem.c | 4 ++++ mm/swapfile.c | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/mm/shmem.c b/mm/shmem.c index af21a89abb0f..3c0c7edd2020 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -1742,6 +1742,10 @@ static int shmem_unuse_inode(struct inode *inode, unsigned int type) if (ret < 0) break; + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } start = indices[folio_batch_count(&fbatch) - 1]; } while (true); diff --git a/mm/swapfile.c b/mm/swapfile.c index 254ce86fa923..0f72a5b23a3a 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -2689,6 +2689,8 @@ static inline int unuse_pmd_range(struct vm_area_struct *vma, pud_t *pud, pmd = pmd_offset(pud, addr); do { cond_resched(); + if (signal_pending(current)) + return -ERESTARTSYS; next = pmd_addr_end(addr, end); ret = unuse_pte_range(vma, pmd, addr, next, type); if (ret) @@ -2922,7 +2924,7 @@ static int try_to_unuse(unsigned int type) if (swap_usage_in_pages(si)) { if (!signal_pending(current)) goto retry; - return -EINTR; + return -ERESTARTSYS; } success: From 7d8746d7dc9cc4003661011424e5880ea7ae364b Mon Sep 17 00:00:00 2001 From: Hungyu Lin Date: Mon, 26 Oct 2026 19:53:29 +0000 Subject: [PATCH 22/43] mm/migrate: untag addresses when querying page nodes When move_pages() is used with nodes == NULL, do_pages_stat_array() passes each page address directly to vma_lookup(). A valid tagged address therefore fails the VMA lookup and gets -EFAULT in its status entry, instead of the node reported for the corresponding untagged address. The tagged address ABI permits these pointers, and the migration path already removes their tags. Apply the target mm's untagging rules to the query path as well, while holding its mmap read lock. Link: https://lore.kernel.org/20261026195330.46757-1-dennylin0707@gmail.com Fixes: 057d3389108e ("mm: untag user pointers passed to memory syscalls") Signed-off-by: Hungyu Lin Signed-off-by: Andrew Morton Reported-by: Sashiko Closes: https://lore.kernel.org/r/20260721183257.706661F000E9@smtp.kernel.org Acked-by: Zi Yan Acked-by: David Hildenbrand (Arm) Reviewed-by: Catalin Marinas Cc: Matthew Brost Cc: Joshua Hahn Cc: Byungchul Park Cc: Gregory Price Cc: Ying Huang Cc: Alistair Popple Cc: Andrey Konovalov Cc: Vincenzo Frascino Cc: Cc: --- mm/migrate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/migrate.c b/mm/migrate.c index 7bdcdb57652f..d649a6cfce6c 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -2461,7 +2461,7 @@ static void do_pages_stat_array(struct mm_struct *mm, unsigned long nr_pages, mmap_read_lock(mm); for (i = 0; i < nr_pages; i++) { - unsigned long addr = (unsigned long)(*pages); + unsigned long addr = (unsigned long)untagged_addr_remote(mm, *pages); struct vm_area_struct *vma; struct folio_walk fw; struct folio *folio; From 990d6c2194a938a19a8cf94c6cfd75171345089b Mon Sep 17 00:00:00 2001 From: Lance Yang Date: Mon, 26 Oct 2026 21:00:54 +0800 Subject: [PATCH 23/43] mm/ksm: fix userfaultfd protection during merge and unmerge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit KSM loses userfaultfd protection when replace_page() builds a new PTE from vma->vm_page_prot. Dropping its uffd bit lets a subsequent write go through ordinary COW instead of userfaultfd write protection handling. For RWP, a merged page also becomes readable without faulting. Keeping the uffd bit and restoring PAGE_NONE fixes merging, but we also have to handle unmerging. An existing KSM page can be protected with RWP before MADV_UNMERGEABLE, so unmerging already needs fixing independently of preserving RWP during merging. Unsharing replaces a shared page with a private one and should leave userfaultfd protection in place, as do_wp_page() already does for write protection. Currently, an unshare fault reaches do_uffd_rwp() before it can proceed to do_wp_page(). - With synchronous RWP, handle_userfault() returns VM_FAULT_SIGBUS. break_ksm() then reports success with a KSM page still merged. - With asynchronous RWP, protection is cleared before unsharing. Preserve the uffd bit when merging, and restore PAGE_NONE for RWP. Let FAULT_FLAG_UNSHARE skip RWP fault handling and keep PAGE_NONE on each private copy as well. Unsharing can then complete with RWP still armed for a subsequent access. Link: https://lore.kernel.org/20261026130054.91461-1-lance.yang@linux.dev Link: https://lore.kernel.org/all/20261019010934.697895-1-samarth.colleges@gmail.com/ Link: https://lore.kernel.org/all/20261020055518.36087-1-lance.yang@linux.dev/ Fixes: 63b2d4174c4a ("userfaultfd: wp: add the writeprotect API to userfaultfd ioctl") Fixes: 2d427bb01639 ("mm/userfaultfd: add RWP fault delivery and expose UFFDIO_REGISTER_MODE_RWP") Signed-off-by: Samarth Narang Signed-off-by: Lance Yang Signed-off-by: Andrew Morton Co-developed-by: Samarth Narang Acked-by: Lorenzo Stoakes (ARM) Reviewed-by: Kiryl Shutsemau (Meta) Acked-by: David Hildenbrand (Arm) Cc: Mel Gorman Cc: Xu Xin " Cc: Chengming Zhou Cc: Liam R. Howlett Cc: Vlastimil Babka Cc: Mike Rapoport Cc: Suren Baghdasaryan Cc: Michal Hocko Cc: Jérôme Glisse Cc: Andrea Arcangeli Cc: Peter Xu Cc: Bobby Powers Cc: Brian Geffon Cc: Denis Plotnikov Cc: Doug Gilbert Cc: Hugh Dickins Cc: Johannes Weiner Cc: Martin Cracauer Cc: Marty McFadden Cc: Maya Gokhale Cc: Pavel Emelyanov Cc: Rik van Riel Cc: Shaohua Li Cc: James Houghton Cc: Jonathan Corbet Cc: Paolo Bonzini Cc: Sean Christopherson Cc: SJ Park Cc: Zi Yan Cc: Mohammed EL Kadiri Cc: --- mm/ksm.c | 8 +++++++- mm/memory.c | 14 ++++++++++---- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/mm/ksm.c b/mm/ksm.c index 910a1307ac9f..d6f736a1aa2f 100644 --- a/mm/ksm.c +++ b/mm/ksm.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -1463,9 +1464,14 @@ static int replace_page(struct vm_area_struct *vma, struct page *page, dec_mm_counter(mm, MM_ANONPAGES); } - /* Preserve soft-dirty when replacing the page. */ + /* Preserve soft-dirty and uffd protection when replacing the page. */ if (pte_soft_dirty(orig_pte)) newpte = pte_mksoft_dirty(newpte); + if (pte_uffd(orig_pte)) { + newpte = pte_mkuffd(newpte); + if (userfaultfd_rwp(vma)) + newpte = pte_modify(newpte, PAGE_NONE); + } flush_cache_page(vma, addr, pte_pfn(ptep_get(ptep))); /* diff --git a/mm/memory.c b/mm/memory.c index 916637e8381d..b65b56fe9714 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -4125,8 +4125,11 @@ static vm_fault_t wp_page_copy(struct vm_fault *vmf) if (unlikely(unshare)) { if (pte_soft_dirty(vmf->orig_pte)) entry = pte_mksoft_dirty(entry); - if (pte_uffd(vmf->orig_pte)) + if (pte_uffd(vmf->orig_pte)) { entry = pte_mkuffd(entry); + if (userfaultfd_rwp(vma)) + entry = pte_modify(entry, PAGE_NONE); + } } else { entry = maybe_mkwrite(pte_mkdirty(entry), vma); } @@ -6662,9 +6665,12 @@ static vm_fault_t handle_pte_fault(struct vm_fault *vmf) * VM_UFFD_RWP VMA, a protnone PTE without the uffd bit is * NUMA hinting and must still fall through to do_numa_page(). */ - if (userfaultfd_pte_rwp(vmf->vma, vmf->orig_pte)) - return do_uffd_rwp(vmf); - return do_numa_page(vmf); + if (userfaultfd_pte_rwp(vmf->vma, vmf->orig_pte)) { + if (!(vmf->flags & FAULT_FLAG_UNSHARE)) + return do_uffd_rwp(vmf); + } else { + return do_numa_page(vmf); + } } spin_lock(vmf->ptl); From fe623a81ad9eddf10059b4e44bca0440d9120613 Mon Sep 17 00:00:00 2001 From: Yafang Shao Date: Tue, 27 Oct 2026 10:17:20 +0800 Subject: [PATCH 24/43] mm/migrate: snapshot the source nid before the misplaced migration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A successful migration frees the source folio before the promotion counter update, so the folio_nid() read there operates on a freed folio — which may already have been reallocated. It happens to work today, as Zi explained: : Strictly speaking, it can be seen as a benign UAF. It works, because : 1. folio->flags happens to be the same as page->flags, : 2. folio/page nid is stored in the ->flags and never changed. : : In memdesc world, after a folio is freed, I assume folio->flags would be : zeroed since struct folio is freed and calling folio_nid() on a freed : folio would give a bogus result (yeah, it can be mitigated by not : zeroing node field and each node has its own struct folio pool, but that : makes folio_nid() implementation dependent). and Matthew explained further: : once struct folio is separately allocated, it's a UAF. It could be : recycled to a different NID or maybe the slab containing the folio could : be freed (assuming we're not holding the RCU read lock at this point in : migration, and I don't see why we would be). A separate concern is memory hot-removal. With memory-tiering mode enabled, successful migration can release the source before the promotion-accounting folio_nid() read. If the task is preempted there, an otherwise-free source block can be offlined and removed first. On the x86 path, removal can unmap the source's vmemmap, so the later read can fault. The snapshot avoids that read. Link: https://lore.kernel.org/20261027021721.95781-1-laoar.shao@gmail.com Fixes: e39bb6be9f2b ("NUMA Balancing: add page promotion counter") Signed-off-by: Yafang Shao Signed-off-by: Andrew Morton Suggested-by: Zi Yan Suggested-by: Andrew Morton Acked-by: Zi Yan Acked-by: David Hildenbrand (Arm) Cc: Ying Huang Cc: Matthew Wilcox Cc: Matthew Brost Cc: Joshua Hahn Cc: Byungchul Park Cc: Gregory Price Cc: Alistair Popple Cc: --- mm/migrate.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mm/migrate.c b/mm/migrate.c index d649a6cfce6c..875079464930 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -2756,6 +2756,7 @@ int migrate_misplaced_folio(struct folio *folio, int node) pg_data_t *pgdat = NODE_DATA(node); int nr_remaining; unsigned int nr_succeeded; + int src_node = folio_nid(folio); LIST_HEAD(migratepages); struct mem_cgroup *memcg = get_mem_cgroup_from_folio(folio); struct lruvec *lruvec = mem_cgroup_lruvec(memcg, pgdat); @@ -2770,7 +2771,7 @@ int migrate_misplaced_folio(struct folio *folio, int node) count_vm_numa_events(NUMA_PAGE_MIGRATE, nr_succeeded); count_memcg_events(memcg, NUMA_PAGE_MIGRATE, nr_succeeded); if ((sysctl_numa_balancing_mode & NUMA_BALANCING_MEMORY_TIERING) - && !node_is_toptier(folio_nid(folio)) + && !node_is_toptier(src_node) && node_is_toptier(node)) mod_lruvec_state(lruvec, PGPROMOTE_SUCCESS, nr_succeeded); } From 168ed9c5d5ec2fd326715eba5750c2b69e559ff3 Mon Sep 17 00:00:00 2001 From: "Lorenzo Stoakes (ARM)" Date: Tue, 27 Oct 2026 11:25:20 +0000 Subject: [PATCH 25/43] mm/vma: update VMA iterator state on error in new VMA merge Commit cacded5e42b9 ("mm: avoid using vma_merge() for new VMAs") shared code between vma_merge_new_range() and vma_expand(). However in doing so, it failed to correctly restore the VMA iterator when vma_expand() errors out. This is a problem when the immediately preceding VMA is mergeable and vmg->just_expand isn't specified, at which point the VMA iterator is moved to the previous VMA. (Note that vmg->just_expand is a special case for brk(), where the iterator will already be positioned at the VMA to be expanded.) Resolve things by restoring the VMA iterator on error. Use vma_iter_next_range() to do this, so it will correctly stop on the gap after the previous VMA, rather than skip to the next VMA after it (this is a new VMA merge, so is being applied at a gap). To isolate the error handling, return early when a merge isn't possible (what the vmg->target check was doing before), then handle the merge success/failure case explicitly. While here, update vma_expand() to jump to the nomem case when dup_anon_vma() fails, so vmg->state is correctly updated. This doesn't impact anything in practice, but is the correct course and since it's part of the error path that this patch addresses, it makes sense to update it also. Users are very unlikely to have observed the issue this patch addresses in practice, as it is near-impossible to fail an allocation for an anon_vma, but a process under memory pressure that receives a fatal signal could trigger it. This issue was originally reported by Li Jinyue for which I am grateful. He indicated this was uncovered by a private syzkaller instance. Link: https://lore.kernel.org/20261027-fix-new-merge-bug-v1-1-7023a787e6b9@kernel.org Fixes: cacded5e42b9 ("mm: avoid using vma_merge() for new VMAs") Signed-off-by: Lorenzo Stoakes (ARM) Signed-off-by: Andrew Morton Reported-by: Li Jinyue Closes: https://lore.kernel.org/all/20261025093921.1011927-1-jinyue.li@linux.alibaba.com/ Cc: Liam R. Howlett Cc: Vlastimil Babka Cc: Jann Horn Cc: Pedro Falcato Cc: --- mm/vma.c | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/mm/vma.c b/mm/vma.c index af8e65ada23b..d416ba58eb4c 100644 --- a/mm/vma.c +++ b/mm/vma.c @@ -1141,6 +1141,7 @@ struct vm_area_struct *vma_merge_new_range(struct vma_merge_struct *vmg) struct vm_area_struct *next = vmg->next; unsigned long end = vmg->end; bool can_merge_left, can_merge_right; + bool move_iter_back = false; mmap_assert_write_locked(vmg->mm); VM_WARN_ON_VMG(vmg->middle, vmg); @@ -1182,23 +1183,30 @@ struct vm_area_struct *vma_merge_new_range(struct vma_merge_struct *vmg) vmg->end = end; /* In expand-only case we are already positioned at prev. */ - if (!vmg->just_expand) { - /* Equivalent to going to the previous range. */ - vma_prev(vmg->vmi); - } + if (!vmg->just_expand) + move_iter_back = true; } + if (!can_merge_left && !can_merge_right) + return NULL; + + if (move_iter_back) + vma_prev(vmg->vmi); + /* * Now try to expand adjacent VMA(s). This takes care of removing the * following VMA if we have VMAs on both sides. */ - if (vmg->target && !vma_expand(vmg)) { - khugepaged_enter_vma(vmg->target, vmg->vm_flags); - vmg->state = VMA_MERGE_SUCCESS; - return vmg->target; + if (vma_expand(vmg)) { + /* Error occurred, restore iterator. */ + if (move_iter_back) + vma_iter_next_range(vmg->vmi); + return NULL; } - return NULL; + khugepaged_enter_vma(vmg->target, vmg->vm_flags); + vmg->state = VMA_MERGE_SUCCESS; + return vmg->target; } /* @@ -1288,7 +1296,7 @@ int vma_expand(struct vma_merge_struct *vmg) if (!ret && vmg->copied_from) ret = dup_anon_vma(target, vmg->copied_from, &anon_dup); if (ret) - return ret; + goto nomem; if (remove_next) { vma_start_write(next); From 1b8fd9a23501bffefa3304ff36f4674b26838c67 Mon Sep 17 00:00:00 2001 From: "Zenghui Yu (Huawei)" Date: Tue, 27 Oct 2026 20:20:13 +0800 Subject: [PATCH 26/43] selftests/mm: fix khugepaged_race build with older glibc glibc versions before 2.37 do not define MADV_COLLAPSE in , so khugepaged_race fails to build against them (e.g. glibc 2.34): [root@localhost mm]# make CC khugepaged_race khugepaged_race.c: In function 'main': khugepaged_race.c:506:23: error: 'MADV_COLLAPSE' undeclared (first use in this function); did you mean 'MADV_COLD'? 506 | hpage_pmd_size, MADV_COLLAPSE); | ^~~~~~~~~~~~~ | MADV_COLD khugepaged_race.c:506:23: note: each undeclared identifier is reported only once for each function it appears in make: *** [../lib.mk:225: /data/code/mm/tools/testing/selftests/mm/khugepaged_race] Error 1 Include to get the UAPI definition of MADV_COLLAPSE. Link: https://lore.kernel.org/20261027122013.4286-1-zenghui.yu@linux.dev Fixes: 66c374464438 ("selftests/mm: add khugepaged race harness") Signed-off-by: Zenghui Yu (Huawei) Signed-off-by: Andrew Morton Acked-by: Lorenzo Stoakes (ARM) Reviewed-by: Kiryl Shutsemau (Meta) Cc: David Hildenbrand Cc: Liam R. Howlett Cc: Vlastimil Babka Cc: Mike Rapoport Cc: Suren Baghdasaryan Cc: Michal Hocko --- tools/testing/selftests/mm/khugepaged_race.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/testing/selftests/mm/khugepaged_race.c b/tools/testing/selftests/mm/khugepaged_race.c index abb0678522cd..1a27cb39745b 100644 --- a/tools/testing/selftests/mm/khugepaged_race.c +++ b/tools/testing/selftests/mm/khugepaged_race.c @@ -8,6 +8,7 @@ #define _GNU_SOURCE #include #include +#include #include #include #include From ee1afc5539b6fe03569a5855b6041b13343b0993 Mon Sep 17 00:00:00 2001 From: "Lorenzo Stoakes (ARM)" Date: Tue, 27 Oct 2026 14:08:26 +0000 Subject: [PATCH 27/43] fs/proc/task_mmu: fix missed VMAs when reading /proc/PID/maps and friends Reading /proc/PID/maps under per-VMA locks can skip continuously mapped address ranges during concurrent VMA splits or merges. This violates the guarantee in Documentation/filesystems/proc.rst: "If there is something at a given vaddr during the entirety of the life of the smaps/maps walk, there will be some output for it." lock_next_vma() reuses a tree iterator across calls under RCU. The iterator remembers the range of its last entry. When its node is replaced, mas_next_slot() walks the tree again at the last index of that range, then advances past the entry containing that index. If VMA boundaries changed, this can skip a range that has not been reported: - A lookup finds [A, C), then mprotect() splits it into [A, B) and [B, C) before vma_start_read() locks the first VMA. The reader reports [A, B), but the iterator still covers [A, C). The next call starts from B yet advances past C, omitting [B, C). - The reader reports [A, B) and unlocks it. A concurrent mprotect() then merges it with [B, C). On the next call, the iterator resumes at B - 1, which now belongs to [A, C), and advances past C. Again, [B, C) is lost. Checking the mmap_lock sequence only within the next call misses writers that completed before that call began. Also, a NULL result is currently accepted without validation, so a race can terminate the walk early. Even a fresh lookup can skip a mapping. Suppose it finds a gap before [B, C). A writer inserts an adjacent VMA at the end of that gap, then merges it into [B, C), replacing the node. The lookup resumes at the old gap's last index, now inside the merged VMA, and steps past it. The original [B, C) remains mapped throughout. Repeating the forward lookup is insufficient: another merge can make it skip the same mapping again, returning the same later VMA or NULL. The fresh-lookup race also affects /proc/PID/smaps, numa_maps and smaps_rollup. Resetting the iterator after gathering each VMA's statistics does not prevent a merge during the next lookup. Fixing lock_next_vma() also prevents omitted entries and incomplete rollup statistics. Tie VMA iterator state with the current mm and the write generation at the point of last mm speculation in vma_rcu_iter, whose values we call 'rvmi'. This allows us to neatly abstract the existing priv->iter logic alongside query_vma_find_by_addr() which uses its own VMA iterator. Then, provide helpers that act on the rvmi - vma_rcu_iter_[maybe_changed, may_have_skipped]() to determine whether any VMAs may have been altered since last mm speculation. Additionally introduce vma_range_is_empty(), used by vma_rcu_iter_may_have_skipped(), to determine conclusively whether VMAs exist in a set range, needing to be traversed. Original fix generated by an LLM and sent by Josef Bacik in private, then amended by David Hildenbrand, and Lorenzo Stoakes added additional abstraction. Link: https://lore.kernel.org/20261027-fix-proc-pid-maps-missing-vmas-v2-1-2791cc9fc95f@kernel.org Fixes: 5631da56c9a8 ("fs/proc/task_mmu: read proc/pid/maps under per-vma lock") Fixes: ea3085dd7a4e ("fs/proc/task_mmu: read proc/pid/{smaps|numa_maps} under per-vma lock") Fixes: 0e985559d72a ("proc/task_mmu: read proc/pid/smaps_rollup under per-vma lock") Fixes: d9d1c2d81797 ("fs/proc/task_mmu: execute PROCMAP_QUERY ioctl under per-vma locks") Signed-off-by: David Hildenbrand (Arm) Signed-off-by: Lorenzo Stoakes (ARM) Signed-off-by: Andrew Morton Co-developed-by: David Hildenbrand (Arm) Reviewed-by: Suren Baghdasaryan Reviewed-by: Jose A. Perez de Azpillaga Tested-by: Jose A. Perez de Azpillaga Cc: Liam R. Howlett Cc: Vlastimil Babka Cc: Jann Horn Cc: Pedro Falcato Cc: Shakeel Butt Cc: SJ Park Cc: --- fs/proc/internal.h | 1 + fs/proc/task_mmu.c | 13 +++++---- include/linux/mmap_lock.h | 22 ++++++++++++-- mm/mmap_lock.c | 61 +++++++++++++++++++++++++++++---------- 4 files changed, 72 insertions(+), 25 deletions(-) diff --git a/fs/proc/internal.h b/fs/proc/internal.h index df873df9afc5..9af1f63258b7 100644 --- a/fs/proc/internal.h +++ b/fs/proc/internal.h @@ -393,6 +393,7 @@ struct proc_maps_private { struct inode *inode; struct task_struct *task; struct vma_iterator iter; + struct vma_rcu_iter rvmi; loff_t last_pos; struct proc_maps_locking_ctx lock_ctx; #ifdef CONFIG_NUMA diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index f68673a91756..b87d3e86442d 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -160,7 +160,7 @@ static struct vm_area_struct *get_next_vma(struct proc_maps_private *priv, return vma_next(&priv->iter); unlock_ctx_vma(lock_ctx); - vma = lock_next_vma(lock_ctx->mm, &priv->iter, last_pos); + vma = lock_next_vma(&priv->rvmi, last_pos); if (!IS_ERR_OR_NULL(vma)) lock_ctx->locked_vma = vma; @@ -265,7 +265,7 @@ static void *m_start(struct seq_file *m, loff_t *ppos) */ if (last_addr > 0) *ppos = last_addr = priv->last_pos; - vma_iter_init(&priv->iter, mm, (unsigned long)last_addr); + vma_rcu_iter_init(&priv->rvmi, mm, &priv->iter, (unsigned long)last_addr); hold_task_mempolicy(priv); /* * If seq_file had to flush its collected data right after m_next() set @@ -515,6 +515,7 @@ static struct vm_area_struct *query_vma_find_by_addr(struct proc_maps_locking_ct struct mm_struct *mm = lock_ctx->mm; struct vm_area_struct *vma; struct vma_iterator vmi; + struct vma_rcu_iter rvmi; if (lock_ctx->mmap_locked) return find_vma(mm, addr); @@ -522,8 +523,8 @@ static struct vm_area_struct *query_vma_find_by_addr(struct proc_maps_locking_ct /* Unlock previously locked VMA and find the next one under RCU */ unlock_ctx_vma(lock_ctx); rcu_read_lock(); - vma_iter_init(&vmi, mm, addr); - vma = lock_next_vma(mm, &vmi, addr); + vma_rcu_iter_init(&rvmi, mm, &vmi, addr); + vma = lock_next_vma(&rvmi, addr); rcu_read_unlock(); if (!vma) @@ -1416,7 +1417,7 @@ static int show_smaps_rollup(struct seq_file *m, void *v) rcu_read_lock(); reset_lock_ctx(lock_ctx); - vma_iter_init(&priv->iter, mm, 0); + vma_rcu_iter_init(&priv->rvmi, mm, &priv->iter, 0); vma = proc_get_vma(m, &pos); if (unlikely(!vma)) goto empty_set; @@ -1465,7 +1466,7 @@ static int show_smaps_rollup(struct seq_file *m, void *v) reset_lock_ctx(lock_ctx); /* Resume from the last position. */ pos = last_vma_end; - vma_iter_init(&priv->iter, mm, pos); + vma_rcu_iter_init(&priv->rvmi, mm, &priv->iter, pos); } vma = proc_get_vma(m, &pos); } diff --git a/include/linux/mmap_lock.h b/include/linux/mmap_lock.h index 11b3ffef3966..9ccbbcbd7aca 100644 --- a/include/linux/mmap_lock.h +++ b/include/linux/mmap_lock.h @@ -17,6 +17,13 @@ extern int rcuwait_wake_up(struct rcuwait *w); #define MMAP_LOCK_INITIALIZER(name) \ .mmap_lock = __RWSEM_INITIALIZER((name).mmap_lock), +/* Tie a VMA iterator to mm write lock state at iterator initialisation. */ +struct vma_rcu_iter { + struct mm_struct *mm; + struct vma_iterator *vmi; + unsigned int mm_wr_seq; +}; + DECLARE_TRACEPOINT(mmap_lock_start_locking); DECLARE_TRACEPOINT(mmap_lock_acquire_returned); DECLARE_TRACEPOINT(mmap_lock_released); @@ -509,6 +516,16 @@ static inline void vma_mark_detached(struct vm_area_struct *vma) struct vm_area_struct *lock_vma_under_rcu(struct mm_struct *mm, unsigned long address); +static inline void vma_rcu_iter_init(struct vma_rcu_iter *rvmi, + struct mm_struct *mm, struct vma_iterator *vmi, + unsigned long addr) +{ + vma_iter_init(vmi, mm, addr); + rvmi->mm = mm; + rvmi->vmi = vmi; + rvmi->mm_wr_seq = UINT_MAX; /* Start odd so changed by default. */ +} + /* * Locks next vma pointed by the iterator. Confirms the locked vma has not * been modified and will retry under mmap_lock protection if modification @@ -516,9 +533,8 @@ struct vm_area_struct *lock_vma_under_rcu(struct mm_struct *mm, * Returns either a valid locked VMA, NULL if no more VMAs or -EINTR if the * process was interrupted. */ -struct vm_area_struct *lock_next_vma(struct mm_struct *mm, - struct vma_iterator *iter, - unsigned long address); +struct vm_area_struct *lock_next_vma(struct vma_rcu_iter *rvmi, + unsigned long from_addr); static inline void vma_assert_can_modify(struct vm_area_struct *vma) { diff --git a/mm/mmap_lock.c b/mm/mmap_lock.c index 83b7d3ded0dc..834bbf86e004 100644 --- a/mm/mmap_lock.c +++ b/mm/mmap_lock.c @@ -13,6 +13,8 @@ #include #include +#include "internal.h" + EXPORT_TRACEPOINT_SYMBOL(mmap_lock_start_locking); EXPORT_TRACEPOINT_SYMBOL(mmap_lock_acquire_returned); EXPORT_TRACEPOINT_SYMBOL(mmap_lock_released); @@ -428,21 +430,53 @@ static struct vm_area_struct *lock_next_vma_under_mmap_lock(struct mm_struct *mm return vma; } -struct vm_area_struct *lock_next_vma(struct mm_struct *mm, - struct vma_iterator *vmi, +/* Is there no VMA in [start, last]? */ +static bool vma_range_is_empty(struct mm_struct *mm, unsigned long start, + unsigned long last) +{ + VMA_ITERATOR(vmi, mm, start); + + return !vma_iter_load(&vmi) && vma_iter_end(&vmi) - 1 >= last; +} + +/* Might the VMA tree have been modified since last mm speculation? */ +static bool vma_rcu_iter_maybe_changed(const struct vma_rcu_iter *rvmi) +{ + return (rvmi->mm_wr_seq & 1) || /* Odd implies write-lock. */ + mmap_lock_speculate_retry(rvmi->mm, rvmi->mm_wr_seq); +} + +/* Could the VMA iterator have skipped a VMA due to the tree changing? */ +static bool vma_rcu_iter_may_have_skipped(const struct vma_rcu_iter *rvmi, + unsigned long from, unsigned long to) +{ + return vma_rcu_iter_maybe_changed(rvmi) && + !vma_range_is_empty(rvmi->mm, from, to); +} + +struct vm_area_struct *lock_next_vma(struct vma_rcu_iter *rvmi, unsigned long from_addr) { + struct mm_struct *mm = rvmi->mm; + struct vma_iterator *vmi = rvmi->vmi; struct vm_area_struct *vma; - unsigned int mm_wr_seq; - bool mmap_unlocked; RCU_LOCKDEP_WARN(!rcu_read_lock_held(), "no rcu read lock held"); retry: - /* Start mmap_lock speculation in case we need to verify the vma later */ - mmap_unlocked = mmap_lock_speculate_try_begin(mm, &mm_wr_seq); + /* Only trust the iterator's position if the tree cannot have changed. */ + if (vma_rcu_iter_maybe_changed(rvmi)) { + vma_iter_set(vmi, from_addr); + mmap_lock_speculate_try_begin(mm, &rvmi->mm_wr_seq); + } vma = vma_next(vmi); - if (!vma) - return NULL; + if (!vma) { + /* No reason to fallback if nothing left in range. */ + if (!vma_rcu_iter_may_have_skipped(rvmi, from_addr, ULONG_MAX)) + return NULL; + + rcu_read_unlock(); + goto fallback; + } vma = vma_start_read(mm, vma); if (IS_ERR_OR_NULL(vma)) { @@ -471,14 +505,9 @@ struct vm_area_struct *lock_next_vma(struct mm_struct *mm, * installed ahead of it. Otherwise we might observe a gap that should * not be there. */ - if (from_addr < vma->vm_start) { - /* Verify only if the address space might have changed since vma lookup. */ - if (!mmap_unlocked || mmap_lock_speculate_retry(mm, mm_wr_seq)) { - vma_iter_set(vmi, from_addr); - if (vma != vma_next(vmi)) - goto fallback_unlock; - } - } + if (from_addr < vma->vm_start && + vma_rcu_iter_may_have_skipped(rvmi, from_addr, vma->vm_start - 1)) + goto fallback_unlock; return vma; From 0120a603afadba8ab4954bb1ce60f8195f80efc8 Mon Sep 17 00:00:00 2001 From: "Kiryl Shutsemau (Meta)" Date: Wed, 28 Oct 2026 13:26:21 +0000 Subject: [PATCH 28/43] mm/hugetlb_vmemmap: map the shared tail page writable page_ref_add_unless_zero() reads the refcount and then updates it with a cmpxchg. A lockless get, such as a page cache lookup or a PFN scanner, can read a non-zero refcount from a page that is then freed and reused as a hugetlb tail page. If HVO remaps its struct page to the shared tail page in between, the cmpxchg writes to read-only vmemmap: CPU0 (lockless get) CPU1 c = atomic_read(&page->_refcount) page freed page allocated as a hugetlb tail HVO remaps the struct page RO try_cmpxchg(&page->_refcount, &c, c + 1) -> write fault on read-only vmemmap The refcount is zero by then and the compare fails, but a compare-and-swap instruction needs write access whether or not it succeeds. The kernel oopses when hugetlb pages are allocated at runtime with HVO enabled while a lockless get races with it. Commit bd225530a4c7 ("mm/hugetlb_vmemmap: fix race with speculative PFN walkers") closed the window with RCU on both sides. The RCU was removed together with fake heads, on the wrong belief that it was only needed for them. Map the shared tail page writable instead of restoring the RCU. Every struct page on it is a tail with a zero refcount, so a late cmpxchg fails and leaves the page unchanged. RCU would put rcu_read_lock() back around every get and synchronize_rcu() back into hugetlb allocation and freeing. The cost is the protection against stray writes to tail struct pages. The shared page is shared by all hugetlb folios of the same order in the zone, so such a write now corrupts all of them instead of oopsing. Bootmem folios optimized when the memmap is set up keep their tails read-only. Nothing does a lockless get on them before hugetlb takes them over. Link: https://lore.kernel.org/20261028132623.2560146-1-kirill@shutemov.name Fixes: 32c440d67e6c ("mm: drop fake head checks") Fixes: 01b1d0ffb6f7 ("hugetlb: remove VMEMMAP_SYNCHRONIZE_RCU") Signed-off-by: Kiryl Shutsemau (Meta) Signed-off-by: Andrew Morton Reported-by: David Hildenbrand (Arm) Closes: https://lore.kernel.org/r/ebc7bde1-f52e-4da8-b16f-822d6c0a4d74@kernel.org Acked-by: Muchun Song Reviewed-by: Oscar Salvador Cc: Peter Zijlstra Cc: Frank van der Linden Cc: Matthew Wilcox (Oracle) Cc: Lorenzo Stoakes Cc: Liam R. Howlett Cc: Vlastimil Babka Cc: Mike Rapoport Cc: Suren Baghdasaryan Cc: Michal Hocko Cc: Jonathan Corbet Cc: Shuah Khan Cc: Randy Dunlap Cc: --- Documentation/mm/vmemmap_dedup.rst | 15 +++++++++++++-- mm/hugetlb_vmemmap.c | 8 +++++--- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/Documentation/mm/vmemmap_dedup.rst b/Documentation/mm/vmemmap_dedup.rst index 8c287ae3f86c..5e792b7a27f0 100644 --- a/Documentation/mm/vmemmap_dedup.rst +++ b/Documentation/mm/vmemmap_dedup.rst @@ -130,8 +130,19 @@ pages of ``struct page`` (page 1 to page 7) are tail pages. The optimization is only applied when the size of the struct page is a power of 2. In this case, all tail pages of the same order are identical. See compound_head(). This allows us to remap the tail pages of the vmemmap to a -shared, read-only page. The head page is also remapped to a new page. This -allows the original vmemmap pages to be freed. +shared page. The head page is also remapped to a new page. This allows the +original vmemmap pages to be freed. + +The shared page is mapped writable. A lockless get of a page reference can +read a non-zero refcount before the remap and attempt its atomic update +after it. The refcount of a tail page is zero, so the update fails without +changing the page. Nothing else may write to the shared page: it is shared +by all HugeTLB pages of the same size in the zone, and a stray write would +corrupt all of them. + +HugeTLB pages whose vmemmap is optimized when the memmap is first set up at +boot keep a read-only mapping of the shared page. Nothing can do a lockless +get on them before they are handed to HugeTLB. Here is how things look after remapping:: diff --git a/mm/hugetlb_vmemmap.c b/mm/hugetlb_vmemmap.c index 0057fa2a16a8..9aa8af5c8856 100644 --- a/mm/hugetlb_vmemmap.c +++ b/mm/hugetlb_vmemmap.c @@ -222,10 +222,12 @@ static void vmemmap_remap_pte(pte_t *pte, unsigned long addr, VM_WARN_ON_ONCE(!PageTail((const struct page *)addr)); /* - * Remap the tail pages as read-only to catch illegal write - * operation to the tail pages. + * Map the shared tail page writable. A lockless get that read + * a non-zero refcount before the remap can still do its + * cmpxchg after it. The refcount is zero, so the cmpxchg + * fails and leaves the page unchanged. */ - entry = mk_pte(walk->vmemmap_tail, PAGE_KERNEL_RO); + entry = mk_pte(walk->vmemmap_tail, PAGE_KERNEL); } list_add(&page->lru, walk->vmemmap_pages); From 6ee3d87b940848aaed5d4e8d005c5f674bf2637a Mon Sep 17 00:00:00 2001 From: Johannes Weiner Date: Wed, 28 Oct 2026 13:15:53 -0400 Subject: [PATCH 29/43] mm: page_alloc: do not give all non-blocking requests reserve access Patch series "mm: page_alloc: fix reserve access for non-blocking requests", v2. Two fixes for how non-blocking allocations interact with the highatomic reserve. There are prerequisites for Salvatore's "mm/page_alloc: avoid direct reclaim and compaction for costly __GFP_NORETRY allocations" [0]. Patch 1 narrows which requests may physically take highatomic pages, patch 2 narrows which requests may count them in their watermark check. Both are prerequisites for Salvatore's patch: it clears __GFP_DIRECT_RECLAIM for costly __GFP_NORETRY requests, which turns every large page cache folio allocation that reaches the slowpath into a non-blocking one. Without patch 1, those requests may fall back to the highatomic freelists reserved for atomic requests. Without patch 2, they count the free highatomic pages in their watermark check while being served from regular memory, so they can consume regular high-order blocks below the min watermark by the size of the reserve. GFP_NOWAIT requests hit both paths today, so these are existing problems worthy of their own stable backports; but Salvatore's patch would widen them significantly. Patch 2 reproduction: a GFP_NOWAIT burst under memory pressure can drain regular free memory to zero while the highatomic reserve sits idle. Measured on a 32G box with an anonymous working set, swap, and a full 290M reserve, three synthetic bursts drove the 28G Normal zone's regular free memory to 28M, 0.8M and 0.6M against a 60M min watermark. Swapout failed its swap table allocation inside reclaim, reclaim scanned 13M pages to free 200k, and page faults stalled for tens to hundreds of milliseconds. With the fix the burst stops at the min watermark and nothing else happens. This was found in review and reproduced synthetically; it has not been observed in production. GFP_ATOMIC order-0 requests have a milder version of the same problem: They are entitled to the reserve but are served regular memory first, so the fix there has to change the watermark check and the freelist order together. Salvatore's change doesn't interact with this, though; fixing that is left for a separate patch. This patch (of 2): 281dd25c1a01 ("mm/page_alloc: let GFP_ATOMIC order-0 allocs access highatomic reserves") accidentally gave all non-blocking allocation requests access to highatomic reserves, which includes GFP_NOWAIT and other sites clearing __GFP_DIRECT_RECLAIM. While this improved atomic request success rate, it's unintentionally broad and can actually worsen highatomic requests by squandering the reserves on requests that don't need it. There are concurrent efforts to clear __GFP_DIRECT_RECLAIM altogether for costly order __GFP_NORETRY requests, which would then also fall into this exemption and deplete reserves even faster. What GFP_ATOMIC has over the others is __GFP_HIGH, which alloc_flags_slowpath() translates to ALLOC_MIN_RESERVE. Narrow the exemption to contexts that have both ALLOC_NON_BLOCK | ALLOC_MIN_RESERVE. Link: https://lore.kernel.org/20261001082152.2879289-1-dipiets@amazon.it [0] Link: https://lore.kernel.org/linux-mm/arFBWvzXPYrKuIYi@cmpxchg.org/ [1] Link: https://lore.kernel.org/linux-mm/20261001082152.2879289-1-dipiets@amazon.it/ [2] Link: https://lore.kernel.org/20261028171719.2043421-1-hannes@cmpxchg.org Link: https://lore.kernel.org/20261028171719.2043421-2-hannes@cmpxchg.org Fixes: 281dd25c1a01 ("mm/page_alloc: let GFP_ATOMIC order-0 allocs access highatomic reserves") Signed-off-by: Johannes Weiner Signed-off-by: Andrew Morton Reported-by: Sashiko Closes: https://sashiko.dev/#/patchset/20260904115629.3993331-1-dipiets@amazon.it Acked-by: Balbir Singh Reviewed-by: Vlastimil Babka (SUSE) Cc: Matthew Wilcox (Oracle) Cc: Salvatore Dipietro Cc: Christoph Hellwig Cc: Dave Chinner Cc: Matt Fleming Cc: Mel Gorman Cc: Michal Hocko Cc: Suren Baghdasaryan Cc: Brendan Jackman Cc: Zi Yan Cc: --- mm/page_alloc.c | 4 +++- mm/page_alloc.h | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 2b2855300217..de0b3acf3e50 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -3241,7 +3241,9 @@ struct page *rmqueue_buddy(struct zone *preferred_zone, struct zone *zone, * reserves as failing now is worse than failing a * high-order atomic allocation in the future. */ - if (!page && (alloc_flags & (ALLOC_OOM|ALLOC_NON_BLOCK))) + if (!page && + ((alloc_flags & ALLOC_OOM) || + (alloc_flags & ALLOC_MASK_ATOMIC) == ALLOC_MASK_ATOMIC)) page = __rmqueue_smallest(zone, order, MIGRATE_HIGHATOMIC); if (!page) { diff --git a/mm/page_alloc.h b/mm/page_alloc.h index e238ee91ed24..9c3c2830acb0 100644 --- a/mm/page_alloc.h +++ b/mm/page_alloc.h @@ -60,6 +60,9 @@ /* Flags that allow allocations below the min watermark. */ #define ALLOC_RESERVES (ALLOC_NON_BLOCK|ALLOC_MIN_RESERVE|ALLOC_HIGHATOMIC|ALLOC_OOM) +/* Flags that mean GFP_ATOMIC */ +#define ALLOC_MASK_ATOMIC (ALLOC_NON_BLOCK|ALLOC_MIN_RESERVE) + /* * Structure for holding the mostly immutable allocation parameters passed * between functions involved in allocations, including the alloc_pages* From fe21f930d1e64d121191673076fc4ebe79fc82d5 Mon Sep 17 00:00:00 2001 From: Johannes Weiner Date: Wed, 28 Oct 2026 13:15:54 -0400 Subject: [PATCH 30/43] mm: page_alloc: remove ALLOC_NON_BLOCK from ALLOC_RESERVES 1ebbb21811b7 ("mm/page_alloc: explicitly define how __GFP_HIGH non-blocking allocations accesses reserves") stopped handing out reserve access for ALLOC_NON_BLOCK on its own: the extra 25% below the min watermark is now only granted on top of ALLOC_MIN_RESERVE. But the flag was left in ALLOC_RESERVES, which produces something odd: With the ALLOC_RESERVES match, __zone_watermark_unusable_free() doesn't subtract the free highatomic pages for them. So in the slowpath, they get to consume regular blocks below the min watermark by the number of free highatomic pages. The highatomic reserve is capped at 1% of the zone, which on any decently sized machine is a multiple of the min watermark: GFP_NOWAIT can drain regular memory to zero. The user-visible result is brutal hiccups during bursts of GFP_NOWAIT allocations under memory pressure. This was found in review and reproduced with a synthetic burst, not observed in production, but the sequence of events is a realistic one: On a 32G box with an anonymous working set, swap, and a filled 290M highatomic reserve, a GFP_NOWAIT burst drove regular free memory in the 28G Normal zone (min=60M) to 28M, 0.8M and 0.6M in three runs. Swapout failed to allocate its swap table, reclaim scanned 13M pages to reclaim 200k, page faults stalled for tens to hundreds of milliseconds. The machine survives it, but not by design: direct reclaimers eventually fail and start unreserving highatomic blocks, until the allocation succeeds or the reserve is gone and the OOM killer runs. That reserve exists for high-order atomic allocations; here it is destroyed to bail out a GFP_NOWAIT consumer that was never entitled to the memory. Remove ALLOC_NON_BLOCK from ALLOC_RESERVES. With that, the GFP_NOWAIT burst is stopped short at the min watermark. No direct reclaim, no stalls, no failed allocations, and the highatomic reserve stays intact for the requests it exists for. __zone_watermark_ok() is unaffected, since everything it keys on ALLOC_NON_BLOCK is already nested under ALLOC_MIN_RESERVE. Update the flag comments accordingly: ALLOC_NON_BLOCK just means the caller can't block; the reserve math belongs with ALLOC_MIN_RESERVE. GFP_ATOMIC order-0 requests have a milder version of the same problem. Through ALLOC_MIN_RESERVE they also count the free highatomic pages in their watermark check, but rmqueue_buddy() serves them regular pages first and only falls back to the reserve once those are gone. They can still push regular free memory to 37.5% of min minus the free highatomic pages before the reserve is touched. Unlike GFP_NOWAIT they are entitled to the reserve, so this needs the watermark check and the freelist order reworked together, and is left to a separate patch. Link: https://lore.kernel.org/20261028171719.2043421-3-hannes@cmpxchg.org Fixes: 1ebbb21811b7 ("mm/page_alloc: explicitly define how __GFP_HIGH non-blocking allocations accesses reserves") Signed-off-by: Johannes Weiner Signed-off-by: Andrew Morton Acked-by: Balbir Singh Reviewed-by: Vlastimil Babka (SUSE) Cc: Matthew Wilcox (Oracle) Cc: Salvatore Dipietro Cc: Christoph Hellwig Cc: Dave Chinner Cc: Matt Fleming Cc: Mel Gorman Cc: Michal Hocko Cc: Suren Baghdasaryan Cc: Brendan Jackman Cc: Zi Yan Cc: --- mm/page_alloc.h | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/mm/page_alloc.h b/mm/page_alloc.h index 9c3c2830acb0..7097ea1805f6 100644 --- a/mm/page_alloc.h +++ b/mm/page_alloc.h @@ -32,12 +32,11 @@ #define ALLOC_OOM ALLOC_NO_WATERMARKS #endif -#define ALLOC_NON_BLOCK 0x10 /* Caller cannot block. Allow access - * to 25% of the min watermark or - * 62.5% if __GFP_HIGH is set. - */ +#define ALLOC_NON_BLOCK 0x10 /* Caller cannot block. */ #define ALLOC_MIN_RESERVE 0x20 /* __GFP_HIGH set. Allow access to 50% - * of the min watermark. + * of the min watermark, or 62.5% if + * the caller cannot block either + * (ALLOC_NON_BLOCK). */ #define ALLOC_CPUSET 0x40 /* check for correct cpuset */ #define ALLOC_CMA 0x80 /* allow allocations from CMA areas */ @@ -58,7 +57,7 @@ #define ALLOC_NO_CODETAG 0x1000 /* Flags that allow allocations below the min watermark. */ -#define ALLOC_RESERVES (ALLOC_NON_BLOCK|ALLOC_MIN_RESERVE|ALLOC_HIGHATOMIC|ALLOC_OOM) +#define ALLOC_RESERVES (ALLOC_MIN_RESERVE|ALLOC_HIGHATOMIC|ALLOC_OOM) /* Flags that mean GFP_ATOMIC */ #define ALLOC_MASK_ATOMIC (ALLOC_NON_BLOCK|ALLOC_MIN_RESERVE) From 0e05a469c29400eb6e35448ce7318581688a1806 Mon Sep 17 00:00:00 2001 From: Salvatore Dipietro Date: Thu, 1 Oct 2026 08:21:51 +0000 Subject: [PATCH 31/43] mm/page_alloc: avoid direct reclaim and compaction for costly __GFP_NORETRY allocations Commit 5d8edfb900d5 ("iomap: Copy larger chunks from userspace") introduced high-order folio allocations in the iomap buffered write path. When memory is fragmented, each failed costly-order allocation enters __alloc_pages_slowpath() which runs direct reclaim, direct compaction and drain_all_pages(), causing a 0.38x throughput drop on PostgreSQL pgbench (simple-update) with 1024 clients on a 96-vCPU arm64 system. The root issue is that direct reclaim and direct compaction are too expensive for hot allocation paths that have fallbacks to smaller allocations. __filemap_get_folio_mpol() already marks higher-order allocations with __GFP_NORETRY | __GFP_NOWARN, signalling that the caller can handle failure. However, the page allocator still enters the full blocking slowpath for costly orders with __GFP_NORETRY, which is unnecessarily aggressive when the caller will simply retry at a lower order. For costly-order allocations with __GFP_NORETRY, clear __GFP_DIRECT_RECLAIM at the very start of the slowpath, before can_direct_reclaim, can_compact and the nofail checks are evaluated. This makes the entire slowpath treat the request as non-blocking: no direct reclaim, no direct compaction and no drain_all_pages() IPI across every CPU. Reclaim and compaction move to the background instead: kswapd is still woken for reclaim, and it wakes kcompactd for defragmentation. The system keeps its long-term health while the latency-critical direct allocation path no longer stalls. Allocations that also request __GFP_THISNODE are exempted. That flag pairing identifies the local-node-first THP attempt issued by alloc_pages_mpol() (mempolicy.c), which relies on direct compaction to form transparent huge pages. Clearing __GFP_DIRECT_RECLAIM makes these allocations reach the !can_direct_reclaim bailout, which since commit 4c0ed883e051 ("mm/page_alloc: fix defrag_mode for non-reclaimable allocations") drops ALLOC_NOFRAGMENT and retries instead of failing when defrag_mode is on. With vm.defrag_mode=1 a __GFP_NORETRY allocation could therefore succeed by fragmenting another migratetype's pageblock. That is the wrong trade for a caller that has already communicated it can handle failure and has a cheap lower-order fallback, so exclude __GFP_NORETRY from the relaxation. The exclusion is not restricted to costly orders: any __GFP_NORETRY caller has said it can cope with failure, so failing is preferable to fragmenting for all of them. Test environment: Hardware: AWS EC2 m8g.24xlarge (96 vCPU, arm64) 12x 1TB IO2 32000 IOPS RAID0 XFS OS: AL2023 Kernel: v7.3-rc1 Database: PostgreSQL 18.4 Workload: pgbench simple-update, 1024 clients, 96 threads, 1200s Results (average of 3 runs, TPS): Config Avg TPS % vs baseline v7.3-rc1 baseline (vm.defrag_mode=0) 58,602 - v7.3-rc1 + this patch (vm.defrag_mode=0) 157,246 +168.3% v7.3-rc1 baseline (vm.defrag_mode=1) 109,012 - v7.3-rc1 + this patch (vm.defrag_mode=1) 157,778 +44.7% Link: https://lore.kernel.org/20261001082152.2879289-1-dipiets@amazon.it Link: https://lore.kernel.org/all/20260403193535.9970-1-dipiets@amazon.it/T/#t [v1] Link: https://lore.kernel.org/linux-mm/20260420161404.642-1-dipiets@amazon.it/T/#u [v2] Link: https://lore.kernel.org/all/20260710143437.12379-1-dipiets@amazon.it/T/#u [v3] Link: https://lore.kernel.org/all/20260904115629.3993331-1-dipiets@amazon.it/T/#u [v4] Link: https://lore.kernel.org/all/20260911142102.2294202-1-dipiets@amazon.it/T/#u [v5] Fixes: 5d8edfb900d5 ("iomap: Copy larger chunks from userspace") Signed-off-by: Salvatore Dipietro Signed-off-by: Andrew Morton Acked-by: Vlastimil Babka (SUSE) Acked-by: Zi Yan Reviewed-by: Johannes Weiner Reviewed-by: Christoph Hellwig Cc: David Hildenbrand Cc: Michal Hocko Cc: Matthew Wilcox Cc: Dave Chinner Cc: Ritesh Harjani Cc: Dmitry Ilvokhin Cc: Hazem Mohamed Abuelfotoh Cc: Ali Saidi Cc: Geoff Blake Cc: Christian Brauner Cc: Cc: Darrick J. Wong Cc: Brendan Jackman Cc: Cc: Suren Baghdasaryan Cc: "Garg, Shivank" --- mm/page_alloc.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index de0b3acf3e50..eb4f02617f53 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -4773,10 +4773,10 @@ static inline struct page * __alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order, struct alloc_context *ac) { - bool can_direct_reclaim = gfp_mask & __GFP_DIRECT_RECLAIM; - bool can_compact = can_direct_reclaim && gfp_compaction_allowed(gfp_mask); - bool nofail = gfp_mask & __GFP_NOFAIL; const bool costly_order = order > PAGE_ALLOC_COSTLY_ORDER; + bool can_direct_reclaim; + bool can_compact; + bool nofail; struct page *page = NULL; unsigned int alloc_flags; unsigned long did_some_progress; @@ -4791,6 +4791,18 @@ __alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order, bool can_retry_reserves = true; unsigned long alloc_start_time = jiffies; + /* + * Costly __GFP_NORETRY callers have a cheap fallback, so don't stall + * them in reclaim or compaction. __GFP_THISNODE callers are exempt. + */ + if (costly_order && (gfp_mask & __GFP_NORETRY) && + !(gfp_mask & __GFP_THISNODE)) + gfp_mask &= ~__GFP_DIRECT_RECLAIM; + + can_direct_reclaim = gfp_mask & __GFP_DIRECT_RECLAIM; + can_compact = can_direct_reclaim && gfp_compaction_allowed(gfp_mask); + nofail = gfp_mask & __GFP_NOFAIL; + if (unlikely(nofail)) { /* * We don't support __GFP_NOFAIL without __GFP_DIRECT_RECLAIM, @@ -4907,8 +4919,11 @@ __alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order, * Reclaim/compaction cannot run, so defrag_mode's strategy * of enforcing ALLOC_NOFRAGMENT cannot be fulfilled. Allow * fallbacks rather than failing the allocation outright. + * Not for __GFP_NORETRY: those have a cheap lower order + * fallback, so failing beats fragmenting. */ if (defrag_mode && (alloc_flags & ALLOC_NOFRAGMENT) && + !(gfp_mask & __GFP_NORETRY) && (gfp_mask & __GFP_KSWAPD_RECLAIM)) { alloc_flags &= ~ALLOC_NOFRAGMENT; goto retry; From 5d002001601a163b6c3500cdc098ff66c34f62d8 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Thu, 29 Oct 2026 12:18:37 +0200 Subject: [PATCH 32/43] mm/cma: accept memory of multi-range CMA areas Patch series "mm/cma: accept memory of CMA areas reserved with memblock_reserve()", v1. Pages of allocated CMA areas are released to the buddy allocator with __free_pages(), which does not check for unaccepted memory. This usually works because memblock accepts the memory it allocates. However, multi-range CMA areas, used by hugetlb_cma, and fixed CMA areas requested with cma=@ are reserved with memblock_reserve() and their memory is never accepted. On TDX and SEV-SNP guests the first access to such a page is fatal. To ease the backporting, each case is addressed by a separate patch: - patch 1 accepts the memory of multi-range CMA areas - patch 2 does the same for the fixed CMA areas The second patch won't apply cleanly to 6.6 and 6.12, but I believe the stable team LLMs will take care of that :) This patch (of 2): On systems with unaccepted memory, memblock allocations accept the allocated range, but when cma_declare_contiguous_multi() uses open coded allocation of multiple ranges, it reserves them with memblock_reserve() and they are never accepted. These pages are released to the buddy allocator by cma_activate_area() with __free_pages() that does not check for unaccepted memory, so the first access to such a page in a TDX or SEV-SNP guest is fatal. Accept the memory of each range when it is reserved. Link: https://lore.kernel.org/20261029-cma-multi-v1-0-2cbced8b5cb7@kernel.org Link: https://lore.kernel.org/20261029-cma-multi-v1-1-2cbced8b5cb7@kernel.org Fixes: c009da4258f9 ("mm, cma: support multiple contiguous ranges, if requested") Signed-off-by: Mike Rapoport (Microsoft) Signed-off-by: Andrew Morton Reviewed-by: Kiryl Shutsemau (Meta) Reviewed-by: Pankaj Gupta Tested-by: Pankaj Gupta Cc: David Hildenbrand Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: --- mm/cma.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mm/cma.c b/mm/cma.c index a10ea37a261d..0035efb1130a 100644 --- a/mm/cma.c +++ b/mm/cma.c @@ -678,6 +678,8 @@ int __init cma_declare_contiguous_multi(phys_addr_t total_size, break; } + accept_memory(mlp->base, size); + pr_debug("created region %d: %016llx - %016llx\n", nr, (u64)mlp->base, (u64)mlp->base + size); cmrp = &cma->ranges[nr++]; From f8f9cbc34a309a388c052706f3329d1b52985aa3 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Thu, 29 Oct 2026 12:18:38 +0200 Subject: [PATCH 33/43] mm/cma: accept memory of CMA areas reserved at a fixed address CMA areas allocated from memblock have their memory accepted by memblock_alloc_range_nid(), but areas reserved at a fixed address with memblock_reserve() are not. If a fixed CMA area is requested by the cma=@ command line parameter, it may be in regular RAM and on TDX and SEV-SNP guests it must be accepted. Accept the memory of fixed CMA areas as well, so that all CMA areas are accepted regardless of how they were reserved. Link: https://lore.kernel.org/20261029-cma-multi-v1-2-2cbced8b5cb7@kernel.org Fixes: 75d090fd167a ("x86/tdx: Add unaccepted memory support") Signed-off-by: Mike Rapoport (Microsoft) Signed-off-by: Andrew Morton Reviewed-by: Kiryl Shutsemau (Meta) Reviewed-by: Pankaj Gupta Tested-by: Pankaj Gupta Cc: David Hildenbrand Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: --- mm/cma.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mm/cma.c b/mm/cma.c index 0035efb1130a..f61d52796f4e 100644 --- a/mm/cma.c +++ b/mm/cma.c @@ -381,6 +381,8 @@ static int __init cma_fixed_reserve(phys_addr_t base, phys_addr_t size) return -EBUSY; } + accept_memory(base, size); + return 0; } From dd94d8c7660a60dbfcd425e0d10120de6d02a7bd Mon Sep 17 00:00:00 2001 From: "T.J. Mercier" Date: Thu, 29 Oct 2026 13:18:58 -0700 Subject: [PATCH 34/43] mm: memcontrol: fix NULL pointer deref and UAF from high_work_func() on dying memcg When memory.high is exceeded in non-task context, try_charge_memcg() schedules memcg->high_work without taking a reference on the memcg, and high_work_func() invokes reclaim_high() without pinning the memcg. With obj_cgroup reparenting, introduced in commit bf4f059954dc ("mm: memcg/slab: obj_cgroup API"), objects charged to a child memcg are reparented to its parent in mem_cgroup_css_offline() instead of holding a reference on the child until uncharged. Subsequent uncharges of those objects go to the parent, which can leave the child's memory use permanently above its high limit, so any later charge on the offlined memcg in non-task context schedules high_work. This was later widened from kernel memory to LRU folios when commit f1cf8d2f36dc ("mm: memcontrol: eliminate the problem of dying memory cgroup for LRU folios") converted LRU folios to use obj_cgroup reparenting. cancel_work_sync(&memcg->high_work) only runs at the end of teardown in mem_cgroup_css_free(), so high_work_func() can start or still be running reclaim_high() after mem_cgroup_css_released() or shrink_many() has called lru_gen_release_memcg() and unhashed lruvec->lrugen.list. If reclaim tries to rotate the memcg, lru_gen_rotate_memcg() dereferences a NULL pprev pointer. This issue is specific to MGLRU and was introduced with the memcg LRU in commit e4dde56cd208 ("mm: multi-gen LRU: per-node lru_gen_folio lists"). With either traditional LRU or MGLRU, css_free_rwork_fn() calls css_rstat_exit() which frees per-CPU rstat data before calling mem_cgroup_css_free(). If reclaim_high() is still running, rstat updates via mod_lruvec_state() -> __css_rstat_updated() cause a use-after-free on the per-CPU rstat data. This was introduced when commit 5da3bfa029d6 ("cgroup: use separate rstat trees for each subsystem") moved css_rstat_exit() ahead of ss->css_free(). Fix both problems by acquiring a reference with css_tryget_online() in high_work_func() before calling reclaim_high(), and by resetting memory.high and swap.high to PAGE_COUNTER_MAX in mem_cgroup_css_offline() so that charges via references on an offlined memcg do not trigger high_work on the dead child and skip checking live ancestors in try_charge_memcg(). Acquiring the reference in high_work_func() rather than before scheduling high_work is safe because mem_cgroup_css_free() calls irq_work_sync() and cancel_work_sync() which cancels pending work and waits for an already running high_work_func() to complete before mem_cgroup_free() frees the memcg. css_tryget_online() uses percpu_ref_tryget_live() and is safe to call after percpu_ref_exit() before the cgroup is freed unlike css_tryget(). Oops: general protection fault, probably for non-canonical address 0xdffffc0000000000: 0000 [#1] SMP KASAN NOPTI KASAN: null-ptr-deref in range [0x0000000000000000-0x0000000000000007] CPU: 1 UID: 0 PID: 27 Comm: kworker/1:0 Tainted: G B W 7.3.0-rc5-g2bba28e98578 #284 PREEMPT(full) Tainted: [B]=BAD_PAGE, [W]=WARN <-- Taint from lru_gen_rotate_memcg() VM_WARN_ON_ONCE Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.17.0-debian-1.17.0-1 04/01/2014 Workqueue: events high_work_func RIP: 0010:lru_gen_rotate_memcg+0x1a3/0xe90 Call Trace: ? read_ctrl_pos+0x22f/0x3f0 shrink_lruvec+0x16cb/0x1b80 ? ret_from_fork_asm+0x1a/0x30 ? lapic_next_event+0x15/0x20 ? clockevents_program_event+0x2bb/0x6f0 ? lapic_next_event+0x15/0x20 ? __pfx_shrink_lruvec+0x10/0x10 ? __pfx_clockevents_program_event+0x10/0x10 ? _raw_spin_lock+0x84/0xe0 ? __pfx__raw_spin_lock+0x10/0x10 ? kasan_save_stack+0x30/0x50 ? psi_group_change+0x381/0x740 ? __pfx_sched_clock_cpu+0x10/0x10 ? save_fpregs_to_fpstate+0xc4/0x2c0 ? finish_task_switch.isra.0+0x1de/0xac0 ? __schedule+0xf62/0x42c0 ? __pfx___schedule+0x10/0x10 ? __pfx___irq_work_queue_local+0x10/0x10 ? shrink_node+0x626/0x31e0 shrink_node+0x626/0x31e0 ? ring_buffer_unlock_commit+0x11d/0x5c0 ? kswapd_clear_hopeless+0xa0/0x150 ? __pfx_shrink_node+0x10/0x10 ? 0xffffffffc0000095 do_try_to_free_pages+0x255/0x13b0 ? ring_buffer_unlock_commit+0x11d/0x5c0 ? reclaim_high+0x10a/0x1b0 try_to_free_mem_cgroup_pages+0x1f9/0x5a0 ? __pfx_try_to_free_mem_cgroup_pages+0x10/0x10 ? irqentry_exit+0xc6/0x670 reclaim_high+0x10a/0x1b0 ? __pfx_reclaim_high+0x10/0x10 ? process_one_work+0x5ff/0x1030 ? __pfx_high_work_func+0x10/0x10 process_one_work+0x6e2/0x1030 ? __pfx___schedule+0x10/0x10 ? __pfx_process_one_work+0x10/0x10 worker_thread+0x422/0xbb0 ? __pfx_worker_thread+0x10/0x10 kthread+0x2cd/0x3a0 ? recalc_sigpending+0x15c/0x200 ? __pfx_kthread+0x10/0x10 ret_from_fork+0x368/0x590 ? __pfx_ret_from_fork+0x10/0x10 ? __switch_to+0x588/0xf50 ? __pfx_kthread+0x10/0x10 ret_from_fork_asm+0x1a/0x30 Link: https://lore.kernel.org/20261029201858.2932905-1-tjmercier@google.com Fixes: e4dde56cd208 ("mm: multi-gen LRU: per-node lru_gen_folio lists") Fixes: 5da3bfa029d6 ("cgroup: use separate rstat trees for each subsystem") Signed-off-by: T.J. Mercier Signed-off-by: Andrew Morton Reviewed-by: Johannes Weiner Acked-by: Muchun Song Acked-by: Shakeel Butt Acked-by: Michal Hocko Cc: Michal Hocko Cc: Roman Gushchin Cc: Qi Zheng Cc: JP Kobryn Cc: --- mm/memcontrol.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mm/memcontrol.c b/mm/memcontrol.c index a05d9a8baa1b..84f4a8fd4d16 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -2457,7 +2457,10 @@ static void high_work_func(struct work_struct *work) struct mem_cgroup *memcg; memcg = container_of(work, struct mem_cgroup, high_work); + if (!css_tryget_online(&memcg->css)) + return; reclaim_high(memcg, MEMCG_CHARGE_BATCH, GFP_KERNEL); + css_put(&memcg->css); } static void high_irq_work_func(struct irq_work *work) @@ -4463,6 +4466,8 @@ static void mem_cgroup_css_offline(struct cgroup_subsys_state *css) page_counter_set_min(&memcg->memory, 0); page_counter_set_low(&memcg->memory, 0); + page_counter_set_high(&memcg->memory, PAGE_COUNTER_MAX); + page_counter_set_high(&memcg->swap, PAGE_COUNTER_MAX); zswap_memcg_offline_cleanup(memcg); From c4948eb28f0ff505d21e520c4354e7486249e862 Mon Sep 17 00:00:00 2001 From: "David Hildenbrand (Arm)" Date: Fri, 30 Oct 2026 22:34:25 +0100 Subject: [PATCH 35/43] MAINTAINERS: mention for-next branch in MEMORY MANAGEMENT - MEMORY MAP Patch series "MAINTAINERS: MM updates". Several MM updates: Make Zi Yan a THP maintainer (thanks for all your hard work over the years and thanks for taking on this responsibility), and perform some MM MAINTAINERS cleanups. Then, start the next step of the transition from Andrew's MM tree to separate component trees that all get integrated into the mm-next integration tree. Start routing shmem through mm/core.git, adding me as a maintainer primarily to pick patches. Then, target the following components for the mm/core.git tree that I (co)maintain: - MEMORY MANAGEMENT - BALLOON - MEMORY MANAGEMENT - CORE - MEMORY MANAGEMENT - GUP (GET USER PAGES) - MEMORY MANAGEMENT - KSM (Kernel Samepage Merging) - MEMORY MANAGEMENT - MEMORY HOT(UN)PLUG - MEMORY MANAGEMENT - MIGRATION - MEMORY MANAGEMENT - NUMA PLACEMENT - MEMORY MANAGEMENT - RMAP (REVERSE MAPPING) - MEMORY MANAGEMENT - THP (TRANSPARENT HUGE PAGE) More details in the last patch. In the upcoming cycles we'll then route more of the other MM components through different trees. Let's see how this goes, in particular with the continuous stream of fixes. But getting these in in a controlled way has highest priority right now wither way. This patch (of 7): "memmap" will be used as a prefix for branches, but not as a branch itself. "for-next" is the integration for all these things against which people should ideally send patches. Not mentioning a branch makes them assume "master", which is wrong. So just mention the right thing, which is "for-next" Link: https://lore.kernel.org/20261030-maintainers-v1-0-97278f8a5f54@kernel.org Link: https://lore.kernel.org/20261030-maintainers-v1-1-97278f8a5f54@kernel.org Signed-off-by: David Hildenbrand (Arm) Signed-off-by: Andrew Morton Acked-by: Vlastimil Babka (SUSE) Acked-by: Qi Zheng Acked-by: Muchun Song Acked-by: Mike Rapoport (Microsoft) Acked-by: Zi Yan Acked-by: SJ Park Acked-by: Lorenzo Stoakes (ARM) Acked-by: Harry Yoo (Meta) Acked-by: Shakeel Butt Acked-by: SJ Park Acked-by: Kiryl Shutsemau (Meta) Cc: Alistair Popple Cc: Aneesh Kumar K.V Cc: Baolin Wang Cc: Baoquan He Cc: Barry Song Cc: Brendan Jackman Cc: Byungchul Park Cc: Chengming Zhou Cc: Chris Li Cc: Christoph Lameter Cc: Dave Chinner Cc: David Rientjes Cc: Dennis Zhou Cc: Dev Jain Cc: Gregory Price Cc: Hao Li Cc: Hugh Dickins Cc: Jann Horn Cc: Johannes Weiner Cc: John Hubbard Cc: Joshua Hahn Cc: Kairui Song Cc: Kemeng Shi Cc: Lance Yang Cc: Liam R. Howlett Cc: Matthew Brost Cc: Miaohe Lin Cc: Michal Hocko Cc: Naoya Horiguchi Cc: Nhat Pham Cc: Nicholas Piggin Cc: Nico Pache Cc: Oscar Salvador Cc: Pedro Falcato Cc: Peter Xu Cc: Peter Zijlstra Cc: Rakie Kim Cc: Rik van Riel Cc: Roman Gushchin Cc: Ryan Roberts Cc: Suren Baghdasaryan Cc: Tejun Heo Cc: Uladzislau Rezki (Sony) Cc: Usama Arif Cc: Will Deacon Cc: Xu Xin Cc: Ying Huang Cc: Youngjun Park Cc: Jason Gunthorpe --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 93abbb315065..9a79807e984d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17604,7 +17604,7 @@ M: Mike Rapoport M: David Hildenbrand L: linux-mm@kvack.org S: Maintained -T: git git://git.kernel.org/pub/scm/linux/kernel/git/mm/core.git memmap +T: git git://git.kernel.org/pub/scm/linux/kernel/git/mm/core.git for-next F: Documentation/mm/memory-model.rst F: arch/*/include/asm/sparsemem.h F: include/asm-generic/memory_model.h From bf2b6c47af60b3ed31b5d74c61fd52c0b79d6ff3 Mon Sep 17 00:00:00 2001 From: "David Hildenbrand (Arm)" Date: Fri, 30 Oct 2026 22:34:26 +0100 Subject: [PATCH 36/43] MAINTAINERS: TMPFS (SHMEM FILESYSTEM) updates I am not really the best shmem/tmpfs/memfd expert around, but it should be good enough to judge when something is ready to be picked up, to then pick it up. I'll have to heavily rely on Hugh and Baolin on judgment for the more involved and more controversial bits. Rename "TMPFS (SHMEM FILESYSTEM)" to "MEMORY MANAGEMENT - SHARED MEMORY (SHMEM/TMPFS/MEMFD)" and move it accordingly. Add myself as a MAINTAINER so people know whom to CC. Add mm/core.git as the target git tree. Once shmem becomes a high volume component we can dedicate it a separate tree and have dedicated people pick it up. While at it, add some more shmem/tmpfs/memfd related files+directories we missed so far. There is also "kernel/pid_sysctl.h", which includes some of the pid namespaced sysctl vm.memfd_noexec. But that's really more of a proc/ namespace thingy, so it's not a good fit here. Link: https://lore.kernel.org/20261030-maintainers-v1-2-97278f8a5f54@kernel.org Signed-off-by: David Hildenbrand (Arm) Signed-off-by: Andrew Morton Acked-by: Vlastimil Babka (SUSE) Acked-by: Qi Zheng Acked-by: Mike Rapoport (Microsoft) Acked-by: Zi Yan Acked-by: SJ Park Acked-by: Harry Yoo (Meta) Acked-by: Lorenzo Stoakes (ARM) Acked-by: Shakeel Butt Acked-by: SJ Park Acked-by: Baolin Wang Acked-by: Kiryl Shutsemau (Meta) Cc: Alistair Popple Cc: Aneesh Kumar K.V Cc: Baoquan He Cc: Barry Song Cc: Brendan Jackman Cc: Byungchul Park Cc: Chengming Zhou Cc: Chris Li Cc: Christoph Lameter Cc: Dave Chinner Cc: David Rientjes Cc: Dennis Zhou Cc: Dev Jain Cc: Gregory Price Cc: Hao Li Cc: Hugh Dickins Cc: Jann Horn Cc: Johannes Weiner Cc: John Hubbard Cc: Joshua Hahn Cc: Kairui Song Cc: Kemeng Shi Cc: Lance Yang Cc: Liam R. Howlett Cc: Matthew Brost Cc: Miaohe Lin Cc: Michal Hocko Cc: Muchun Song Cc: Naoya Horiguchi Cc: Nhat Pham Cc: Nicholas Piggin Cc: Nico Pache Cc: Oscar Salvador Cc: Pedro Falcato Cc: Peter Xu Cc: Peter Zijlstra Cc: Rakie Kim Cc: Rik van Riel Cc: Roman Gushchin Cc: Ryan Roberts Cc: Suren Baghdasaryan Cc: Tejun Heo Cc: Uladzislau Rezki (Sony) Cc: Usama Arif Cc: Will Deacon Cc: Xu Xin Cc: Ying Huang Cc: Youngjun Park Cc: Jason Gunthorpe --- MAINTAINERS | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 9a79807e984d..517e24e7216c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17810,6 +17810,25 @@ S: Maintained F: include/linux/secretmem.h F: mm/secretmem.c +MEMORY MANAGEMENT - SHARED MEMORY (SHMEM/TMPFS/MEMFD) +M: Hugh Dickins +M: David Hildenbrand +R: Baolin Wang +L: linux-mm@kvack.org +S: Maintained +T: git git://git.kernel.org/pub/scm/linux/kernel/git/mm/core.git for-next +F: Documentation/filesystems/tmpfs.rst +F: Documentation/mm/shmfs.rst +F: Documentation/userspace-api/mfd_noexec.rst +F: include/linux/memfd.h +F: include/linux/shmem_fs.h +F: include/uapi/linux/memfd.h +F: mm/memfd.c +F: mm/shmem.c +F: mm/shmem_quota.c +F: tools/testing/selftests/memfd/ +F: tools/testing/selftests/tmpfs/ + MEMORY MANAGEMENT - SWAP M: Andrew Morton M: Chris Li @@ -28245,17 +28264,6 @@ S: Maintained F: Documentation/hwmon/tmp513.rst F: drivers/hwmon/tmp513.c -TMPFS (SHMEM FILESYSTEM) -M: Hugh Dickins -R: Baolin Wang -L: linux-mm@kvack.org -S: Maintained -F: include/linux/memfd.h -F: include/linux/shmem_fs.h -F: mm/memfd.c -F: mm/shmem.c -F: mm/shmem_quota.c - TOMOYO SECURITY MODULE M: Kentaro Takeda M: Tetsuo Handa From afa77645c9deb01595de58fd90acc91017c41754 Mon Sep 17 00:00:00 2001 From: "David Hildenbrand (Arm)" Date: Fri, 30 Oct 2026 22:34:27 +0100 Subject: [PATCH 37/43] MAINTAINERS: add Zi Yan as co-maintainer for MEMORY MANAGEMENT - THP Zi has been contributing to THP consistently and reliably over many years and is considered one of our most valuable THP experts. As THP is an especially active area lately, having a single dedicated THP maintainer is not sustainable. Fortunately, Zi volunteered to take on more responsibility by stepping up as a THP co-maintainer. So let's do exactly that. His placement in the MAINTAINERS entry is very convenient to perform this change :) Link: https://lore.kernel.org/20261030-maintainers-v1-3-97278f8a5f54@kernel.org Signed-off-by: David Hildenbrand (Arm) Signed-off-by: Andrew Morton Acked-by: Vlastimil Babka (SUSE) Acked-by: Qi Zheng Acked-by: Lorenzo Stoakes (ARM) Acked-by: Harry Yoo (Meta) Acked-by: Muchun Song Acked-by: Mike Rapoport (Microsoft) Acked-by: Zi Yan Acked-by: SJ Park Acked-by: Barry Song Acked-by: Lance Yang Acked-by: Dev Jain Acked-by: Shakeel Butt Acked-by: SJ Park Acked-by: Baolin Wang Acked-by: Kiryl Shutsemau (Meta) Cc: Alistair Popple Cc: Aneesh Kumar K.V Cc: Baoquan He Cc: Brendan Jackman Cc: Byungchul Park Cc: Chengming Zhou Cc: Chris Li Cc: Christoph Lameter Cc: Dave Chinner Cc: David Rientjes Cc: Dennis Zhou Cc: Gregory Price Cc: Hao Li Cc: Hugh Dickins Cc: Jann Horn Cc: Johannes Weiner Cc: John Hubbard Cc: Joshua Hahn Cc: Kairui Song Cc: Kemeng Shi Cc: Liam R. Howlett Cc: Matthew Brost Cc: Miaohe Lin Cc: Michal Hocko Cc: Naoya Horiguchi Cc: Nhat Pham Cc: Nicholas Piggin Cc: Nico Pache Cc: Oscar Salvador Cc: Pedro Falcato Cc: Peter Xu Cc: Peter Zijlstra Cc: Rakie Kim Cc: Rik van Riel Cc: Roman Gushchin Cc: Ryan Roberts Cc: Suren Baghdasaryan Cc: Tejun Heo Cc: Uladzislau Rezki (Sony) Cc: Usama Arif Cc: Will Deacon Cc: Xu Xin Cc: Ying Huang Cc: Youngjun Park Cc: Jason Gunthorpe --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 517e24e7216c..6f37dbba6dcd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17855,7 +17855,7 @@ F: mm/swapfile.c MEMORY MANAGEMENT - THP (TRANSPARENT HUGE PAGE) M: Andrew Morton M: David Hildenbrand -R: Zi Yan +M: Zi Yan R: Baolin Wang R: Liam R. Howlett R: Nico Pache From 3504810144b22c9beb0a992601465b6ba1bc1db3 Mon Sep 17 00:00:00 2001 From: "David Hildenbrand (Arm)" Date: Fri, 30 Oct 2026 22:34:28 +0100 Subject: [PATCH 38/43] MAINTAINERS: sort entries in MM sections We try to keep these entries sorted; let's sort the MM related ones. Link: https://lore.kernel.org/20261030-maintainers-v1-4-97278f8a5f54@kernel.org Signed-off-by: David Hildenbrand (Arm) Signed-off-by: Andrew Morton Acked-by: Vlastimil Babka (SUSE) Acked-by: Qi Zheng Acked-by: Mike Rapoport (Microsoft) Acked-by: Zi Yan Acked-by: SJ Park Reviewed-by: Harry Yoo (Meta) Acked-by: Lorenzo Stoakes (ARM) Acked-by: Shakeel Butt Acked-by: SJ Park Acked-by: Kiryl Shutsemau (Meta) Cc: Alistair Popple Cc: Aneesh Kumar K.V Cc: Baolin Wang Cc: Baoquan He Cc: Barry Song Cc: Brendan Jackman Cc: Byungchul Park Cc: Chengming Zhou Cc: Chris Li Cc: Christoph Lameter Cc: Dave Chinner Cc: David Rientjes Cc: Dennis Zhou Cc: Dev Jain Cc: Gregory Price Cc: Hao Li Cc: Hugh Dickins Cc: Jann Horn Cc: Johannes Weiner Cc: John Hubbard Cc: Joshua Hahn Cc: Kairui Song Cc: Kemeng Shi Cc: Lance Yang Cc: Liam R. Howlett Cc: Matthew Brost Cc: Miaohe Lin Cc: Michal Hocko Cc: Muchun Song Cc: Naoya Horiguchi Cc: Nhat Pham Cc: Nicholas Piggin Cc: Nico Pache Cc: Oscar Salvador Cc: Pedro Falcato Cc: Peter Xu Cc: Peter Zijlstra Cc: Rakie Kim Cc: Rik van Riel Cc: Roman Gushchin Cc: Ryan Roberts Cc: Suren Baghdasaryan Cc: Tejun Heo Cc: Uladzislau Rezki (Sony) Cc: Usama Arif Cc: Will Deacon Cc: Xu Xin Cc: Ying Huang Cc: Youngjun Park Cc: Jason Gunthorpe --- MAINTAINERS | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 6f37dbba6dcd..bc05b1bb4687 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6870,9 +6870,9 @@ L: linux-mm@kvack.org S: Maintained F: include/linux/memcontrol.h F: include/linux/page_counter.h -F: mm/memcontrol.c F: mm/memcontrol-v1.c F: mm/memcontrol-v1.h +F: mm/memcontrol.c F: mm/page_counter.c F: samples/cgroup/* F: tools/testing/selftests/cgroup/memcg_protection.m @@ -17467,8 +17467,8 @@ F: Documentation/core-api/memory-hotplug.rst F: drivers/base/memory.c F: include/linux/memory_hotplug.h F: include/linux/memremap.h -F: mm/memremap.c F: mm/memory_hotplug.c +F: mm/memremap.c F: tools/testing/selftests/memory-hotplug/ MEMORY MANAGEMENT @@ -17518,6 +17518,7 @@ S: Maintained W: http://www.linux-mm.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm F: Documentation/admin-guide/sysctl/vm.rst +F: fs/proc/meminfo.c F: include/linux/folio_batch.h F: include/linux/gfp.h F: include/linux/gfp_types.h @@ -17526,16 +17527,15 @@ F: include/linux/leafops.h F: include/linux/memory.h F: include/linux/mm.h F: include/linux/mm_*.h -F: include/linux/mmzone.h F: include/linux/mmdebug.h F: include/linux/mmu_notifier.h +F: include/linux/mmzone.h F: include/linux/pagewalk.h F: include/linux/pgalloc.h F: include/linux/pgtable.h F: include/linux/ptdump.h F: include/linux/vmpressure.h F: include/linux/vmstat.h -F: fs/proc/meminfo.c F: kernel/fork.c F: mm/Kconfig F: mm/debug.c @@ -17674,14 +17674,12 @@ F: Documentation/admin-guide/mm/ F: Documentation/core-api/memory-allocation.rst F: Documentation/core-api/mm-api.rst F: Documentation/mm/ -X: Documentation/admin-guide/mm/damon/ -X: Documentation/mm/damon/ -F: mm/char-mem.c F: include/linux/cma.h F: include/linux/dmapool.h F: include/linux/ioremap.h F: include/linux/page_idle.h F: mm/backing-dev.c +F: mm/char-mem.c F: mm/cma.c F: mm/cma_debug.c F: mm/cma_sysfs.c @@ -17696,6 +17694,8 @@ F: mm/pgalloc-track.h F: mm/process_vm_access.c F: tools/lib/mm/ F: tools/testing/selftests/mm/ +X: Documentation/admin-guide/mm/damon/ +X: Documentation/mm/damon/ MEMORY MANAGEMENT - NUMA MEMBLOCKS AND NUMA EMULATION M: Andrew Morton @@ -17848,8 +17848,8 @@ F: include/linux/swapfile.h F: include/linux/swapops.h F: mm/page_io.c F: mm/swap.h -F: mm/swap_table.h F: mm/swap_state.c +F: mm/swap_table.h F: mm/swapfile.c MEMORY MANAGEMENT - THP (TRANSPARENT HUGE PAGE) @@ -17921,10 +17921,10 @@ L: linux-mm@kvack.org S: Maintained W: http://www.linux-mm.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm -F: mm/char-mem.c -F: include/trace/events/mmap.h F: fs/proc/task_mmu.c F: fs/proc/task_nommu.c +F: include/trace/events/mmap.h +F: mm/char-mem.c F: mm/mincore.c F: mm/mlock.c F: mm/mmap.c @@ -29887,9 +29887,9 @@ S: Maintained W: http://www.linux-mm.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm F: include/linux/vmalloc.h +F: lib/test_vmalloc.c F: mm/vmalloc.c F: mm/vmalloc.h -F: lib/test_vmalloc.c VME SUBSYSTEM L: linux-kernel@vger.kernel.org From a32c741897afc5a18d7ae0e2f1767768d6312ae6 Mon Sep 17 00:00:00 2001 From: "David Hildenbrand (Arm)" Date: Fri, 30 Oct 2026 22:34:29 +0100 Subject: [PATCH 39/43] MAINTAINERS: add missing files to MM component sections We want to make sure that MM components cover all relevant MM files. The generic "MEMORY MANAGEMENT" sections will probably go away at some point. We have a bunch of files that are not explicitly tracked under MM components. Let's add these missing files. Split up tools/mm to the appropriate components, but still add it also under MISC, similar to what we did with selftests. Don't split up selftests for now. They are covered by MISC and some sections name them separately. Some files like - include/linux/numa.h - include/trace/events/kmem.h - Documentation/admin-guide/numastat.rst are a bit fuzzy. Add them to the best fit for now. Link: https://lore.kernel.org/20261030-maintainers-v1-5-97278f8a5f54@kernel.org Signed-off-by: David Hildenbrand (Arm) Signed-off-by: Andrew Morton Acked-by: Vlastimil Babka (SUSE) Acked-by: Qi Zheng Acked-by: Mike Rapoport (Microsoft) Acked-by: Zi Yan Acked-by: SJ Park Acked-by: Lorenzo Stoakes (ARM) Acked-by: Muchun Song Acked-by: Shakeel Butt Acked-by: SJ Park Acked-by: Kiryl Shutsemau (Meta) Cc: Alistair Popple Cc: Aneesh Kumar K.V Cc: Baolin Wang Cc: Baoquan He Cc: Barry Song Cc: Brendan Jackman Cc: Byungchul Park Cc: Chengming Zhou Cc: Chris Li Cc: Christoph Lameter Cc: Dave Chinner Cc: David Rientjes Cc: Dennis Zhou Cc: Dev Jain Cc: Gregory Price Cc: Hao Li Cc: Harry Yoo Cc: Hugh Dickins Cc: Jann Horn Cc: Johannes Weiner Cc: John Hubbard Cc: Joshua Hahn Cc: Kairui Song Cc: Kemeng Shi Cc: Lance Yang Cc: Liam R. Howlett Cc: Matthew Brost Cc: Miaohe Lin Cc: Michal Hocko Cc: Naoya Horiguchi Cc: Nhat Pham Cc: Nicholas Piggin Cc: Nico Pache Cc: Oscar Salvador Cc: Pedro Falcato Cc: Peter Xu Cc: Peter Zijlstra Cc: Rakie Kim Cc: Rik van Riel Cc: Roman Gushchin Cc: Ryan Roberts Cc: Suren Baghdasaryan Cc: Tejun Heo Cc: Uladzislau Rezki (Sony) Cc: Usama Arif Cc: Will Deacon Cc: Xu Xin Cc: Ying Huang Cc: Youngjun Park Cc: Jason Gunthorpe --- MAINTAINERS | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index bc05b1bb4687..d111a58f7cfa 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6868,8 +6868,10 @@ R: Muchun Song L: cgroups@vger.kernel.org L: linux-mm@kvack.org S: Maintained +F: Documentation/admin-guide/cgroup-v1/memory.rst F: include/linux/memcontrol.h F: include/linux/page_counter.h +F: include/trace/events/memcg.h F: mm/memcontrol-v1.c F: mm/memcontrol-v1.h F: mm/memcontrol.c @@ -12376,6 +12378,7 @@ R: David Hildenbrand L: linux-mm@kvack.org S: Maintained F: Documentation/ABI/testing/sysfs-kernel-mm-hugepages +F: Documentation/admin-guide/cgroup-v1/hugetlb.rst F: Documentation/admin-guide/mm/hugetlbpage.rst F: Documentation/mm/hugetlbfs_reserv.rst F: Documentation/mm/vmemmap_dedup.rst @@ -12387,6 +12390,7 @@ F: mm/hugetlb.c F: mm/hugetlb_cgroup.c F: mm/hugetlb_cma.c F: mm/hugetlb_cma.h +F: mm/hugetlb_internal.h F: mm/hugetlb_sysctl.c F: mm/hugetlb_sysfs.c F: mm/hugetlb_vmemmap.c @@ -12406,6 +12410,7 @@ M: Miaohe Lin R: Naoya Horiguchi L: linux-mm@kvack.org S: Maintained +F: Documentation/ABI/testing/sysfs-memory-page-offline F: include/linux/memory-failure.h F: include/trace/events/memory-failure.h F: mm/hwpoison-inject.c @@ -17462,6 +17467,7 @@ M: Oscar Salvador L: linux-mm@kvack.org L: linux-cxl@vger.kernel.org S: Maintained +F: Documentation/ABI/testing/sysfs-devices-memory F: Documentation/admin-guide/mm/memory-hotplug.rst F: Documentation/core-api/memory-hotplug.rst F: drivers/base/memory.c @@ -17518,10 +17524,13 @@ S: Maintained W: http://www.linux-mm.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm F: Documentation/admin-guide/sysctl/vm.rst +F: Documentation/core-api/gfp_mask-from-fs-io.rst F: fs/proc/meminfo.c +F: include/linux/cacheflush.h F: include/linux/folio_batch.h F: include/linux/gfp.h F: include/linux/gfp_types.h +F: include/linux/highmem-internal.h F: include/linux/highmem.h F: include/linux/leafops.h F: include/linux/memory.h @@ -17531,11 +17540,16 @@ F: include/linux/mmdebug.h F: include/linux/mmu_notifier.h F: include/linux/mmzone.h F: include/linux/pagewalk.h +F: include/linux/pfn.h F: include/linux/pgalloc.h F: include/linux/pgtable.h F: include/linux/ptdump.h +F: include/linux/sched/mm.h +F: include/linux/vm_event_item.h F: include/linux/vmpressure.h F: include/linux/vmstat.h +F: include/trace/events/mmflags.h +F: include/trace/events/pagemap.h F: kernel/fork.c F: mm/Kconfig F: mm/debug.c @@ -17554,6 +17568,7 @@ F: mm/ptdump.c F: mm/util.c F: mm/vmpressure.c F: mm/vmstat.c +F: scripts/gdb/linux/mm.py N: include\/linux\/page[-_][a-zA-Z]* MEMORY MANAGEMENT - EXECMEM @@ -17574,6 +17589,7 @@ L: linux-mm@kvack.org S: Maintained W: http://www.linux-mm.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm +F: Documentation/core-api/pin_user_pages.rst F: mm/gup.c F: mm/gup_test.c F: mm/gup_test.h @@ -17651,6 +17667,7 @@ W: http://www.linux-mm.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm F: include/linux/migrate.h F: include/linux/migrate_mode.h +F: include/trace/events/migrate.h F: mm/migrate.c F: mm/migrate_device.c @@ -17674,13 +17691,20 @@ F: Documentation/admin-guide/mm/ F: Documentation/core-api/memory-allocation.rst F: Documentation/core-api/mm-api.rst F: Documentation/mm/ +F: fs/proc/page.c +F: include/linux/backing-dev-defs.h +F: include/linux/backing-dev.h F: include/linux/cma.h F: include/linux/dmapool.h F: include/linux/ioremap.h +F: include/linux/kernel-page-flags.h F: include/linux/page_idle.h +F: include/trace/events/cma.h +F: include/uapi/linux/kernel-page-flags.h F: mm/backing-dev.c F: mm/char-mem.c F: mm/cma.c +F: mm/cma.h F: mm/cma_debug.c F: mm/cma_sysfs.c F: mm/dmapool.c @@ -17693,7 +17717,9 @@ F: mm/page_idle.c F: mm/pgalloc-track.h F: mm/process_vm_access.c F: tools/lib/mm/ +F: tools/mm/ F: tools/testing/selftests/mm/ +X: Documentation/ABI/testing/sysfs-kernel-mm-damon X: Documentation/admin-guide/mm/damon/ X: Documentation/mm/damon/ @@ -17703,6 +17729,7 @@ M: Mike Rapoport L: linux-mm@kvack.org S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/rppt/memblock.git +F: include/linux/numa.h F: include/linux/numa_memblks.h F: mm/numa.c F: mm/numa_emulation.c @@ -17723,6 +17750,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm F: Documentation/ABI/testing/sysfs-kernel-mm-memory-tiers F: Documentation/ABI/testing/sysfs-kernel-mm-mempolicy F: Documentation/ABI/testing/sysfs-kernel-mm-mempolicy-weighted-interleave +F: Documentation/admin-guide/numastat.rst F: include/linux/memory-tiers.h F: include/linux/mempolicy.h F: include/uapi/linux/mempolicy.h @@ -17754,6 +17782,9 @@ F: include/linux/compaction.h F: include/linux/gfp.h F: include/linux/page-isolation.h F: include/linux/pageblock-flags.h +F: include/trace/events/compaction.h +F: include/trace/events/page_isolation.h +F: include/trace/events/page_ref.h F: mm/compaction.c F: mm/debug_page_alloc.c F: mm/debug_page_ref.c @@ -17770,6 +17801,9 @@ F: mm/page_reporting.h F: mm/show_mem.c F: mm/shuffle.c F: mm/shuffle.h +F: scripts/gdb/linux/page_owner.py +F: tools/mm/page_owner_filter.c +F: tools/mm/page_owner_sort.c MEMORY MANAGEMENT - RECLAIM M: Andrew Morton @@ -17781,6 +17815,7 @@ R: Shakeel Butt R: Lorenzo Stoakes L: linux-mm@kvack.org S: Maintained +F: include/trace/events/vmscan.h F: mm/vmscan.c F: mm/workingset.c @@ -17874,10 +17909,12 @@ F: Documentation/admin-guide/mm/transhuge.rst F: include/linux/huge_mm.h F: include/linux/khugepaged.h F: include/trace/events/huge_memory.h +F: include/trace/events/thp.h F: mm/collapse.h F: mm/huge_memory.c F: mm/khugepaged.c F: mm/mm_slot.h +F: tools/mm/thpmaps F: tools/testing/selftests/mm/khugepaged.c F: tools/testing/selftests/mm/split_huge_page_test.c F: tools/testing/selftests/mm/transhuge-stress.c @@ -17921,9 +17958,15 @@ L: linux-mm@kvack.org S: Maintained W: http://www.linux-mm.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm +F: Documentation/core-api/protection-keys.rst +F: Documentation/filesystems/mmap_prepare.rst +F: Documentation/userspace-api/mseal.rst F: fs/proc/task_mmu.c F: fs/proc/task_nommu.c +F: include/linux/mman.h +F: include/linux/pkeys.h F: include/trace/events/mmap.h +F: include/uapi/linux/mman.h F: mm/char-mem.c F: mm/mincore.c F: mm/mlock.c @@ -18762,6 +18805,7 @@ M: Peter Zijlstra L: linux-arch@vger.kernel.org L: linux-mm@kvack.org S: Maintained +F: Documentation/core-api/cachetlb.rst F: arch/*/include/asm/tlb.h F: include/asm-generic/tlb.h F: include/trace/events/tlb.h @@ -21872,6 +21916,7 @@ S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/dennis/percpu.git F: arch/*/include/asm/percpu.h F: include/linux/percpu*.h +F: include/trace/events/percpu.h F: lib/percpu*.c F: mm/percpu*.c F: mm/percpu-internal.h @@ -25598,6 +25643,7 @@ F: include/linux/shrinker.h F: mm/list_lru.c F: mm/shrinker.c F: mm/shrinker_debug.c +F: tools/cgroup/memcg_shrinker.py SH_VOU V4L2 OUTPUT DRIVER L: linux-media@vger.kernel.org @@ -25830,10 +25876,12 @@ R: Roman Gushchin L: linux-mm@kvack.org S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/mm/slab.git +F: Documentation/ABI/testing/sysfs-kernel-slab F: Documentation/admin-guide/mm/slab.rst F: Documentation/mm/slab.rst F: include/linux/mempool.h F: include/linux/slab.h +F: include/trace/events/kmem.h F: lib/tests/slub_kunit.c F: mm/failslab.c F: mm/mempool.c @@ -29887,9 +29935,11 @@ S: Maintained W: http://www.linux-mm.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm F: include/linux/vmalloc.h +F: include/trace/events/vmalloc.h F: lib/test_vmalloc.c F: mm/vmalloc.c F: mm/vmalloc.h +F: scripts/gdb/linux/vmalloc.py VME SUBSYSTEM L: linux-kernel@vger.kernel.org From d6503586227ed145d72c8e69be0a6e35044636b4 Mon Sep 17 00:00:00 2001 From: "David Hildenbrand (Arm)" Date: Fri, 30 Oct 2026 22:34:30 +0100 Subject: [PATCH 40/43] MAINTAINERS: rename and move MEMORY HOT(UN)PLUG Prefix "MEMORY HOT(UN)PLUG" with "MEMORY MANAGEMENT" and put it into the right spot in the MM component section list. Link: https://lore.kernel.org/20261030-maintainers-v1-6-97278f8a5f54@kernel.org Signed-off-by: David Hildenbrand (Arm) Signed-off-by: Andrew Morton Acked-by: Vlastimil Babka (SUSE) Acked-by: Qi Zheng Acked-by: Mike Rapoport (Microsoft) Acked-by: Zi Yan Acked-by: SJ Park Acked-by: Muchun Song Acked-by: Lorenzo Stoakes (ARM) Acked-by: Shakeel Butt Acked-by: SJ Park Acked-by: Kiryl Shutsemau (Meta) Cc: Alistair Popple Cc: Aneesh Kumar K.V Cc: Baolin Wang Cc: Baoquan He Cc: Barry Song Cc: Brendan Jackman Cc: Byungchul Park Cc: Chengming Zhou Cc: Chris Li Cc: Christoph Lameter Cc: Dave Chinner Cc: David Rientjes Cc: Dennis Zhou Cc: Dev Jain Cc: Gregory Price Cc: Hao Li Cc: Harry Yoo Cc: Hugh Dickins Cc: Jann Horn Cc: Johannes Weiner Cc: John Hubbard Cc: Joshua Hahn Cc: Kairui Song Cc: Kemeng Shi Cc: Lance Yang Cc: Liam R. Howlett Cc: Matthew Brost Cc: Miaohe Lin Cc: Michal Hocko Cc: Naoya Horiguchi Cc: Nhat Pham Cc: Nicholas Piggin Cc: Nico Pache Cc: Oscar Salvador Cc: Pedro Falcato Cc: Peter Xu Cc: Peter Zijlstra Cc: Rakie Kim Cc: Rik van Riel Cc: Roman Gushchin Cc: Ryan Roberts Cc: Suren Baghdasaryan Cc: Tejun Heo Cc: Uladzislau Rezki (Sony) Cc: Usama Arif Cc: Will Deacon Cc: Xu Xin Cc: Ying Huang Cc: Youngjun Park Cc: Jason Gunthorpe --- MAINTAINERS | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index d111a58f7cfa..0f2608347180 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17461,22 +17461,6 @@ S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/chanwoo/linux.git F: drivers/devfreq/tegra30-devfreq.c -MEMORY HOT(UN)PLUG -M: David Hildenbrand -M: Oscar Salvador -L: linux-mm@kvack.org -L: linux-cxl@vger.kernel.org -S: Maintained -F: Documentation/ABI/testing/sysfs-devices-memory -F: Documentation/admin-guide/mm/memory-hotplug.rst -F: Documentation/core-api/memory-hotplug.rst -F: drivers/base/memory.c -F: include/linux/memory_hotplug.h -F: include/linux/memremap.h -F: mm/memory_hotplug.c -F: mm/memremap.c -F: tools/testing/selftests/memory-hotplug/ - MEMORY MANAGEMENT M: Andrew Morton L: linux-mm@kvack.org @@ -17614,6 +17598,22 @@ F: include/trace/events/ksm.h F: mm/ksm.c F: mm/mm_slot.h +MEMORY MANAGEMENT - MEMORY HOT(UN)PLUG +M: David Hildenbrand +M: Oscar Salvador +L: linux-mm@kvack.org +L: linux-cxl@vger.kernel.org +S: Maintained +F: Documentation/ABI/testing/sysfs-devices-memory +F: Documentation/admin-guide/mm/memory-hotplug.rst +F: Documentation/core-api/memory-hotplug.rst +F: drivers/base/memory.c +F: include/linux/memory_hotplug.h +F: include/linux/memremap.h +F: mm/memory_hotplug.c +F: mm/memremap.c +F: tools/testing/selftests/memory-hotplug/ + MEMORY MANAGEMENT - MEMORY MAP M: Muchun Song M: Mike Rapoport From 4e8a639c281b3cc6a8b1f2c3885a07b0648791c8 Mon Sep 17 00:00:00 2001 From: "David Hildenbrand (Arm)" Date: Fri, 30 Oct 2026 22:34:31 +0100 Subject: [PATCH 41/43] MAINTAINERS: route more MM changes through mm/core.git Let's start routing more components I (co)maintain through mm/core.git. Update the git trees by pointing them at the for-next branch: - MEMORY MANAGEMENT - BALLOON - MEMORY MANAGEMENT - CORE - MEMORY MANAGEMENT - GUP (GET USER PAGES) - MEMORY MANAGEMENT - KSM (Kernel Samepage Merging) - MEMORY MANAGEMENT - MEMORY HOT(UN)PLUG - MEMORY MANAGEMENT - MIGRATION - MEMORY MANAGEMENT - NUMA PLACEMENT - MEMORY MANAGEMENT - RMAP (REVERSE MAPPING) - MEMORY MANAGEMENT - THP (TRANSPARENT HUGE PAGE) Leave out MISC for now. This change doesn't imply that all changes to these components will immediately be routed through that tree; in particular changes that cover other components, or severe conflicts will require coordination with Andrew's tree. So this marks merely the start of "mm/core.git will start picking relevant material for these components". Remove Andrew from these components to set the expectation that he won't be the primary person picking stuff for these components; he is listed as maintainer under the generic "MEMORY MANAGEMENT" and will get CCed on most changes, and can keep an eye on stuff that falls through the cracks (thanks!). While at it, drop the link to http://www.linux-mm.org: it's not particularly useful in 2026. Link: https://lore.kernel.org/20261030-maintainers-v1-7-97278f8a5f54@kernel.org Signed-off-by: David Hildenbrand (Arm) Signed-off-by: Andrew Morton Acked-by: Vlastimil Babka (SUSE) Acked-by: Qi Zheng Acked-by: Mike Rapoport (Microsoft) Acked-by: Zi Yan Acked-by: SJ Park Acked-by: Lorenzo Stoakes (ARM) Acked-by: Shakeel Butt Acked-by: SJ Park Acked-by: Kiryl Shutsemau (Meta) Cc: Alistair Popple Cc: Aneesh Kumar K.V Cc: Baolin Wang Cc: Baoquan He Cc: Barry Song Cc: Brendan Jackman Cc: Byungchul Park Cc: Chengming Zhou Cc: Chris Li Cc: Christoph Lameter Cc: Dave Chinner Cc: David Rientjes Cc: Dennis Zhou Cc: Dev Jain Cc: Gregory Price Cc: Hao Li Cc: Harry Yoo Cc: Hugh Dickins Cc: Jann Horn Cc: Johannes Weiner Cc: John Hubbard Cc: Joshua Hahn Cc: Kairui Song Cc: Kemeng Shi Cc: Lance Yang Cc: Liam R. Howlett Cc: Matthew Brost Cc: Miaohe Lin Cc: Michal Hocko Cc: Muchun Song Cc: Naoya Horiguchi Cc: Nhat Pham Cc: Nicholas Piggin Cc: Nico Pache Cc: Oscar Salvador Cc: Pedro Falcato Cc: Peter Xu Cc: Peter Zijlstra Cc: Rakie Kim Cc: Rik van Riel Cc: Roman Gushchin Cc: Ryan Roberts Cc: Suren Baghdasaryan Cc: Tejun Heo Cc: Uladzislau Rezki (Sony) Cc: Usama Arif Cc: Will Deacon Cc: Xu Xin Cc: Ying Huang Cc: Youngjun Park Cc: Jason Gunthorpe --- MAINTAINERS | 31 +++++++++---------------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 0f2608347180..11869861aa0d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17484,18 +17484,15 @@ F: mm/alloc_tag.c F: tools/testing/selftests/alloc_tag/ MEMORY MANAGEMENT - BALLOON -M: Andrew Morton M: David Hildenbrand L: linux-mm@kvack.org L: virtualization@lists.linux.dev S: Maintained -W: http://www.linux-mm.org -T: git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm +T: git git://git.kernel.org/pub/scm/linux/kernel/git/mm/core.git for-next F: include/linux/balloon.h F: mm/balloon.c MEMORY MANAGEMENT - CORE -M: Andrew Morton M: David Hildenbrand R: Lorenzo Stoakes R: Liam R. Howlett @@ -17505,8 +17502,7 @@ R: Suren Baghdasaryan R: Michal Hocko L: linux-mm@kvack.org S: Maintained -W: http://www.linux-mm.org -T: git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm +T: git git://git.kernel.org/pub/scm/linux/kernel/git/mm/core.git for-next F: Documentation/admin-guide/sysctl/vm.rst F: Documentation/core-api/gfp_mask-from-fs-io.rst F: fs/proc/meminfo.c @@ -17564,15 +17560,13 @@ F: include/linux/execmem.h F: mm/execmem.c MEMORY MANAGEMENT - GUP (GET USER PAGES) -M: Andrew Morton M: David Hildenbrand R: Jason Gunthorpe R: John Hubbard R: Peter Xu L: linux-mm@kvack.org S: Maintained -W: http://www.linux-mm.org -T: git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm +T: git git://git.kernel.org/pub/scm/linux/kernel/git/mm/core.git for-next F: Documentation/core-api/pin_user_pages.rst F: mm/gup.c F: mm/gup_test.c @@ -17582,14 +17576,12 @@ F: tools/testing/selftests/mm/gup.c F: tools/testing/selftests/mm/gup_longterm.c MEMORY MANAGEMENT - KSM (Kernel Samepage Merging) -M: Andrew Morton M: David Hildenbrand R: Xu Xin R: Chengming Zhou L: linux-mm@kvack.org S: Maintained -W: http://www.linux-mm.org -T: git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm +T: git git://git.kernel.org/pub/scm/linux/kernel/git/mm/core.git for-next F: Documentation/ABI/testing/sysfs-kernel-mm-ksm F: Documentation/admin-guide/mm/ksm.rst F: Documentation/mm/ksm.rst @@ -17604,6 +17596,7 @@ M: Oscar Salvador L: linux-mm@kvack.org L: linux-cxl@vger.kernel.org S: Maintained +T: git git://git.kernel.org/pub/scm/linux/kernel/git/mm/core.git for-next F: Documentation/ABI/testing/sysfs-devices-memory F: Documentation/admin-guide/mm/memory-hotplug.rst F: Documentation/core-api/memory-hotplug.rst @@ -17652,7 +17645,6 @@ F: mm/vmscan.c F: mm/workingset.c MEMORY MANAGEMENT - MIGRATION -M: Andrew Morton M: David Hildenbrand R: Zi Yan R: Matthew Brost @@ -17663,8 +17655,7 @@ R: Ying Huang R: Alistair Popple L: linux-mm@kvack.org S: Maintained -W: http://www.linux-mm.org -T: git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm +T: git git://git.kernel.org/pub/scm/linux/kernel/git/mm/core.git for-next F: include/linux/migrate.h F: include/linux/migrate_mode.h F: include/trace/events/migrate.h @@ -17736,7 +17727,6 @@ F: mm/numa_emulation.c F: mm/numa_memblks.c MEMORY MANAGEMENT - NUMA PLACEMENT -M: Andrew Morton M: David Hildenbrand M: Gregory Price R: Zi Yan @@ -17745,8 +17735,7 @@ R: Rakie Kim R: Ying Huang L: linux-mm@kvack.org S: Maintained -W: http://www.linux-mm.org -T: git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm +T: git git://git.kernel.org/pub/scm/linux/kernel/git/mm/core.git for-next F: Documentation/ABI/testing/sysfs-kernel-mm-memory-tiers F: Documentation/ABI/testing/sysfs-kernel-mm-mempolicy F: Documentation/ABI/testing/sysfs-kernel-mm-mempolicy-weighted-interleave @@ -17820,7 +17809,6 @@ F: mm/vmscan.c F: mm/workingset.c MEMORY MANAGEMENT - RMAP (REVERSE MAPPING) -M: Andrew Morton M: David Hildenbrand M: Lorenzo Stoakes R: Rik van Riel @@ -17831,6 +17819,7 @@ R: Jann Horn R: Lance Yang L: linux-mm@kvack.org S: Maintained +T: git git://git.kernel.org/pub/scm/linux/kernel/git/mm/core.git for-next F: include/linux/rmap.h F: mm/interval_tree.c F: mm/page_vma_mapped.c @@ -17888,7 +17877,6 @@ F: mm/swap_table.h F: mm/swapfile.c MEMORY MANAGEMENT - THP (TRANSPARENT HUGE PAGE) -M: Andrew Morton M: David Hildenbrand M: Zi Yan R: Baolin Wang @@ -17902,8 +17890,7 @@ R: Usama Arif R: Kiryl Shutsemau L: linux-mm@kvack.org S: Maintained -W: http://www.linux-mm.org -T: git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm +T: git git://git.kernel.org/pub/scm/linux/kernel/git/mm/core.git for-next F: Documentation/ABI/testing/sysfs-kernel-mm-transparent-hugepage F: Documentation/admin-guide/mm/transhuge.rst F: include/linux/huge_mm.h From a1836d0d6f76b024ef4457424647b98cf88baf67 Mon Sep 17 00:00:00 2001 From: "Vlastimil Babka (SUSE)" Date: Sun, 1 Nov 2026 17:34:48 +0100 Subject: [PATCH 42/43] MAINTAINERS: add Johannes as the page allocator co-maintainer Patch series "MAINTAINERS updates for page and slab allocators". Following David's updates [1], perform similar updates for page and slab allocators to indicate the MM process transition for these subcomponents. Importantly, add Johannes as a page allocator co-maintainer. This patch (of 2): Johannes is a long-time expert and reviewer for the page allocator and has agreed to step up to a co-maintainer role, so do that. Thanks! Additionally, page allocator is joining the MM transition, following the MM core updates from David [2]. Patches will be collected in the mm/page_alloc git tree and integrated via the mm/linux integration tree. To signify the change from Andrew's mm tree, remove Andrew as M: - get_maintainers will continue CCing him on most patches via the catch-all MM entry. David prefers not being added for mail volume reasons. Link: https://lore.kernel.org/20261101-maint-page_alloc-update-v1-0-27e8c246b13d@kernel.org Link: https://lore.kernel.org/20261101-maint-page_alloc-update-v1-1-27e8c246b13d@kernel.org Link: https://lore.kernel.org/all/20261030-maintainers-v1-0-97278f8a5f54@kernel.org/ [1] Link: https://lore.kernel.org/all/20261030-maintainers-v1-7-97278f8a5f54@kernel.org/ [2] Signed-off-by: Vlastimil Babka (SUSE) Signed-off-by: Andrew Morton Acked-by: Zi Yan Acked-by: Harry Yoo (Meta) Acked-by: David Hildenbrand (Arm) Acked-by: Lorenzo Stoakes (ARM) Reviewed-by: Johannes Weiner Acked-by: SJ Park Cc: Hao Li Cc: Christoph Lameter Cc: David Rientjes Cc: Roman Gushchin Cc: Suren Baghdasaryan Cc: Michal Hocko Cc: Brendan Jackman Cc: Liam R. Howlett Cc: Jann Horn Cc: Pedro Falcato Cc: Mike Rapoport Cc: Gregory Price --- MAINTAINERS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 11869861aa0d..4bbdb7b80fae 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17758,15 +17758,15 @@ F: include/uapi/linux/oom.h F: mm/oom_kill.c MEMORY MANAGEMENT - PAGE ALLOCATOR -M: Andrew Morton M: Vlastimil Babka +M: Johannes Weiner R: Suren Baghdasaryan R: Michal Hocko R: Brendan Jackman -R: Johannes Weiner R: Zi Yan L: linux-mm@kvack.org S: Maintained +T: git git://git.kernel.org/pub/scm/linux/kernel/git/mm/page_alloc.git page_alloc/for-next F: include/linux/compaction.h F: include/linux/gfp.h F: include/linux/page-isolation.h From 19eab4cf787c917bfb7615460933e4ae894af65b Mon Sep 17 00:00:00 2001 From: "Vlastimil Babka (SUSE)" Date: Sun, 1 Nov 2026 17:34:49 +0100 Subject: [PATCH 43/43] MAINTAINERS: move SLAB ALLOCATOR under MEMORY MANAGEMENT Following David's consolidation of several other MM subcomponents, do the same for the slab allocator. Rename the section to MEMORY MANAGEMENT - SLAB ALLOCATOR and move it to remain in alphabetical order. Remove Andrew as M: to signify he's not the primary person for picking patches in this subcomponent. get_maintainers will continue CCing him on most patches via the catch-all MM entry. David prefers not being added for mail volume reasons. Indicate the primary integration branch (slab/for-next) in the T: entry. It will now be integrrated via the mm/linux integration tree. Link: https://lore.kernel.org/20261101-maint-page_alloc-update-v1-2-27e8c246b13d@kernel.org Signed-off-by: Vlastimil Babka (SUSE) Signed-off-by: Andrew Morton Acked-by: David Rientjes Acked-by: Harry Yoo (Meta) Acked-by: David Hildenbrand (Arm) Acked-by: Lorenzo Stoakes (ARM) Acked-by: SJ Park Cc: Hao Li Cc: Christoph Lameter Cc: Roman Gushchin Cc: Johannes Weiner Cc: Suren Baghdasaryan Cc: Michal Hocko Cc: Brendan Jackman Cc: Zi Yan Cc: Liam R. Howlett Cc: Jann Horn Cc: Pedro Falcato Cc: Mike Rapoport Cc: Gregory Price --- MAINTAINERS | 59 ++++++++++++++++++++++++++--------------------------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 4bbdb7b80fae..d63d8b215277 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17853,6 +17853,35 @@ F: mm/shmem_quota.c F: tools/testing/selftests/memfd/ F: tools/testing/selftests/tmpfs/ +MEMORY MANAGEMENT - SLAB ALLOCATOR +M: Vlastimil Babka +M: Harry Yoo +R: Hao Li +R: Christoph Lameter +R: David Rientjes +R: Roman Gushchin +L: linux-mm@kvack.org +S: Maintained +T: git git://git.kernel.org/pub/scm/linux/kernel/git/mm/slab.git slab/for-next +F: Documentation/ABI/testing/sysfs-kernel-slab +F: Documentation/admin-guide/mm/slab.rst +F: Documentation/mm/slab.rst +F: include/linux/mempool.h +F: include/linux/slab.h +F: include/trace/events/kmem.h +F: lib/tests/slub_kunit.c +F: mm/failslab.c +F: mm/mempool.c +F: mm/slab.h +F: mm/slab_common.c +F: mm/slub.c +F: scripts/gdb/linux/slab.py +F: tools/cgroup/memcg_slabinfo.py +F: tools/include/linux/slab.h +F: tools/lib/slab.c +F: tools/mm/slabinfo-gnuplot.sh +F: tools/mm/slabinfo.c + MEMORY MANAGEMENT - SWAP M: Andrew Morton M: Chris Li @@ -25852,36 +25881,6 @@ S: Maintained F: Documentation/devicetree/bindings/nvmem/layouts/kontron,sl28-vpd.yaml F: drivers/nvmem/layouts/sl28vpd.c -SLAB ALLOCATOR -M: Vlastimil Babka -M: Harry Yoo -M: Andrew Morton -R: Hao Li -R: Christoph Lameter -R: David Rientjes -R: Roman Gushchin -L: linux-mm@kvack.org -S: Maintained -T: git git://git.kernel.org/pub/scm/linux/kernel/git/mm/slab.git -F: Documentation/ABI/testing/sysfs-kernel-slab -F: Documentation/admin-guide/mm/slab.rst -F: Documentation/mm/slab.rst -F: include/linux/mempool.h -F: include/linux/slab.h -F: include/trace/events/kmem.h -F: lib/tests/slub_kunit.c -F: mm/failslab.c -F: mm/mempool.c -F: mm/slab.h -F: mm/slab_common.c -F: mm/slub.c -F: scripts/gdb/linux/slab.py -F: tools/cgroup/memcg_slabinfo.py -F: tools/include/linux/slab.h -F: tools/lib/slab.c -F: tools/mm/slabinfo-gnuplot.sh -F: tools/mm/slabinfo.c - SLCAN CAN NETWORK DRIVER M: Dario Binacchi L: linux-can@vger.kernel.org