[RFC PATCH 4/5] mm/damon/paddr: skip free pageblocks in migration walk

From: Ravi Jonnalagadda

Date: Sat May 16 2026 - 17:06:03 EST


damon_pa_migrate() walks every PFN in a region linearly, calling
damon_get_folio() for each one. On sparse physical address spaces
(e.g., CXL-attached memory), a single DAMON region can span hundreds
of gigabytes where most memory is free and sitting in the buddy
allocator. Most page lookups are fruitless and dominate kdamond
tick time.

Check at pageblock boundaries (2MB on x86_64) whether the block is
entirely free. If the first page of a pageblock is a buddy page at
pageblock_order or higher, the entire block is free and can be
skipped. Similarly skip pageblocks where pfn_to_online_page() returns
NULL.

This reduces the iteration from O(region_sz / PAGE_SIZE) to
O(region_sz / pageblock_sz) + O(populated_pages).

buddy_order_unsafe() is used without zone->lock. A transient false
positive (block becomes non-free between the PageBuddy and order
checks) costs at most one tick of missed candidates on that block;
the next tick re-scans. No correctness consequence as DAMON walks
are best-effort.

Signed-off-by: Ravi Jonnalagadda <ravis.opensrc@xxxxxxxxx>
---
mm/damon/paddr.c | 25 ++++++++++++++++++++++---
1 file changed, 22 insertions(+), 3 deletions(-)

diff --git a/mm/damon/paddr.c b/mm/damon/paddr.c
index c4738cd5e221e..e844c990987b9 100644
--- a/mm/damon/paddr.c
+++ b/mm/damon/paddr.c
@@ -258,13 +258,32 @@ static unsigned long damon_pa_migrate(struct damon_region *r,
unsigned long addr_unit, struct damos *s,
unsigned long *sz_filter_passed)
{
- phys_addr_t addr, applied;
+ phys_addr_t addr, end, applied;
LIST_HEAD(folio_list);
struct folio *folio = NULL;
+ unsigned long pfn;

addr = damon_pa_phys_addr(r->ar.start, addr_unit);
- while (addr < damon_pa_phys_addr(r->ar.end, addr_unit)) {
- folio = damon_get_folio(PHYS_PFN(addr));
+ end = damon_pa_phys_addr(r->ar.end, addr_unit);
+ while (addr < end) {
+ pfn = PHYS_PFN(addr);
+
+ /* Skip pageblocks that are entirely free. */
+ if (IS_ALIGNED(pfn, pageblock_nr_pages)) {
+ struct page *page = pfn_to_online_page(pfn);
+
+ if (!page) {
+ addr += pageblock_nr_pages * PAGE_SIZE;
+ continue;
+ }
+ if (PageBuddy(page) &&
+ buddy_order_unsafe(page) >= pageblock_order) {
+ addr += pageblock_nr_pages * PAGE_SIZE;
+ continue;
+ }
+ }
+
+ folio = damon_get_folio(pfn);
if (damon_pa_invalid_damos_folio(folio, s)) {
addr += PAGE_SIZE;
continue;
--
2.43.0