Propagate crash_memaction marks set through madvise() to the crash_memaction registry. Register the folios of a VMA carrying VM_CRASH_MARK with the crash memaction registry as they are mapped, and update the registry when a folio moves.
Marks are processed in rmap since the fault paths, CoW, swapin, migration and khugepaged collapse all converge here. The marks don't affect pinning, reclaim, compaction or hot-unplug. De-regsitering free'd marked pages is done in post_alloc_hook() and the hugetlb pool to account for stale data in free'd pages. The marking code is gated behind a static key that is only enabled when the crash_memaction registry is explicitly enabled by a cmdline argument, so there should be no performance impact unless the feature is actually used. Signed-off-by: Jan Sebastian Götte <[email protected]> Assisted-by: Claude Opus 5 <[email protected]> --- include/linux/crash_memaction.h | 15 +++++++++++++++ include/linux/rmap.h | 5 ++++- mm/huge_memory.c | 2 ++ mm/hugetlb.c | 6 +++--- mm/migrate.c | 2 +- mm/rmap.c | 8 ++++++++ mm/userfaultfd.c | 1 + 7 files changed, 34 insertions(+), 5 deletions(-) diff --git a/include/linux/crash_memaction.h b/include/linux/crash_memaction.h index 5f3e114c60ad..2de60bc12dd9 100644 --- a/include/linux/crash_memaction.h +++ b/include/linux/crash_memaction.h @@ -4,6 +4,7 @@ #include <linux/init.h> #include <linux/jump_label.h> +#include <linux/mm.h> #include <linux/types.h> struct kimage; @@ -50,6 +51,18 @@ static inline void crash_memaction_unmark_pfns(unsigned long pfn, __crash_memaction_unmark_pfns(pfn, nr_pages); } +static inline void crash_mark_pages(struct page *page, int nr_pages, + struct vm_area_struct *vma) +{ + if (!static_branch_unlikely(&crash_memaction_active)) + return; + + if (likely(!(vma->vm_flags & VM_CRASH_MARK))) + return; + + __crash_memaction_mark_pfns(page_to_pfn(page), nr_pages); +} + void crash_memaction_mark(void *addr, size_t size, int types); void crash_memaction_unmark(void *addr, size_t size); @@ -62,6 +75,8 @@ void crash_memaction_unload(struct kimage *image); static inline void crash_memaction_init(void) { } static inline void crash_memaction_unmark_pfns(unsigned long pfn, unsigned long nr_pages) { } +static inline void crash_mark_pages(struct page *page, int nr_pages, + struct vm_area_struct *vma) { } static inline void crash_memaction_mark(void *addr, size_t size, int types) { } static inline void crash_memaction_unmark(void *addr, size_t size) { } static inline int crash_memaction_types(void) { return 0; } diff --git a/include/linux/rmap.h b/include/linux/rmap.h index 74cca0e3c726..1eeb7762b4a3 100644 --- a/include/linux/rmap.h +++ b/include/linux/rmap.h @@ -9,6 +9,7 @@ #include <linux/slab.h> #include <linux/mm.h> #include <linux/rwsem.h> +#include <linux/crash_memaction.h> #include <linux/memcontrol.h> #include <linux/highmem.h> #include <linux/pagemap.h> @@ -472,13 +473,15 @@ static inline int hugetlb_try_share_anon_rmap(struct folio *folio) return 0; } -static inline void hugetlb_add_file_rmap(struct folio *folio) +static inline void hugetlb_add_file_rmap(struct folio *folio, + struct vm_area_struct *vma) { VM_WARN_ON_FOLIO(!folio_test_hugetlb(folio), folio); VM_WARN_ON_FOLIO(folio_test_anon(folio), folio); atomic_inc(&folio->_entire_mapcount); atomic_inc(&folio->_large_mapcount); + crash_mark_pages(&folio->page, folio_nr_pages(folio), vma); } static inline void hugetlb_remove_rmap(struct folio *folio) diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 3fb9504dff7a..adbda968d58e 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -2966,6 +2966,8 @@ int move_pages_huge_pmd(struct mm_struct *mm, pmd_t *dst_pmd, pmd_t *src_pmd, pm folio_move_anon_rmap(src_folio, dst_vma); src_folio->index = linear_anon_page_index(dst_vma, dst_addr); + /* No rmap add, and the two VMAs need not agree on the flag. */ + crash_mark_pages(&src_folio->page, HPAGE_PMD_NR, dst_vma); _dst_pmd = folio_mk_pmd(src_folio, dst_vma->vm_page_prot); /* Follow mremap() behavior and treat the entry dirty after the move */ diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 36f0c0d8f5f1..309be0b301ca 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -5063,7 +5063,7 @@ int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src, * sleep during the process. */ if (!folio_test_anon(pte_folio)) { - hugetlb_add_file_rmap(pte_folio); + hugetlb_add_file_rmap(pte_folio, dst_vma); } else if (hugetlb_try_dup_anon_rmap(pte_folio, src_vma)) { pte_t src_pte_old = entry; struct folio *new_folio; @@ -5982,7 +5982,7 @@ static vm_fault_t hugetlb_no_page(struct address_space *mapping, if (new_anon_folio) hugetlb_add_new_anon_rmap(folio, vma, vmf->address); else - hugetlb_add_file_rmap(folio); + hugetlb_add_file_rmap(folio, vma); new_pte = make_huge_pte(vma, folio, vma->vm_flags & VM_SHARED); /* * If this pte was previously wr-protected, keep it wr-protected even @@ -6502,7 +6502,7 @@ int hugetlb_mfill_atomic_pte(pte_t *dst_pte, goto out_release_unlock; if (folio_in_pagecache) - hugetlb_add_file_rmap(folio); + hugetlb_add_file_rmap(folio, dst_vma); else hugetlb_add_new_anon_rmap(folio, dst_vma, dst_addr); diff --git a/mm/migrate.c b/mm/migrate.c index 7bdcdb57652f..467b695e6dd6 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -434,7 +434,7 @@ static bool remove_migration_pte(struct folio *folio, hugetlb_add_anon_rmap(folio, vma, pvmw.address, rmap_flags); else - hugetlb_add_file_rmap(folio); + hugetlb_add_file_rmap(folio, vma); set_huge_pte_at(vma->vm_mm, pvmw.address, pvmw.pte, pte, psize); } else diff --git a/mm/rmap.c b/mm/rmap.c index fbd66a2823b7..21183478f606 100644 --- a/mm/rmap.c +++ b/mm/rmap.c @@ -1582,6 +1582,8 @@ static __always_inline void __folio_add_anon_rmap(struct folio *folio, */ if (folio_nr_pages(folio) == nr_pages) mlock_vma_folio(folio, vma); + + crash_mark_pages(page, nr_pages, vma); } /** @@ -1703,6 +1705,8 @@ void folio_add_new_anon_rmap(struct folio *folio, struct vm_area_struct *vma, __folio_mod_stat(folio, nr, nr_pmdmapped); mod_mthp_stat(folio_order(folio), MTHP_STAT_NR_ANON, 1); + + crash_mark_pages(&folio->page, nr, vma); } static __always_inline void __folio_add_file_rmap(struct folio *folio, @@ -1721,6 +1725,8 @@ static __always_inline void __folio_add_file_rmap(struct folio *folio, */ if (folio_nr_pages(folio) == nr_pages) mlock_vma_folio(folio, vma); + + crash_mark_pages(page, nr_pages, vma); } /** @@ -3190,6 +3196,7 @@ void hugetlb_add_anon_rmap(struct folio *folio, struct vm_area_struct *vma, SetPageAnonExclusive(&folio->page); VM_WARN_ON_FOLIO(folio_entire_mapcount(folio) > 1 && PageAnonExclusive(&folio->page), folio); + crash_mark_pages(&folio->page, folio_nr_pages(folio), vma); } void hugetlb_add_new_anon_rmap(struct folio *folio, @@ -3204,5 +3211,6 @@ void hugetlb_add_new_anon_rmap(struct folio *folio, folio_clear_hugetlb_restore_reserve(folio); __folio_set_anon(folio, vma, address, true); SetPageAnonExclusive(&folio->page); + crash_mark_pages(&folio->page, folio_nr_pages(folio), vma); } #endif /* CONFIG_HUGETLB_PAGE */ diff --git a/mm/userfaultfd.c b/mm/userfaultfd.c index b242fa8b22c8..bb0a35d59daa 100644 --- a/mm/userfaultfd.c +++ b/mm/userfaultfd.c @@ -1326,6 +1326,7 @@ static long move_present_ptes(struct mm_struct *mm, folio_move_anon_rmap(src_folio, dst_vma); src_folio->index = linear_anon_page_index(dst_vma, dst_addr); + crash_mark_pages(&src_folio->page, 1, dst_vma); orig_dst_pte = folio_mk_pte(src_folio, dst_vma->vm_page_prot); /* Set soft dirty bit so userspace can notice the pte was moved */ -- 2.55.0

