[RFC PATCH v3 1/9] mm/damon/paddr: remove page_fault access check primitive

From: Ravi Jonnalagadda

Date: Sat Oct 03 2026 - 17:09:40 EST


The page_fault access check primitive reports faults through
damon_report_page_fault() into the global, mutex-protected
damon_access_reports[] buffer. A later patch in this series replaces that
buffer with per-context rings fed only by perf-event probes, which leaves
the page-fault producer with nowhere to report. Remove the producer here
so no report is silently dropped.

Remove damon_pa_prepare_access_checks_faults() and its supporting
damon_pa_change_protection()/damon_pa_change_protection_one() helpers,
and the page_fault dispatch in damon_pa_prepare_access_checks(). Remove
damon_report_page_fault() and its sole caller, do_damon_page(), along
with the two page-fault-handler dispatch sites in mm/memory.c that
selected it over the ordinary NUMA-hinting fault path.

Both removed pieces predate this series and are unrelated to the
perf-event probe infrastructure it adds.

Signed-off-by: Ravi Jonnalagadda <ravis.opensrc@xxxxxxxxx>
---
include/linux/damon.h | 10 ---------
mm/damon/core.c | 20 ------------------
mm/damon/paddr.c | 57 ---------------------------------------------------
mm/memory.c | 53 -----------------------------------------------
4 files changed, 140 deletions(-)

diff --git a/include/linux/damon.h b/include/linux/damon.h
index 3d0c05df3258..10582f669673 100644
--- a/include/linux/damon.h
+++ b/include/linux/damon.h
@@ -1299,13 +1299,6 @@ int damon_call(struct damon_ctx *ctx, struct damon_call_control *control);
int damos_walk(struct damon_ctx *ctx, struct damos_walk_control *control);

void damon_report_access(struct damon_access_report *report);
-#ifdef CONFIG_MMU
-void damon_report_page_fault(struct vm_fault *vmf, bool huge_pmd);
-#else
-static inline void damon_report_page_fault(struct vm_fault *vmf, bool huge_pmd)
-{
-}
-#endif

int damon_set_region_system_rams_default(struct damon_target *t,
unsigned long *start, unsigned long *end,
@@ -1323,9 +1316,6 @@ unsigned long damon_alloced_bytes(void);
static inline void damon_report_access(struct damon_access_report *report)
{
}
-static inline void damon_report_page_fault(struct vm_fault *vmf, bool huge_pmd)
-{
-}

#endif /* CONFIG_DAMON */

diff --git a/mm/damon/core.c b/mm/damon/core.c
index ddf9c08aa6c1..886e068e7844 100644
--- a/mm/damon/core.c
+++ b/mm/damon/core.c
@@ -2544,26 +2544,6 @@ void damon_report_access(struct damon_access_report *report)
mutex_unlock(&damon_access_reports_lock);
}

-#ifdef CONFIG_MMU
-void damon_report_page_fault(struct vm_fault *vmf, bool huge_pmd)
-{
- struct damon_access_report access_report = {
- .vaddr = vmf->address,
- .size = 1, /* todo: set appripriately */
- .cpu = smp_processor_id(),
- .tid = task_pid_vnr(current),
- .is_write = vmf->flags & FAULT_FLAG_WRITE,
- };
-
- if (huge_pmd)
- access_report.paddr = PFN_PHYS(pmd_pfn(vmf->orig_pmd));
- else
- access_report.paddr = PFN_PHYS(pte_pfn(vmf->orig_pte));
-
- damon_report_access(&access_report);
-}
-#endif
-
/*
* Reset the aggregated monitoring results ('nr_accesses' of each region).
*/
diff --git a/mm/damon/paddr.c b/mm/damon/paddr.c
index f4fa7c231e55..65a5b3269d1d 100644
--- a/mm/damon/paddr.c
+++ b/mm/damon/paddr.c
@@ -67,67 +67,10 @@ static void damon_pa_prepare_access_checks_abit(struct damon_ctx *ctx)
}
}

