* [PATCH v2 1/3] mm/damon: move damon_hugetlb_mkold() from vaddr to ops-common
2026-09-02 2:56 [PATCH v2 0/3] mm/damon: support access monitoring of hugetlb-backed memory Krishna Iyer
@ 2026-09-02 2:56 ` Krishna Iyer
2026-09-02 3:14 ` sashiko-bot
2026-09-02 2:56 ` [PATCH v2 2/3] mm/damon/ops-common: handle hugetlb folios in folio mkold/young rmap walkers Krishna Iyer
` (2 subsequent siblings)
3 siblings, 1 reply; 13+ messages in thread
From: Krishna Iyer @ 2026-09-02 2:56 UTC (permalink / raw)
To: SJ Park; +Cc: Andrew Morton, damon, linux-mm, linux-kernel
damon_hugetlb_mkold() clears the accessed bit of a hugetlb-mapping huge
PTE and propagates the aging to secondary MMUs via
mmu_notifier_clear_young(), spanning the whole huge page size. It
currently lives in vaddr.c, and is thus usable only by the virtual
address space monitoring operations set.
The physical address space monitoring operations set will need the same
logic, to support access monitoring of hugetlb-backed memory. Move the
function to ops-common as-is, with no behavioral change. A follow-up
change will use it from the folio-granular rmap walkers.
Assisted-by: Claude:claude-fable-5
Signed-off-by: Krishna Iyer <kiyer@crusoe.ai>
Reviewed-by: SJ Park <sj@kernel.org>
---
mm/damon/ops-common.c | 30 ++++++++++++++++++++++++++++++
mm/damon/ops-common.h | 9 +++++++++
mm/damon/vaddr.c | 27 ---------------------------
3 files changed, 39 insertions(+), 27 deletions(-)
diff --git a/mm/damon/ops-common.c b/mm/damon/ops-common.c
index fbda70d8ea4d..f5fe92b825bb 100644
--- a/mm/damon/ops-common.c
+++ b/mm/damon/ops-common.c
@@ -3,6 +3,7 @@
* Common Code for Data Access Monitoring
*/
+#include <linux/hugetlb.h>
#include <linux/migrate.h>
#include <linux/mmu_notifier.h>
#include <linux/page_idle.h>
@@ -98,6 +99,35 @@ void damon_pmdp_mkold(pmd_t *pmd, struct vm_area_struct *vma, unsigned long addr
#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
}
+#ifdef CONFIG_HUGETLB_PAGE
+void damon_hugetlb_mkold(pte_t *pte, struct mm_struct *mm,
+ struct vm_area_struct *vma, unsigned long addr)
+{
+ bool referenced = false;
+ pte_t entry = huge_ptep_get(mm, addr, pte);
+ struct folio *folio = pfn_folio(pte_pfn(entry));
+ unsigned long psize = huge_page_size(hstate_vma(vma));
+
+ folio_get(folio);
+
+ if (pte_young(entry)) {
+ referenced = true;
+ entry = pte_mkold(entry);
+ set_huge_pte_at(mm, addr, pte, entry, psize);
+ }
+
+ if (mmu_notifier_clear_young(mm, addr,
+ addr + huge_page_size(hstate_vma(vma))))
+ referenced = true;
+
+ if (referenced)
+ folio_set_young(folio);
+
+ folio_set_idle(folio);
+ folio_put(folio);
+}
+#endif /* CONFIG_HUGETLB_PAGE */
+
#define DAMON_MAX_SUBSCORE (100)
#define DAMON_MAX_AGE_IN_LOG (32)
diff --git a/mm/damon/ops-common.h b/mm/damon/ops-common.h
index 38d295488fa1..f7811c9c7a02 100644
--- a/mm/damon/ops-common.h
+++ b/mm/damon/ops-common.h
@@ -9,6 +9,15 @@ struct folio *damon_get_folio(unsigned long pfn);
void damon_ptep_mkold(pte_t *pte, struct vm_area_struct *vma, unsigned long addr);
void damon_pmdp_mkold(pmd_t *pmd, struct vm_area_struct *vma, unsigned long addr);
+#ifdef CONFIG_HUGETLB_PAGE
+void damon_hugetlb_mkold(pte_t *pte, struct mm_struct *mm,
+ struct vm_area_struct *vma, unsigned long addr);
+#else
+static inline void damon_hugetlb_mkold(pte_t *pte, struct mm_struct *mm,
+ struct vm_area_struct *vma, unsigned long addr)
+{
+}
+#endif /* CONFIG_HUGETLB_PAGE */
void damon_folio_mkold(struct folio *folio);
bool damon_folio_young(struct folio *folio);
diff --git a/mm/damon/vaddr.c b/mm/damon/vaddr.c
index 0648400b2d65..15379d984689 100644
--- a/mm/damon/vaddr.c
+++ b/mm/damon/vaddr.c
@@ -293,33 +293,6 @@ static int damon_mkold_pmd_entry(pmd_t *pmd, unsigned long addr,
}
#ifdef CONFIG_HUGETLB_PAGE
-static void damon_hugetlb_mkold(pte_t *pte, struct mm_struct *mm,
- struct vm_area_struct *vma, unsigned long addr)
-{
- bool referenced = false;
- pte_t entry = huge_ptep_get(mm, addr, pte);
- struct folio *folio = pfn_folio(pte_pfn(entry));
- unsigned long psize = huge_page_size(hstate_vma(vma));
-
- folio_get(folio);
-
- if (pte_young(entry)) {
- referenced = true;
- entry = pte_mkold(entry);
- set_huge_pte_at(mm, addr, pte, entry, psize);
- }
-
- if (mmu_notifier_clear_young(mm, addr,
- addr + huge_page_size(hstate_vma(vma))))
- referenced = true;
-
- if (referenced)
- folio_set_young(folio);
-
- folio_set_idle(folio);
- folio_put(folio);
-}
-
static int damon_mkold_hugetlb_entry(pte_t *pte, unsigned long hmask,
unsigned long addr, unsigned long end,
struct mm_walk *walk)
--
2.54.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH v2 1/3] mm/damon: move damon_hugetlb_mkold() from vaddr to ops-common
2026-09-02 2:56 ` [PATCH v2 1/3] mm/damon: move damon_hugetlb_mkold() from vaddr to ops-common Krishna Iyer
@ 2026-09-02 3:14 ` sashiko-bot
2026-09-02 4:51 ` SJ Park
0 siblings, 1 reply; 13+ messages in thread
From: sashiko-bot @ 2026-09-02 3:14 UTC (permalink / raw)
To: Krishna Iyer; +Cc: damon
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
Pre-existing issues:
- [Critical] Non-atomic read-modify-write of huge PTE in damon_hugetlb_mkold clobbers hardware-updated dirty bits.
- [High] Unaligned addresses in DAMON mkold functions cause incorrect secondary MMU aging.
--
commit c27851bf8f97112ca3d3c8258d2ecf636424ff88
Author: Krishna Iyer <kiyer@crusoe.ai>
mm/damon: move damon_hugetlb_mkold() from vaddr to ops-common
This commit moves the damon_hugetlb_mkold function from the virtual address
space monitoring operations set to the common operations set. The physical
address space monitoring operations set will now be able to use it to
support access monitoring of hugetlb-backed memory.
> diff --git a/mm/damon/ops-common.c b/mm/damon/ops-common.c
> index fbda70d8ea4d0..f5fe92b825bbc 100644
> --- a/mm/damon/ops-common.c
> +++ b/mm/damon/ops-common.c
[ ... ]
> @@ -98,6 +99,35 @@ void damon_pmdp_mkold(pmd_t *pmd, struct vm_area_struct *vma, unsigned long addr
> #endif /* CONFIG_TRANSPARENT_HUGEPAGE */
> }
>
> +#ifdef CONFIG_HUGETLB_PAGE
> +void damon_hugetlb_mkold(pte_t *pte, struct mm_struct *mm,
> + struct vm_area_struct *vma, unsigned long addr)
> +{
> + bool referenced = false;
> + pte_t entry = huge_ptep_get(mm, addr, pte);
> + struct folio *folio = pfn_folio(pte_pfn(entry));
> + unsigned long psize = huge_page_size(hstate_vma(vma));
> +
> + folio_get(folio);
> +
> + if (pte_young(entry)) {
> + referenced = true;
> + entry = pte_mkold(entry);
> + set_huge_pte_at(mm, addr, pte, entry, psize);
> + }
[Severity: Critical]
This is a pre-existing issue, but does this non-atomic sequence overwrite
the PTE and silently clobber concurrent hardware updates to the dirty bit?
In damon_hugetlb_mkold(), the entry is read via huge_ptep_get(), manually
cleared of the young bit with pte_mkold(), and unconditionally written back
with set_huge_pte_at(). A hardware update to the dirty bit occurring between
the read and the write could be lost. If the dirty bit is overwritten with a
stale clean copy, data could be reclaimed and lost instead of being written
back.
> +
> + if (mmu_notifier_clear_young(mm, addr,
> + addr + huge_page_size(hstate_vma(vma))))
[Severity: High]
This is a pre-existing issue, but could passing an unaligned addr to
mmu_notifier_clear_young() cause incorrect secondary MMU aging?
When mmu_notifier_clear_young() receives a range that crosses page
boundaries due to an unaligned start address, the secondary MMU (such as
KVM) may age multiple adjacent host pages instead of just the target page.
This would corrupt working set statistics for adjacent pages and lead to
incorrect memory management, like reclaiming hot pages.
This unaligned addr behavior appears to originate from damon_rand() and is
also present in damon_ptep_mkold() and damon_pmdp_mkold().
> + referenced = true;
> +
> + if (referenced)
> + folio_set_young(folio);
> +
> + folio_set_idle(folio);
> + folio_put(folio);
> +}
> +#endif /* CONFIG_HUGETLB_PAGE */
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260902025700.17975-1-kiyer@crusoe.ai?part=1
^ permalink raw reply [flat|nested] 13+ messages in thread* Re: [PATCH v2 1/3] mm/damon: move damon_hugetlb_mkold() from vaddr to ops-common
2026-09-02 3:14 ` sashiko-bot
@ 2026-09-02 4:51 ` SJ Park
0 siblings, 0 replies; 13+ messages in thread
From: SJ Park @ 2026-09-02 4:51 UTC (permalink / raw)
To: sashiko-bot; +Cc: SJ Park, Krishna Iyer, damon
On Wed, 02 Sep 2026 03:14:22 +0000 sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
>
> Pre-existing issues:
> - [Critical] Non-atomic read-modify-write of huge PTE in damon_hugetlb_mkold clobbers hardware-updated dirty bits.
> - [High] Unaligned addresses in DAMON mkold functions cause incorrect secondary MMU aging.
> --
>
> commit c27851bf8f97112ca3d3c8258d2ecf636424ff88
> Author: Krishna Iyer <kiyer@crusoe.ai>
>
> mm/damon: move damon_hugetlb_mkold() from vaddr to ops-common
>
> This commit moves the damon_hugetlb_mkold function from the virtual address
> space monitoring operations set to the common operations set. The physical
> address space monitoring operations set will now be able to use it to
> support access monitoring of hugetlb-backed memory.
>
> > diff --git a/mm/damon/ops-common.c b/mm/damon/ops-common.c
> > index fbda70d8ea4d0..f5fe92b825bbc 100644
> > --- a/mm/damon/ops-common.c
> > +++ b/mm/damon/ops-common.c
> [ ... ]
> > @@ -98,6 +99,35 @@ void damon_pmdp_mkold(pmd_t *pmd, struct vm_area_struct *vma, unsigned long addr
> > #endif /* CONFIG_TRANSPARENT_HUGEPAGE */
> > }
> >
> > +#ifdef CONFIG_HUGETLB_PAGE
> > +void damon_hugetlb_mkold(pte_t *pte, struct mm_struct *mm,
> > + struct vm_area_struct *vma, unsigned long addr)
> > +{
> > + bool referenced = false;
> > + pte_t entry = huge_ptep_get(mm, addr, pte);
> > + struct folio *folio = pfn_folio(pte_pfn(entry));
> > + unsigned long psize = huge_page_size(hstate_vma(vma));
> > +
> > + folio_get(folio);
> > +
> > + if (pte_young(entry)) {
> > + referenced = true;
> > + entry = pte_mkold(entry);
> > + set_huge_pte_at(mm, addr, pte, entry, psize);
> > + }
>
> [Severity: Critical]
> This is a pre-existing issue, but does this non-atomic sequence overwrite
> the PTE and silently clobber concurrent hardware updates to the dirty bit?
>
> In damon_hugetlb_mkold(), the entry is read via huge_ptep_get(), manually
> cleared of the young bit with pte_mkold(), and unconditionally written back
> with set_huge_pte_at(). A hardware update to the dirty bit occurring between
> the read and the write could be lost. If the dirty bit is overwritten with a
> stale clean copy, data could be reclaimed and lost instead of being written
> back.
I'm separatively working on this.
>
> > +
> > + if (mmu_notifier_clear_young(mm, addr,
> > + addr + huge_page_size(hstate_vma(vma))))
>
> [Severity: High]
> This is a pre-existing issue, but could passing an unaligned addr to
> mmu_notifier_clear_young() cause incorrect secondary MMU aging?
>
> When mmu_notifier_clear_young() receives a range that crosses page
> boundaries due to an unaligned start address, the secondary MMU (such as
> KVM) may age multiple adjacent host pages instead of just the target page.
> This would corrupt working set statistics for adjacent pages and lead to
> incorrect memory management, like reclaiming hot pages.
>
> This unaligned addr behavior appears to originate from damon_rand() and is
> also present in damon_ptep_mkold() and damon_pmdp_mkold().
I will separatively work on this.
FYI, I think Sashiko maybe reported this kind of issue before, but I ignored it
by misunderstanding the point. Now I understand it better and agree this is an
issue that better to be fixed.
>
> > + referenced = true;
> > +
> > + if (referenced)
> > + folio_set_young(folio);
> > +
> > + folio_set_idle(folio);
> > + folio_put(folio);
> > +}
> > +#endif /* CONFIG_HUGETLB_PAGE */
>
> --
> Sashiko AI review · https://sashiko.dev/#/patchset/20260902025700.17975-1-kiyer@crusoe.ai?part=1
Thanks,
SJ
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v2 2/3] mm/damon/ops-common: handle hugetlb folios in folio mkold/young rmap walkers
2026-09-02 2:56 [PATCH v2 0/3] mm/damon: support access monitoring of hugetlb-backed memory Krishna Iyer
2026-09-02 2:56 ` [PATCH v2 1/3] mm/damon: move damon_hugetlb_mkold() from vaddr to ops-common Krishna Iyer
@ 2026-09-02 2:56 ` Krishna Iyer
2026-09-02 3:12 ` sashiko-bot
2026-09-02 5:05 ` SJ Park
2026-09-02 2:57 ` [PATCH v2 3/3] mm/damon/paddr: support hugetlb folios in access monitoring Krishna Iyer
2026-09-02 5:29 ` [PATCH v2 0/3] mm/damon: support access monitoring of hugetlb-backed memory SJ Park
3 siblings, 2 replies; 13+ messages in thread
From: Krishna Iyer @ 2026-09-02 2:56 UTC (permalink / raw)
To: SJ Park; +Cc: Andrew Morton, damon, linux-mm, linux-kernel
damon_folio_mkold_one() and damon_folio_young_one() assume the folios
they walk are mapped by normal PTEs or THP PMDs. When the folio is a
hugetlb folio, page_vma_mapped_walk() returns the huge PTE in pvmw.pte
with its page table lock held, but the walkers treat it as a normal
PTE: they read and age it with PAGE_SIZE-granularity helpers, which is
wrong for huge PTEs (up to PUD level), and notify secondary MMUs for
only PAGE_SIZE of the mapping.
Add hugetlb branches to both walkers. The mkold walker reuses
damon_hugetlb_mkold(), which the virtual address space operations set
has been using for hugetlb aging: it clears the young bit of the huge
PTE via set_huge_pte_at() and calls mmu_notifier_clear_young() spanning
the whole huge page size. The young walker gets an equivalent new
helper, damon_hugetlb_young(), which reads the huge PTE with
huge_ptep_get() and consults the page idle flag and
mmu_notifier_test_young() like the existing PTE branch.
Locking mirrors what page_vma_mapped_walk() provides: the huge PTE's
page table lock is held inside the walk, and for shared hugetlb
mappings (the only ones subject to huge PMD sharing), rmap_walk_file()
already holds i_mmap_rwsem, satisfying hugetlb_walk()'s locking
requirements.
This is currently dead code: both rmap walkers are only reachable
through damon_get_folio(), which rejects hugetlb folios since they are
not on the LRU lists. A following commit will let the physical address
space monitoring primitives opt in to hugetlb folios.
Assisted-by: Claude:claude-fable-5
Signed-off-by: Krishna Iyer <kiyer@crusoe.ai>
---
mm/damon/ops-common.c | 61 +++++++++++++++++++++++++++++++++----------
1 file changed, 47 insertions(+), 14 deletions(-)
diff --git a/mm/damon/ops-common.c b/mm/damon/ops-common.c
index f5fe92b825bb..373b25db5950 100644
--- a/mm/damon/ops-common.c
+++ b/mm/damon/ops-common.c
@@ -193,10 +193,15 @@ static bool damon_folio_mkold_one(struct folio *folio,
while (page_vma_mapped_walk(&pvmw)) {
addr = pvmw.address;
- if (pvmw.pte)
- damon_ptep_mkold(pvmw.pte, vma, addr);
- else
+ if (pvmw.pte) {
+ if (folio_test_hugetlb(folio))
+ damon_hugetlb_mkold(pvmw.pte, vma->vm_mm, vma,
+ addr);
+ else
+ damon_ptep_mkold(pvmw.pte, vma, addr);
+ } else {
damon_pmdp_mkold(pvmw.pmd, vma, addr);
+ }
}
return true;
}
@@ -221,27 +226,55 @@ void damon_folio_mkold(struct folio *folio)
}
+#ifdef CONFIG_HUGETLB_PAGE
+static bool damon_hugetlb_young(pte_t *pte, struct vm_area_struct *vma,
+ unsigned long addr, struct folio *folio)
+{
+ pte_t entry = huge_ptep_get(vma->vm_mm, addr, pte);
+
+ return (pte_present(entry) && pte_young(entry)) ||
+ !folio_test_idle(folio) ||
+ mmu_notifier_test_young(vma->vm_mm, addr);
+}
+#else
+static bool damon_hugetlb_young(pte_t *pte, struct vm_area_struct *vma,
+ unsigned long addr, struct folio *folio)
+{
+ return false;
+}
+#endif /* CONFIG_HUGETLB_PAGE */
+
+static bool damon_pte_young(pte_t *pte, struct vm_area_struct *vma,
+ unsigned long addr, struct folio *folio)
+{
+ pte_t entry = ptep_get(pte);
+
+ /*
+ * PFN swap PTEs, such as device-exclusive ones, that actually map
+ * pages are "old" from a CPU perspective. The MMU notifier takes care
+ * of any device aspects.
+ */
+ return (pte_present(entry) && pte_young(entry)) ||
+ !folio_test_idle(folio) ||
+ mmu_notifier_test_young(vma->vm_mm, addr);
+}
+
static bool damon_folio_young_one(struct folio *folio,
struct vm_area_struct *vma, unsigned long addr, void *arg)
{
bool *accessed = arg;
DEFINE_FOLIO_VMA_WALK(pvmw, folio, vma, addr, 0);
- pte_t pte;
*accessed = false;
while (page_vma_mapped_walk(&pvmw)) {
addr = pvmw.address;
if (pvmw.pte) {
- pte = ptep_get(pvmw.pte);
-
- /*
- * PFN swap PTEs, such as device-exclusive ones, that
- * actually map pages are "old" from a CPU perspective.
- * The MMU notifier takes care of any device aspects.
- */
- *accessed = (pte_present(pte) && pte_young(pte)) ||
- !folio_test_idle(folio) ||
- mmu_notifier_test_young(vma->vm_mm, addr);
+ if (folio_test_hugetlb(folio))
+ *accessed = damon_hugetlb_young(pvmw.pte, vma,
+ addr, folio);
+ else
+ *accessed = damon_pte_young(pvmw.pte, vma,
+ addr, folio);
} else {
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
pmd_t pmd = pmdp_get(pvmw.pmd);
--
2.54.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH v2 2/3] mm/damon/ops-common: handle hugetlb folios in folio mkold/young rmap walkers
2026-09-02 2:56 ` [PATCH v2 2/3] mm/damon/ops-common: handle hugetlb folios in folio mkold/young rmap walkers Krishna Iyer
@ 2026-09-02 3:12 ` sashiko-bot
2026-09-02 4:53 ` SJ Park
2026-09-02 5:05 ` SJ Park
1 sibling, 1 reply; 13+ messages in thread
From: sashiko-bot @ 2026-09-02 3:12 UTC (permalink / raw)
To: Krishna Iyer; +Cc: damon
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Non-atomic clearing of huge PTE young bit silently overwrites hardware dirty bit
--
commit 04cc6d81cf1593c6fb092b306da28db5569a3f11
Author: Krishna Iyer <kiyer@crusoe.ai>
mm/damon/ops-common: handle hugetlb folios in folio mkold/young rmap walkers
This commit adds hugetlb branches to the damon_folio_mkold_one and
damon_folio_young_one rmap walkers. It reuses damon_hugetlb_mkold to properly
age huge PTEs and introduces a new helper damon_hugetlb_young for checking
their accessed state.
> diff --git a/mm/damon/ops-common.c b/mm/damon/ops-common.c
> index f5fe92b825bbc..373b25db59505 100644
> --- a/mm/damon/ops-common.c
> +++ b/mm/damon/ops-common.c
> @@ -193,10 +193,15 @@ static bool damon_folio_mkold_one(struct folio *folio,
>
> while (page_vma_mapped_walk(&pvmw)) {
> addr = pvmw.address;
> - if (pvmw.pte)
> - damon_ptep_mkold(pvmw.pte, vma, addr);
> - else
> + if (pvmw.pte) {
> + if (folio_test_hugetlb(folio))
> + damon_hugetlb_mkold(pvmw.pte, vma->vm_mm, vma,
> + addr);
[Severity: High]
This is a pre-existing issue, but does expanding the use of
damon_hugetlb_mkold here expose us to hardware dirty bit overwrites?
Looking at damon_hugetlb_mkold in mm/damon/ops-common.c, it performs a
non-atomic read-modify-write on the huge PTE:
pte_t entry = huge_ptep_get(mm, addr, pte);
struct folio *folio = pfn_folio(pte_pfn(entry));
unsigned long psize = huge_page_size(hstate_vma(vma));
folio_get(folio);
if (pte_young(entry)) {
referenced = true;
entry = pte_mkold(entry);
set_huge_pte_at(mm, addr, pte, entry, psize);
}
If the hardware sets the dirty bit after software reads the PTE via
huge_ptep_get, but before it writes it back via set_huge_pte_at, the kernel
will silently discard the newly dirtied state. This can lead to data
corruption as the page might be dropped assuming it is clean.
Since DAMON monitoring can be configured to monitor hugetlb VMAs, triggering
this path, can this non-atomic clearing be safely avoided or addressed here?
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260902025700.17975-1-kiyer@crusoe.ai?part=2
^ permalink raw reply [flat|nested] 13+ messages in thread* Re: [PATCH v2 2/3] mm/damon/ops-common: handle hugetlb folios in folio mkold/young rmap walkers
2026-09-02 3:12 ` sashiko-bot
@ 2026-09-02 4:53 ` SJ Park
0 siblings, 0 replies; 13+ messages in thread
From: SJ Park @ 2026-09-02 4:53 UTC (permalink / raw)
To: sashiko-bot; +Cc: SJ Park, Krishna Iyer, damon
On Wed, 02 Sep 2026 03:12:16 +0000 sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
>
> Pre-existing issues:
> - [High] Non-atomic clearing of huge PTE young bit silently overwrites hardware dirty bit
> --
>
> commit 04cc6d81cf1593c6fb092b306da28db5569a3f11
> Author: Krishna Iyer <kiyer@crusoe.ai>
>
> mm/damon/ops-common: handle hugetlb folios in folio mkold/young rmap walkers
>
> This commit adds hugetlb branches to the damon_folio_mkold_one and
> damon_folio_young_one rmap walkers. It reuses damon_hugetlb_mkold to properly
> age huge PTEs and introduces a new helper damon_hugetlb_young for checking
> their accessed state.
>
> > diff --git a/mm/damon/ops-common.c b/mm/damon/ops-common.c
> > index f5fe92b825bbc..373b25db59505 100644
> > --- a/mm/damon/ops-common.c
> > +++ b/mm/damon/ops-common.c
> > @@ -193,10 +193,15 @@ static bool damon_folio_mkold_one(struct folio *folio,
> >
> > while (page_vma_mapped_walk(&pvmw)) {
> > addr = pvmw.address;
> > - if (pvmw.pte)
> > - damon_ptep_mkold(pvmw.pte, vma, addr);
> > - else
> > + if (pvmw.pte) {
> > + if (folio_test_hugetlb(folio))
> > + damon_hugetlb_mkold(pvmw.pte, vma->vm_mm, vma,
> > + addr);
>
> [Severity: High]
> This is a pre-existing issue, but does expanding the use of
> damon_hugetlb_mkold here expose us to hardware dirty bit overwrites?
>
> Looking at damon_hugetlb_mkold in mm/damon/ops-common.c, it performs a
> non-atomic read-modify-write on the huge PTE:
>
> pte_t entry = huge_ptep_get(mm, addr, pte);
> struct folio *folio = pfn_folio(pte_pfn(entry));
> unsigned long psize = huge_page_size(hstate_vma(vma));
>
> folio_get(folio);
>
> if (pte_young(entry)) {
> referenced = true;
> entry = pte_mkold(entry);
> set_huge_pte_at(mm, addr, pte, entry, psize);
> }
>
> If the hardware sets the dirty bit after software reads the PTE via
> huge_ptep_get, but before it writes it back via set_huge_pte_at, the kernel
> will silently discard the newly dirtied state. This can lead to data
> corruption as the page might be dropped assuming it is clean.
>
> Since DAMON monitoring can be configured to monitor hugetlb VMAs, triggering
> this path, can this non-atomic clearing be safely avoided or addressed here?
Pre-existing issue that not a blocker of this patch.
I'm working on the issue.
>
> [ ... ]
>
> --
> Sashiko AI review · https://sashiko.dev/#/patchset/20260902025700.17975-1-kiyer@crusoe.ai?part=2
Thanks,
SJ
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v2 2/3] mm/damon/ops-common: handle hugetlb folios in folio mkold/young rmap walkers
2026-09-02 2:56 ` [PATCH v2 2/3] mm/damon/ops-common: handle hugetlb folios in folio mkold/young rmap walkers Krishna Iyer
2026-09-02 3:12 ` sashiko-bot
@ 2026-09-02 5:05 ` SJ Park
1 sibling, 0 replies; 13+ messages in thread
From: SJ Park @ 2026-09-02 5:05 UTC (permalink / raw)
To: Krishna Iyer; +Cc: SJ Park, Andrew Morton, damon, linux-mm, linux-kernel
On Tue, 1 Sep 2026 19:56:59 -0700 Krishna Iyer <kiyer@crusoe.ai> wrote:
> damon_folio_mkold_one() and damon_folio_young_one() assume the folios
> they walk are mapped by normal PTEs or THP PMDs. When the folio is a
> hugetlb folio, page_vma_mapped_walk() returns the huge PTE in pvmw.pte
> with its page table lock held, but the walkers treat it as a normal
> PTE: they read and age it with PAGE_SIZE-granularity helpers, which is
> wrong for huge PTEs (up to PUD level), and notify secondary MMUs for
> only PAGE_SIZE of the mapping.
>
> Add hugetlb branches to both walkers. The mkold walker reuses
> damon_hugetlb_mkold(), which the virtual address space operations set
> has been using for hugetlb aging: it clears the young bit of the huge
> PTE via set_huge_pte_at() and calls mmu_notifier_clear_young() spanning
> the whole huge page size. The young walker gets an equivalent new
> helper, damon_hugetlb_young(), which reads the huge PTE with
> huge_ptep_get() and consults the page idle flag and
> mmu_notifier_test_young() like the existing PTE branch.
>
> Locking mirrors what page_vma_mapped_walk() provides: the huge PTE's
> page table lock is held inside the walk, and for shared hugetlb
> mappings (the only ones subject to huge PMD sharing), rmap_walk_file()
> already holds i_mmap_rwsem, satisfying hugetlb_walk()'s locking
> requirements.
>
> This is currently dead code: both rmap walkers are only reachable
> through damon_get_folio(), which rejects hugetlb folios since they are
> not on the LRU lists. A following commit will let the physical address
> space monitoring primitives opt in to hugetlb folios.
>
> Assisted-by: Claude:claude-fable-5
> Signed-off-by: Krishna Iyer <kiyer@crusoe.ai>
Reviewed-by: SJ Park <sj@kernel.org>
Thanks,
SJ
[...]
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v2 3/3] mm/damon/paddr: support hugetlb folios in access monitoring
2026-09-02 2:56 [PATCH v2 0/3] mm/damon: support access monitoring of hugetlb-backed memory Krishna Iyer
2026-09-02 2:56 ` [PATCH v2 1/3] mm/damon: move damon_hugetlb_mkold() from vaddr to ops-common Krishna Iyer
2026-09-02 2:56 ` [PATCH v2 2/3] mm/damon/ops-common: handle hugetlb folios in folio mkold/young rmap walkers Krishna Iyer
@ 2026-09-02 2:57 ` Krishna Iyer
2026-09-02 3:15 ` sashiko-bot
2026-09-02 5:17 ` SJ Park
2026-09-02 5:29 ` [PATCH v2 0/3] mm/damon: support access monitoring of hugetlb-backed memory SJ Park
3 siblings, 2 replies; 13+ messages in thread
From: Krishna Iyer @ 2026-09-02 2:57 UTC (permalink / raw)
To: SJ Park; +Cc: Andrew Morton, damon, linux-mm, linux-kernel
DAMON's physical address space monitoring is blind to hugetlb-backed
memory. Every access check starts at damon_get_folio(), which rejects
folios that are not on the LRU lists. Hugetlb folios are managed
outside of the LRU by design, so every sampling attempt on
hugetlb-backed memory silently fails and the pages are reported as
never accessed.
This is a significant blind spot on virtualization hosts. Cloud
hypervisor hosts commonly back guest memory with 1 GiB hugetlbfs pages,
covering the vast majority of the machine's memory. On such hosts,
modules like DAMON_STAT observe only the host-side remainder (page
cache, daemons) and report all guest working sets as permanently
idle, defeating the purpose of host-level access monitoring. In
testing on a 1 TiB host, an hour of 4-thread random access over 842 GiB
inside a guest was statistically indistinguishable from an idle host,
while a 40x smaller host-side workload produced a quantitatively
correct response.
Add damon_get_monitor_folio(), which additionally accepts hugetlb
folios, and use it in the two paddr access monitoring primitives,
damon_pa_mkold() and damon_pa_young(). With the previous commit
teaching the folio-granular rmap walkers to age huge PTEs and to call
the mmu notifiers spanning the whole huge page, this makes guest
accesses visible through secondary MMU (e.g. KVM/EPT) young bits.
Free hugetlb pool folios have a zero refcount, so folio_try_get()
naturally keeps rejecting them.
The DAMOS action appliers (damon_pa_pageout(),
damon_pa_mark_accessed_or_deactivate(), damon_pa_migrate(),
damon_pa_stat()) keep using damon_get_folio(): reclaim, LRU
manipulation and migration cannot act on hugetlb folios, so their
behavior is unchanged.
Note that the access check granularity for hugetlb-backed memory is the
huge page size: one touched byte reports the whole (up to 1 GiB) page
as accessed. Also, DAMON now consumes secondary MMU young bits that
KVM's own aging uses; at DAMON's sampling rate (one page per region per
sampling interval) the interference is negligible.
Assisted-by: Claude:claude-fable-5
Signed-off-by: Krishna Iyer <kiyer@crusoe.ai>
---
mm/damon/ops-common.c | 31 +++++++++++++++++++++++++++----
mm/damon/ops-common.h | 1 +
mm/damon/paddr.c | 4 ++--
3 files changed, 30 insertions(+), 6 deletions(-)
diff --git a/mm/damon/ops-common.c b/mm/damon/ops-common.c
index 373b25db5950..80207b56c6fd 100644
--- a/mm/damon/ops-common.c
+++ b/mm/damon/ops-common.c
@@ -15,14 +15,20 @@
#include "../internal.h"
#include "ops-common.h"
+static bool damon_folio_acceptable(struct folio *folio, bool monitor)
+{
+ return folio_test_lru(folio) ||
+ (monitor && folio_test_hugetlb(folio));
+}
+
/*
- * Get an online page for a pfn if it's in the LRU list. Otherwise, returns
- * NULL.
+ * Get an online page for a pfn if it's in the LRU list, or a hugetlb folio if
+ * @monitor is set. Otherwise, returns NULL.
*
* The body of this function is stolen from the 'page_idle_get_folio()'. We
* steal rather than reuse it because the code is quite simple.
*/
-struct folio *damon_get_folio(unsigned long pfn)
+static struct folio *__damon_get_folio(unsigned long pfn, bool monitor)
{
struct page *page = pfn_to_online_page(pfn);
struct folio *folio;
@@ -33,13 +39,30 @@ struct folio *damon_get_folio(unsigned long pfn)
folio = page_folio(page);
if (!folio_try_get(folio))
return NULL;
- if (unlikely(page_folio(page) != folio) || !folio_test_lru(folio)) {
+ if (unlikely(page_folio(page) != folio) ||
+ !damon_folio_acceptable(folio, monitor)) {
folio_put(folio);
folio = NULL;
}
return folio;
}
+struct folio *damon_get_folio(unsigned long pfn)
+{
+ return __damon_get_folio(pfn, false);
+}
+
+/*
+ * Same to damon_get_folio(), but also accepts hugetlb folios, which are
+ * managed outside of the LRU lists. Aimed to be used by access monitoring
+ * primitives. DAMOS actions that assume LRU-managed folios should keep using
+ * damon_get_folio().
+ */
+struct folio *damon_get_monitor_folio(unsigned long pfn)
+{
+ return __damon_get_folio(pfn, true);
+}
+
void damon_ptep_mkold(pte_t *pte, struct vm_area_struct *vma, unsigned long addr)
{
pte_t pteval = ptep_get(pte);
diff --git a/mm/damon/ops-common.h b/mm/damon/ops-common.h
index f7811c9c7a02..172f0f17c4a8 100644
--- a/mm/damon/ops-common.h
+++ b/mm/damon/ops-common.h
@@ -6,6 +6,7 @@
#include <linux/damon.h>
struct folio *damon_get_folio(unsigned long pfn);
+struct folio *damon_get_monitor_folio(unsigned long pfn);
void damon_ptep_mkold(pte_t *pte, struct vm_area_struct *vma, unsigned long addr);
void damon_pmdp_mkold(pmd_t *pmd, struct vm_area_struct *vma, unsigned long addr);
diff --git a/mm/damon/paddr.c b/mm/damon/paddr.c
index 5c6c3a597fd0..ed7b7f31291a 100644
--- a/mm/damon/paddr.c
+++ b/mm/damon/paddr.c
@@ -37,7 +37,7 @@ static unsigned long damon_pa_core_addr(
static void damon_pa_mkold(phys_addr_t paddr)
{
- struct folio *folio = damon_get_folio(PHYS_PFN(paddr));
+ struct folio *folio = damon_get_monitor_folio(PHYS_PFN(paddr));
if (!folio)
return;
@@ -67,7 +67,7 @@ static void damon_pa_prepare_access_checks(struct damon_ctx *ctx)
static bool damon_pa_young(phys_addr_t paddr)
{
- struct folio *folio = damon_get_folio(PHYS_PFN(paddr));
+ struct folio *folio = damon_get_monitor_folio(PHYS_PFN(paddr));
bool accessed;
if (!folio)
--
2.54.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH v2 3/3] mm/damon/paddr: support hugetlb folios in access monitoring
2026-09-02 2:57 ` [PATCH v2 3/3] mm/damon/paddr: support hugetlb folios in access monitoring Krishna Iyer
@ 2026-09-02 3:15 ` sashiko-bot
2026-09-02 4:57 ` SJ Park
2026-09-02 5:17 ` SJ Park
1 sibling, 1 reply; 13+ messages in thread
From: sashiko-bot @ 2026-09-02 3:15 UTC (permalink / raw)
To: Krishna Iyer; +Cc: damon
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
Pre-existing issues:
- [Critical] Non-atomic PTE modification in `damon_hugetlb_mkold()` clobbers hardware-updated dirty bits.
- [High] DAMOS action appliers suffer from severe O(N) performance overhead on hugetlb pages due to PAGE_SIZE stepping.
--
commit c04c513b2839f46c6e864892a28fa35c945f1209
Author: Krishna Iyer <kiyer@crusoe.ai>
mm/damon/paddr: support hugetlb folios in access monitoring
This commit introduces damon_get_monitor_folio() to allow Damon's physical
address space monitoring to sample hugetlb-backed memory. It updates the
access monitoring primitives damon_pa_mkold() and damon_pa_young() to use
this new function. The Damos action appliers continue to use
damon_get_folio() to skip hugetlb folios.
> The DAMOS action appliers (damon_pa_pageout(),
> damon_pa_mark_accessed_or_deactivate(), damon_pa_migrate(),
> damon_pa_stat()) keep using damon_get_folio(): reclaim, LRU
> manipulation and migration cannot act on hugetlb folios, so their
> behavior is unchanged.
[Severity: High]
This is a pre-existing issue, but does this unchanged behavior cause a severe
performance regression when Damos action appliers encounter hugetlb pages?
When damon_pa_pageout() and damon_pa_stat() (with filters) run on regions
with hugetlb memory, damon_get_folio() rejects the hugetlb folios. This causes
the code to fall back to PAGE_SIZE stepping:
mm/damon/paddr.c:damon_pa_pageout() {
...
while (addr < damon_pa_phys_addr(r->ar.end, addr_unit)) {
folio = damon_get_folio(PHYS_PFN(addr));
if (damon_pa_invalid_damos_folio(folio, s)) {
addr += PAGE_SIZE;
continue;
}
...
}
For large folios like 1 GiB hugetlb pages, this results in hundreds of thousands
of redundant atomic refcount operations per page (262,144 times). Does this
PAGE_SIZE stepping need to be updated to advance by folio_size() to prevent
massive CPU consumption by Damon threads?
> diff --git a/mm/damon/paddr.c b/mm/damon/paddr.c
> index 5c6c3a597fd0b..ed7b7f31291a0 100644
> --- a/mm/damon/paddr.c
> +++ b/mm/damon/paddr.c
> @@ -37,7 +37,7 @@ static unsigned long damon_pa_core_addr(
>
> static void damon_pa_mkold(phys_addr_t paddr)
> {
> - struct folio *folio = damon_get_folio(PHYS_PFN(paddr));
> + struct folio *folio = damon_get_monitor_folio(PHYS_PFN(paddr));
>
> if (!folio)
> return;
[Severity: Critical]
This is a pre-existing issue, but can this resulting call chain clobber
hardware-updated dirty bits?
Now that damon_pa_mkold() supports hugetlb folios, it processes them via
damon_folio_mkold_one(), which reaches damon_hugetlb_mkold(). In this
function, the PTE is updated non-atomically:
mm/damon/ops-common.c:damon_hugetlb_mkold() {
...
if (pte_young(entry)) {
referenced = true;
entry = pte_mkold(entry);
set_huge_pte_at(mm, addr, pte, entry, psize);
}
...
}
Any hardware modifications to the PTE (like setting the dirty bit) between
huge_ptep_get() and set_huge_pte_at() are silently overwritten. Could this
result in data loss or memory corruption where modified memory is discarded
without writeback?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260902025700.17975-1-kiyer@crusoe.ai?part=3
^ permalink raw reply [flat|nested] 13+ messages in thread* Re: [PATCH v2 3/3] mm/damon/paddr: support hugetlb folios in access monitoring
2026-09-02 3:15 ` sashiko-bot
@ 2026-09-02 4:57 ` SJ Park
0 siblings, 0 replies; 13+ messages in thread
From: SJ Park @ 2026-09-02 4:57 UTC (permalink / raw)
To: sashiko-bot; +Cc: SJ Park, Krishna Iyer, damon
On Wed, 02 Sep 2026 03:15:18 +0000 sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
>
> Pre-existing issues:
> - [Critical] Non-atomic PTE modification in `damon_hugetlb_mkold()` clobbers hardware-updated dirty bits.
> - [High] DAMOS action appliers suffer from severe O(N) performance overhead on hugetlb pages due to PAGE_SIZE stepping.
> --
>
> commit c04c513b2839f46c6e864892a28fa35c945f1209
> Author: Krishna Iyer <kiyer@crusoe.ai>
>
> mm/damon/paddr: support hugetlb folios in access monitoring
>
> This commit introduces damon_get_monitor_folio() to allow Damon's physical
> address space monitoring to sample hugetlb-backed memory. It updates the
> access monitoring primitives damon_pa_mkold() and damon_pa_young() to use
> this new function. The Damos action appliers continue to use
> damon_get_folio() to skip hugetlb folios.
>
> > The DAMOS action appliers (damon_pa_pageout(),
> > damon_pa_mark_accessed_or_deactivate(), damon_pa_migrate(),
> > damon_pa_stat()) keep using damon_get_folio(): reclaim, LRU
> > manipulation and migration cannot act on hugetlb folios, so their
> > behavior is unchanged.
>
> [Severity: High]
> This is a pre-existing issue, but does this unchanged behavior cause a severe
> performance regression when Damos action appliers encounter hugetlb pages?
>
> When damon_pa_pageout() and damon_pa_stat() (with filters) run on regions
> with hugetlb memory, damon_get_folio() rejects the hugetlb folios. This causes
> the code to fall back to PAGE_SIZE stepping:
>
> mm/damon/paddr.c:damon_pa_pageout() {
> ...
> while (addr < damon_pa_phys_addr(r->ar.end, addr_unit)) {
> folio = damon_get_folio(PHYS_PFN(addr));
> if (damon_pa_invalid_damos_folio(folio, s)) {
> addr += PAGE_SIZE;
> continue;
> }
> ...
> }
>
> For large folios like 1 GiB hugetlb pages, this results in hundreds of thousands
> of redundant atomic refcount operations per page (262,144 times). Does this
> PAGE_SIZE stepping need to be updated to advance by folio_size() to prevent
> massive CPU consumption by Damon threads?
For such cases DAMOS quota feature exists. We could revisit this later, or
whenever a real user reports this as a problem.
>
> > diff --git a/mm/damon/paddr.c b/mm/damon/paddr.c
> > index 5c6c3a597fd0b..ed7b7f31291a0 100644
> > --- a/mm/damon/paddr.c
> > +++ b/mm/damon/paddr.c
> > @@ -37,7 +37,7 @@ static unsigned long damon_pa_core_addr(
> >
> > static void damon_pa_mkold(phys_addr_t paddr)
> > {
> > - struct folio *folio = damon_get_folio(PHYS_PFN(paddr));
> > + struct folio *folio = damon_get_monitor_folio(PHYS_PFN(paddr));
> >
> > if (!folio)
> > return;
>
> [Severity: Critical]
> This is a pre-existing issue, but can this resulting call chain clobber
> hardware-updated dirty bits?
>
> Now that damon_pa_mkold() supports hugetlb folios, it processes them via
> damon_folio_mkold_one(), which reaches damon_hugetlb_mkold(). In this
> function, the PTE is updated non-atomically:
>
> mm/damon/ops-common.c:damon_hugetlb_mkold() {
> ...
> if (pte_young(entry)) {
> referenced = true;
> entry = pte_mkold(entry);
> set_huge_pte_at(mm, addr, pte, entry, psize);
> }
> ...
> }
>
> Any hardware modifications to the PTE (like setting the dirty bit) between
> huge_ptep_get() and set_huge_pte_at() are silently overwritten. Could this
> result in data loss or memory corruption where modified memory is discarded
> without writeback?
I'm separatively working on it.
>
> --
> Sashiko AI review · https://sashiko.dev/#/patchset/20260902025700.17975-1-kiyer@crusoe.ai?part=3
Thanks,
SJ
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v2 3/3] mm/damon/paddr: support hugetlb folios in access monitoring
2026-09-02 2:57 ` [PATCH v2 3/3] mm/damon/paddr: support hugetlb folios in access monitoring Krishna Iyer
2026-09-02 3:15 ` sashiko-bot
@ 2026-09-02 5:17 ` SJ Park
1 sibling, 0 replies; 13+ messages in thread
From: SJ Park @ 2026-09-02 5:17 UTC (permalink / raw)
To: Krishna Iyer; +Cc: SJ Park, Andrew Morton, damon, linux-mm, linux-kernel
On Tue, 1 Sep 2026 19:57:00 -0700 Krishna Iyer <kiyer@crusoe.ai> wrote:
> DAMON's physical address space monitoring is blind to hugetlb-backed
> memory. Every access check starts at damon_get_folio(), which rejects
> folios that are not on the LRU lists. Hugetlb folios are managed
> outside of the LRU by design, so every sampling attempt on
> hugetlb-backed memory silently fails and the pages are reported as
> never accessed.
>
> This is a significant blind spot on virtualization hosts. Cloud
> hypervisor hosts commonly back guest memory with 1 GiB hugetlbfs pages,
> covering the vast majority of the machine's memory. On such hosts,
> modules like DAMON_STAT observe only the host-side remainder (page
> cache, daemons) and report all guest working sets as permanently
> idle, defeating the purpose of host-level access monitoring. In
> testing on a 1 TiB host, an hour of 4-thread random access over 842 GiB
> inside a guest was statistically indistinguishable from an idle host,
> while a 40x smaller host-side workload produced a quantitatively
> correct response.
>
> Add damon_get_monitor_folio(), which additionally accepts hugetlb
> folios, and use it in the two paddr access monitoring primitives,
> damon_pa_mkold() and damon_pa_young(). With the previous commit
> teaching the folio-granular rmap walkers to age huge PTEs and to call
> the mmu notifiers spanning the whole huge page, this makes guest
> accesses visible through secondary MMU (e.g. KVM/EPT) young bits.
>
> Free hugetlb pool folios have a zero refcount, so folio_try_get()
> naturally keeps rejecting them.
>
> The DAMOS action appliers (damon_pa_pageout(),
> damon_pa_mark_accessed_or_deactivate(), damon_pa_migrate(),
> damon_pa_stat()) keep using damon_get_folio(): reclaim, LRU
> manipulation and migration cannot act on hugetlb folios, so their
> behavior is unchanged.
>
> Note that the access check granularity for hugetlb-backed memory is the
> huge page size: one touched byte reports the whole (up to 1 GiB) page
> as accessed. Also, DAMON now consumes secondary MMU young bits that
> KVM's own aging uses; at DAMON's sampling rate (one page per region per
> sampling interval) the interference is negligible.
>
> Assisted-by: Claude:claude-fable-5
> Signed-off-by: Krishna Iyer <kiyer@crusoe.ai>
Assuming you agree to below suggestion,
Reviewed-by: SJ Park <sj@kernel.org>
> ---
> mm/damon/ops-common.c | 31 +++++++++++++++++++++++++++----
> mm/damon/ops-common.h | 1 +
> mm/damon/paddr.c | 4 ++--
> 3 files changed, 30 insertions(+), 6 deletions(-)
>
> diff --git a/mm/damon/ops-common.c b/mm/damon/ops-common.c
> index 373b25db5950..80207b56c6fd 100644
> --- a/mm/damon/ops-common.c
> +++ b/mm/damon/ops-common.c
> @@ -15,14 +15,20 @@
> #include "../internal.h"
> #include "ops-common.h"
>
> +static bool damon_folio_acceptable(struct folio *folio, bool monitor)
> +{
> + return folio_test_lru(folio) ||
> + (monitor && folio_test_hugetlb(folio));
> +}
> +
> /*
> - * Get an online page for a pfn if it's in the LRU list. Otherwise, returns
> - * NULL.
> + * Get an online page for a pfn if it's in the LRU list, or a hugetlb folio if
> + * @monitor is set. Otherwise, returns NULL.
> *
> * The body of this function is stolen from the 'page_idle_get_folio()'. We
> * steal rather than reuse it because the code is quite simple.
> */
> -struct folio *damon_get_folio(unsigned long pfn)
> +static struct folio *__damon_get_folio(unsigned long pfn, bool monitor)
> {
> struct page *page = pfn_to_online_page(pfn);
> struct folio *folio;
> @@ -33,13 +39,30 @@ struct folio *damon_get_folio(unsigned long pfn)
> folio = page_folio(page);
> if (!folio_try_get(folio))
> return NULL;
> - if (unlikely(page_folio(page) != folio) || !folio_test_lru(folio)) {
> + if (unlikely(page_folio(page) != folio) ||
> + !damon_folio_acceptable(folio, monitor)) {
> folio_put(folio);
> folio = NULL;
> }
> return folio;
> }
>
> +struct folio *damon_get_folio(unsigned long pfn)
> +{
> + return __damon_get_folio(pfn, false);
> +}
> +
> +/*
> + * Same to damon_get_folio(), but also accepts hugetlb folios, which are
> + * managed outside of the LRU lists. Aimed to be used by access monitoring
> + * primitives. DAMOS actions that assume LRU-managed folios should keep using
> + * damon_get_folio().
> + */
> +struct folio *damon_get_monitor_folio(unsigned long pfn)
> +{
> + return __damon_get_folio(pfn, true);
> +}
The comment looks bit verbose and feel more like commit message to me. I'd
suggest to drop the comment for now. Because it is a very trivial change, I
will do so when I pick this into my tree, and later repost with the change for
mm.git merge. Please let me know if it doesn't work for you.
> +
> void damon_ptep_mkold(pte_t *pte, struct vm_area_struct *vma, unsigned long addr)
> {
> pte_t pteval = ptep_get(pte);
> diff --git a/mm/damon/ops-common.h b/mm/damon/ops-common.h
> index f7811c9c7a02..172f0f17c4a8 100644
> --- a/mm/damon/ops-common.h
> +++ b/mm/damon/ops-common.h
> @@ -6,6 +6,7 @@
> #include <linux/damon.h>
>
> struct folio *damon_get_folio(unsigned long pfn);
> +struct folio *damon_get_monitor_folio(unsigned long pfn);
>
> void damon_ptep_mkold(pte_t *pte, struct vm_area_struct *vma, unsigned long addr);
> void damon_pmdp_mkold(pmd_t *pmd, struct vm_area_struct *vma, unsigned long addr);
> diff --git a/mm/damon/paddr.c b/mm/damon/paddr.c
> index 5c6c3a597fd0..ed7b7f31291a 100644
> --- a/mm/damon/paddr.c
> +++ b/mm/damon/paddr.c
> @@ -37,7 +37,7 @@ static unsigned long damon_pa_core_addr(
>
> static void damon_pa_mkold(phys_addr_t paddr)
> {
> - struct folio *folio = damon_get_folio(PHYS_PFN(paddr));
> + struct folio *folio = damon_get_monitor_folio(PHYS_PFN(paddr));
>
> if (!folio)
> return;
> @@ -67,7 +67,7 @@ static void damon_pa_prepare_access_checks(struct damon_ctx *ctx)
>
> static bool damon_pa_young(phys_addr_t paddr)
> {
> - struct folio *folio = damon_get_folio(PHYS_PFN(paddr));
> + struct folio *folio = damon_get_monitor_folio(PHYS_PFN(paddr));
> bool accessed;
>
> if (!folio)
> --
> 2.54.0
Thanks,
SJ
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v2 0/3] mm/damon: support access monitoring of hugetlb-backed memory
2026-09-02 2:56 [PATCH v2 0/3] mm/damon: support access monitoring of hugetlb-backed memory Krishna Iyer
` (2 preceding siblings ...)
2026-09-02 2:57 ` [PATCH v2 3/3] mm/damon/paddr: support hugetlb folios in access monitoring Krishna Iyer
@ 2026-09-02 5:29 ` SJ Park
3 siblings, 0 replies; 13+ messages in thread
From: SJ Park @ 2026-09-02 5:29 UTC (permalink / raw)
To: Krishna Iyer; +Cc: SJ Park, Andrew Morton, damon, linux-mm, linux-kernel
On Tue, 1 Sep 2026 19:56:57 -0700 Krishna Iyer <kiyer@crusoe.ai> wrote:
> On virtualization hosts, most system memory is often backed by
> hugetlbfs. On our production hosts, for example, ~95% of RAM is 1 GiB
> hugetlb pages backing guest memory. DAMON's physical address space
> monitoring is blind to such memory: every access check starts at
> damon_get_folio(), which rejects folios that are not on the LRU lists,
> and hugetlb folios are managed outside of the LRU by design. As a
> result, all hugetlb-backed memory is silently reported as never
> accessed. In testing on a 1 TiB host, an hour of 4-thread random
> access over 842 GiB inside a guest was statistically indistinguishable
> from an idle host.
>
> The first patch moves damon_hugetlb_mkold() from vaddr to ops-common
> as a preparation. The second patch teaches the folio mkold/young rmap
> walkers to handle hugetlb folios, aging the huge PTE and notifying
> secondary MMUs across the whole huge page size; the secondary MMU
> notification is what surfaces guest-side (e.g., KVM/EPT) accessed
> bits. The third patch adds damon_get_monitor_folio() and uses it from
> the paddr monitoring primitives only. DAMOS action appliers such as
> DAMON_RECLAIM and DAMON_LRU_SORT keep the LRU-only lookup and are
> behaviorally unchanged.
>
> This series is the first half of an earlier six-patch series [1],
> split out as SJ suggested [2]. The second half (the 'aging_flush'
> TLB-flush-assisted aging) is deferred: we will gather more
> quantitative data on the gap it addresses, including the workload-side
> impact of the flushes and the working set measurement details SJ asked
> about, and post it separately once the data is in hand, aligned with
> the ongoing monitoring preparation actions work.
>
> Per Documentation/process/generated-content.rst, this series was
> developed with the assistance of an AI coding assistant (Anthropic
> Claude, via Claude Code). The assistant helped draft the code and
> changelogs, and applied the v1 review feedback. All changes were
> reviewed by the human submitter, who takes full responsibility for the
> contribution.
>
> The series as posted here was regression-tested on its base commit
> with a full x86_64 kernel build (no W=1 warnings in mm/damon), the
> DAMON kunit suite (41/41 passing) and the DAMON selftests (15/15
> passing) on a kernel booted with virtme-ng.
Looks good to me, thank you for this series Krishna!
With the comment modification I commented to the patch 3, I applied this series
to damon/next [1] tree. Unless you raise other opinions or Andrew picks this
into mm.git with the comment modification, I will repost the version in my tree
as the next version of this series with the comment modification soon (up to ~1
week later). If you have a different opinion for the comment modification, it
seems I forgot doing that or you cannot wait for my action, please feel free to
let me know or post the next version on your own.
[1] https://origin.kernel.org/doc/html/latest/mm/damon/maintainer-profile.html#scm-trees
Thanks,
SJ
[...]
^ permalink raw reply [flat|nested] 13+ messages in thread