[RFC PATCH 3/5] mm/damon/vaddr: support apply_probe

From: SJ Park

Date: Sat Sep 05 2026 - 16:31:06 EST


DAMON virtual address space operation set (vaddr) is not supporting
apply_probe. Add a minimum support. Do not support hugetlb pages and
PGIDLE_UNSET filter type for simplicity.

Signed-off-by: SJ Park <sj@xxxxxxxxxx>
---
mm/damon/vaddr.c | 123 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 123 insertions(+)

diff --git a/mm/damon/vaddr.c b/mm/damon/vaddr.c
index 20f4784f17817..7c548ec0cf6b5 100644
--- a/mm/damon/vaddr.c
+++ b/mm/damon/vaddr.c
@@ -522,6 +522,128 @@ static void damon_va_prep_probes(struct damon_ctx *ctx, bool set_samples)
}
}

+static bool damon_va_filter_pass(struct folio *folio, struct damon_probe *p)
+{
+ struct damon_filter *f;
+ bool pass = true;
+
+ damon_for_each_filter(f, p) {
+ if (damon_ops_filter_match(f, folio)) {
+ pass = f->allow;
+ break;
+ }
+ pass = !f->allow;
+ }
+ return pass;
+}
+
+struct damon_va_probe_walk_private {
+ struct damon_ctx *ctx;
+ struct damon_region *r;
+};
+
+static void damon_va_probe_folio(struct damon_ctx *ctx,
+ struct damon_region *r, struct folio *folio)
+{
+ struct damon_probe *probe;
+ int i = 0;
+
+ damon_for_each_probe(probe, ctx) {
+ if (damon_va_filter_pass(folio, probe))
+ r->probe_hits[i]++;
+ i++;
+ }
+}
+
+static int damon_va_probe_pmd_entry(pmd_t *pmd, unsigned long addr,
+ unsigned long next, struct mm_walk *walk)
+{
+ pte_t *pte;
+ pte_t ptent;
+ spinlock_t *ptl;
+ struct folio *folio;
+ struct damon_va_probe_walk_private *priv = walk->private;
+
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+ ptl = pmd_trans_huge_lock(pmd, walk->vma);
+ if (ptl) {
+ pmd_t pmde = pmdp_get(pmd);
+
+ if (!pmd_present(pmde))
+ goto huge_out;
+ folio = vm_normal_folio_pmd(walk->vma, addr, pmde);
+ if (!folio)
+ goto huge_out;
+ damon_va_probe_folio(priv->ctx, priv->r, folio);
+
+huge_out:
+ spin_unlock(ptl);
+ return 0;
+ }
+#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
+
+ pte = pte_offset_map_lock(walk->mm, pmd, addr, &ptl);
+ if (!pte)
+ return 0;
+ ptent = ptep_get(pte);
+ if (!pte_present(ptent))
+ goto out;
+ folio = vm_normal_folio(walk->vma, addr, ptent);
+ if (!folio)
+ goto out;
+ damon_va_probe_folio(priv->ctx, priv->r, folio);
+
+out:
+ pte_unmap_unlock(pte, ptl);
+ return 0;
+}
+
+static void __damon_va_apply_probes(struct damon_ctx *ctx,
+ struct mm_struct *mm, struct damon_region *r)
+{
+ struct damon_va_probe_walk_private arg = {
+ .ctx = ctx,
+ .r = r,
+ };
+ struct mm_walk_ops damon_probe_walk_ops = {
+ .pmd_entry = damon_va_probe_pmd_entry,
+ .hugetlb_entry = NULL,
+ };
+ unsigned long addr = r->sampling_addr;
+
+ if (!mm)
+ return;
+
+ damon_va_walk_page_range(mm, addr, addr + 1, &damon_probe_walk_ops,
+ &arg);
+}
+
+static unsigned int damon_va_apply_probes(struct damon_ctx *ctx,
+ bool set_samples, bool return_max_wsum)
+{
+ struct damon_target *t;
+ struct mm_struct *mm;
+ struct damon_region *r;
+ unsigned int max_wsum = 0;
+
+ damon_for_each_target(t, ctx) {
+ mm = damon_get_mm(t);
+ damon_for_each_region(r, t) {
+ if (set_samples)
+ r->sampling_addr = damon_rand(ctx, r->ar.start,
+ r->ar.end);
+ __damon_va_apply_probes(ctx, mm, r);
+ if (return_max_wsum)
+ max_wsum = max(damon_probe_hits_wsum(r, false,
+ ctx), max_wsum);
+ }
+ if (mm)
+ mmput(mm);
+ }
+
+ return max_wsum;
+}
+
static bool damos_va_filter_young_match(struct damos_filter *filter,
struct folio *folio, struct vm_area_struct *vma,
unsigned long addr, pte_t *ptep, pmd_t *pmdp)
@@ -951,6 +1073,7 @@ static int __init damon_va_initcall(void)
.prepare_access_checks = damon_va_prepare_access_checks,
.check_accesses = damon_va_check_accesses,
.prep_probes = damon_va_prep_probes,
+ .apply_probes = damon_va_apply_probes,
.target_valid = damon_va_target_valid,
.cleanup_target = damon_va_cleanup_target,
.apply_scheme = damon_va_apply_scheme,
--
2.47.3