-static bool damon_pa_change_protection_one(struct folio *folio,
- struct vm_area_struct *vma, unsigned long addr, void *arg)
-{
- /* todo: batch or remove tlb flushing */
- struct mmu_gather tlb;
-
- if (!vma_is_accessible(vma))
- return true;
-
- tlb_gather_mmu(&tlb, vma->vm_mm);
-
- change_protection(&tlb, vma, addr, addr + PAGE_SIZE, MM_CP_DAMON);
-
- tlb_finish_mmu(&tlb);
- return true;
-}
-
-static void damon_pa_change_protection(unsigned long paddr)
-{
- struct folio *folio = damon_get_folio(PHYS_PFN(paddr));
- struct rmap_walk_control rwc = {
- .rmap_one = damon_pa_change_protection_one,
- .anon_lock = folio_lock_anon_vma_read,
- };
- bool need_lock;
-
- if (!folio)
- return;
- if (!folio_mapped(folio) || !folio_raw_mapping(folio))
- return;
-
- need_lock = !folio_test_anon(folio) || folio_test_ksm(folio);
- if (need_lock && !folio_trylock(folio))
- return;
-
- rmap_walk(folio, &rwc);
-
- if (need_lock)
- folio_unlock(folio);
-}
-
-static void damon_pa_prepare_access_checks_faults(struct damon_ctx *ctx)
-{
- struct damon_target *t;
- struct damon_region *r;
-
- damon_for_each_target(t, ctx) {
- damon_for_each_region(r, t) {
- r->sampling_addr = damon_rand(ctx, r->ar.start,
- r->ar.end);
- damon_pa_change_protection(r->sampling_addr);
- }
- }
-}
-
static void damon_pa_prepare_access_checks(struct damon_ctx *ctx)
{
if (ctx->sample_control.primitives_enabled.page_table)
damon_pa_prepare_access_checks_abit(ctx);
- if (ctx->sample_control.primitives_enabled.page_fault)
- damon_pa_prepare_access_checks_faults(ctx);
}

static bool damon_pa_young(phys_addr_t paddr)
diff --git a/mm/memory.c b/mm/memory.c
index 41278e32dde6..44034d5b32ab 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -6565,54 +6565,6 @@ static void fix_spurious_fault(struct vm_fault *vmf,
}
}

-/*
- * NOTE: This is only poc purpose "hack" that will not be upstreamed as is.
- * More discussions between all stakeholders including maintainers of MM core,
- * NUMA balancing, and DAMON should be made to make this upstreamable.
- * (https://lore.kernel.org/20251128193947.80866-1-sj@xxxxxxxxxx)
- *
- * This function is called from page fault handler, for page faults on
- * P{TE,MD}-protected but vma-accessible pages. DAMON is making the fake
- * protection for access sampling purpose. This function simply clear the
- * protection and report this access to DAMON, by calling
- * damon_report_page_fault().
- *
- * The protection clear code is copied from NUMA fault handling code for PTE.
- * Again, this is only poc purpose "hack" to show what information DAMON want
- * from page fault events, rather than an upstream-aimed version.
- */
-static vm_fault_t do_damon_page(struct vm_fault *vmf, bool huge_pmd)
-{
- struct vm_area_struct *vma = vmf->vma;
- struct folio *folio;
- pte_t pte, old_pte;
- bool writable = false, ignore_writable = false;
- bool pte_write_upgrade = vma_wants_manual_pte_write_upgrade(vma);
-
- spin_lock(vmf->ptl);
- old_pte = ptep_get(vmf->pte);
- if (unlikely(!pte_same(old_pte, vmf->orig_pte))) {
- pte_unmap_unlock(vmf->pte, vmf->ptl);
- return 0;
- }
- pte = pte_modify(old_pte, vma->vm_page_prot);
- writable = pte_write(pte);
- if (!writable && pte_write_upgrade &&
- can_change_pte_writable(vma, vmf->address, pte))
- writable = true;
- folio = vm_normal_folio(vma, vmf->address, pte);
- if (folio && folio_test_large(folio))
- numa_rebuild_large_mapping(vmf, vma, folio, pte,
- ignore_writable, pte_write_upgrade);
- else
- numa_rebuild_single_mapping(vmf, vma, vmf->address, vmf->pte,
- writable);
- pte_unmap_unlock(vmf->pte, vmf->ptl);
-
- damon_report_page_fault(vmf, huge_pmd);
- return 0;
-}
-
/*
* These routines also need to handle stuff like marking pages dirty
* and/or accessed for architectures that don't do it in hardware (most
@@ -6686,8 +6638,6 @@ static vm_fault_t handle_pte_fault(struct vm_fault *vmf)
*/
if (userfaultfd_pte_rwp(vmf->vma, vmf->orig_pte))
return do_uffd_rwp(vmf);
- if (sysctl_numa_balancing_mode == NUMA_BALANCING_DISABLED)
- return do_damon_page(vmf, false);
return do_numa_page(vmf);
}

@@ -6806,9 +6756,6 @@ static vm_fault_t __handle_mm_fault(struct vm_area_struct *vma,
if (pmd_protnone(vmf.orig_pmd) && vma_is_accessible(vma)) {
if (userfaultfd_huge_pmd_rwp(vma, vmf.orig_pmd))
return do_huge_pmd_uffd_rwp(&vmf);
- if (sysctl_numa_balancing_mode ==
- NUMA_BALANCING_DISABLED)
- return do_damon_page(&vmf, true);
return do_huge_pmd_numa_page(&vmf);
}


--
Git-157)