mm/damon/ops-common: prevent migration fallback to non-target nodes
Subsystems (2)
SJ Park
maintainerPATCH v3 1/1 mm/damon/ops-common: prevent migration fallback to non-target nodes
From: Jiahui Zhang <[email protected]>
DAMOS_MIGRATE_{HOT,COLD} passes a target NUMA node to migrate_pages(). But alloc_migration_target() only treats mtc->nid as a preferred node unless __GFP_THISNODE is set. Hence target allocation can fall back to another node, and migrate_pages() can report success without placing the folio on the requested target node.
Consider a two-node tiered system where node 0 is a fast tier and node 1 is a CPU-less slow tier such as CXL memory, and the user wants to promote hot regions from node 1 to node 0 with a command like:
sudo damo start --ops vaddr --target_pid ${workload_pid} \
--damos_action migrate_hot 0 \
--damos_access_rate 70% maxWithout the __GFP_THISNODE flag, when the memory allocator finds that node 0 is nearly full, it can fall back to node 1 without waking up kswapd. Then the pages allocated for migrate_pages() are still on node 1, and the regions that are expected to be promoted to node 0 are only moved to different physical pages on node 1.
Meanwhile, both the mm_migrate_pages tracepoint and DAMOS's own sz_applied statistics (reported via the damos_stat_after_apply_interval tracepoint) show the migrations as successful, which makes the failure practically invisible and hard to investigate.
Running a demotion-purpose DAMOS scheme alongside the promotion scheme does not fully avoid this either. If demotion cannot keep up with the promotion rate, allocation can still fall back to node 1 during promotion, and the same misleading statistics show up.
Make DAMON's migration target allocation strict by setting __GFP_THISNODE, so that a failed allocation on the target node is reported as a failure instead of silently landing on a different node. This is consistent with alloc_misplaced_dst_folio(), alloc_demote_folio(), and with do_move_pages_to_node(), which all use __GFP_THISNODE for migrations to an explicit destination node.
---
Changes from v2
- v2: https://lore.kernel.org/ <MN2PR02MB665479119535A1235D69F125B5F92@MN2PR02MB6654.namprd02.prod.outlook.com>
- Collect R-b: from SJ.
- Rebase to the latest mm-new.
Changes since v1:
- Use the mm/damon/ops-common subject prefix.
- Describe the observed misleading migration accounting and how strict
target-node allocation resolves it.
- Use the formal author name.
- No functional/code change from v1
- Link to v1: https://lore.kernel.org/damon/[email protected]
mm/damon/ops-common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
mm/damon/ops-common.c +1 -1
@@ -312,7 +312,7 @@ static unsigned int __damon_migrate_folio_list(* instead of migrated. */ .gfp_mask = (GFP_HIGHUSER_MOVABLE & ~__GFP_RECLAIM) | - __GFP_NOMEMALLOC | GFP_NOWAIT, + __GFP_NOMEMALLOC | GFP_NOWAIT | __GFP_THISNODE, .nid = target_nid, };
base-commit: 86e9ddaf85a3e872e81e1946ed410ef7f5341e16
Signature · 1 line
2.47.3
[email protected]
AI reviewThank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] alloc_migration_target() overrides non-blocking gfp flags for large folios, causing kdamond to stall in synchronous direct reclaim. --
--- Patch [1]: [PATCH v3] mm/damon/ops-common: prevent migration fallback to non-target nodes --- commit 18e8e5ad17abc759ddf7302875cde768864ba912
Author: Jiahui Zhang <[email protected]>
mm/damon/ops-common: prevent migration fallback to non-target nodes
This commit modifies DAMON's migration target allocation to use __GFP_THISNODE. This prevents the allocator from silently falling back to other nodes when the target node is full, which previously caused misleading migration statistics.
mm/damon/ops-common.c static unsigned int __damon_migrate_folio_list( · line 315
Quoted 10 lines
mm/damon/ops-common.c +1
-1@@ -312,7 +312,7 @@ static unsigned int __damon_migrate_folio_list(* instead of migrated. */ .gfp_mask = (GFP_HIGHUSER_MOVABLE & ~__GFP_RECLAIM) | - __GFP_NOMEMALLOC | GFP_NOWAIT, + __GFP_NOMEMALLOC | GFP_NOWAIT | __GFP_THISNODE,
[Severity: High]
This is a pre-existing issue, but does adding __GFP_THISNODE here cause kdamond to unexpectedly stall in synchronous direct reclaim when migrating large folios?
When migrate_pages() is called, it uses alloc_migration_target() in mm/migrate.c. For large folios, alloc_migration_target() unconditionally overrides the non-blocking GFP flags requested by DAMON:
mm/migrate.c:alloc_migration_target() {
...
if (folio_test_large(src)) {
/*
* clear __GFP_RECLAIM to make the migration callback
* consistent with regular THP allocations.
*/
gfp_mask &= ~__GFP_RECLAIM;
gfp_mask |= GFP_TRANSHUGE;
order = folio_order(src);
}
...
}Because GFP_TRANSHUGE includes __GFP_DIRECT_RECLAIM, the allocation can block. Previously, without __GFP_THISNODE, the allocator could fall back to another node. By enforcing __GFP_THISNODE on a full target node, this forced direct reclaim will trigger, stalling the kdamond thread and preventing kswapd wakeups.
Could alloc_migration_target() be updated to respect the caller's intent to not block?
Quoted 3 lines
mm/damon/ops-common.c +0
-0.nid = target_nid, };
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
SJ Park
maintainerQuoted 49 lines · On Tue, 21 Jul 2026 14:07:16 +0000 [email protected] wrote:
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High]alloc_migration_target()overrides non-blocking gfp flags for large folios, causing kdamond to stall in synchronous direct reclaim. ----- Patch [1]: [PATCH v3]
mm/damon/ops-common: prevent migration fallback to non-target nodes --- commit18e8e5ad17abc759ddf7302875cde768864ba912
Author: Jiahui Zhang <[email protected]>
mm/damon/ops-common: prevent migration fallback to non-target nodesThis commit modifies DAMON's migration target allocation to use
__GFP_THISNODE. This prevents the allocator from silently falling back to other nodes when the target node is full, which previously caused misleading migration statistics.mm/damon/ops-common.c static unsigned int __damon_migrate_folio_list( · line 315
Quoted 10 lines
mm/damon/ops-common.c +1
-1@@ -312,7 +312,7 @@ static unsigned int __damon_migrate_folio_list(* instead of migrated. */ .gfp_mask = (GFP_HIGHUSER_MOVABLE & ~__GFP_RECLAIM) | - __GFP_NOMEMALLOC | GFP_NOWAIT, + __GFP_NOMEMALLOC | GFP_NOWAIT | __GFP_THISNODE,[Severity: High]
This is a pre-existing issue, but does adding__GFP_THISNODEhere cause kdamond to unexpectedly stall in synchronous direct reclaim when migrating large folios?When
migrate_pages()is called, it usesalloc_migration_target()inmm/migrate.c. For large folios,alloc_migration_target()unconditionally overrides the non-blocking GFP flags requested by DAMON:mm/migrate.c:alloc_migration_target() { ... if (folio_test_large(src)) { /* * clear __GFP_RECLAIM to make the migration callback * consistent with regular THP allocations. */ gfp_mask &= ~__GFP_RECLAIM; gfp_mask |= GFP_TRANSHUGE; order = folio_order(src); } ... }
Quoted 7 lines
Because
GFP_TRANSHUGEincludes__GFP_DIRECT_RECLAIM, the allocation can block. Previously, without__GFP_THISNODE, the allocator could fall back to another node. By enforcing__GFP_THISNODEon a full target node, this forced direct reclaim will trigger, stalling the kdamond thread and preventing kswapd wakeups.Could
alloc_migration_target()be updated to respect the caller's intent to not block?
The new behavior looks fine for now. DAMON-based memory tiering is usually for proactive use, so no much memory pressure is expected. We migth need to revisit this later for making it more cleaner, though. Maybe it should be more similar to that of do_move_pages_to_node().
Thanks,
SJ
[...]