Skip to contentSkip to conversation

mm/damon: Add node_sys_bp quota goal metric for PA-based migration control

RFC PATCH Superseded Ravi Jonnalagadda 3 participants 13 messages Jan 23 – Feb 12

Subsystems (3)

v1 of 2 Superseded Latest is v2 →

lore
Share & tools
lore
#0 lore raw

cover mm/damon: Add node_sys_bp quota goal metric for PA-based migration control

This series introduces a new DAMON quota goal metric, node_sys_bp, designed for controlling memory migration in heterogeneous memory systems (e.g., DRAM↔CXL tiering). These patches are provided as an initial RFC and have not been tested on actual hardware.

Background and Motivation
=========================

A previous patch series [1] by Bijan Tabatabai and myself added weighted interleave support for DAMON migrate_{hot,cold} actions. That series implemented the feature for vaddr (virtual address) schemes because the weight-based approach requires VMA offset information to determine target nodes:

target_node = (vma_offset % total_weight) → node_from_weights

For paddr (physical address) schemes, obtaining VMA offset requires costly rmap (reverse mapping) walks. As noted in that series:

"However, finding out how a folio is mapped inside of a VMA requires
a costly rmap walk when using a paddr scheme. As such, we have decided that this functionality makes more sense as a vaddr scheme."

This new series takes a different approach that enables PA-based migration WITHOUT requiring rmap walks, by using basis points (bp) target-state goals instead of weight-based action rates.

The rmap Cost vs Oscillation Trade-off
======================================

For PA-based migration with weights, there are two possibilities:

1. Weight-based with rmap:
- Use rmap to find VMA offset for each physical page
- Apply weights based on VMA offset (same algorithm as VA)
- Works correctly: VMA offset provides stable identity
- Problem: rmap walks are expensive for every migration candidate

2. Weight-based without rmap:
- Attempt to apply weights using only physical address information
- No stable identity across migrations
- Results in page oscillation (see below)
- Not viable

The Oscillation Problem (Weights Without rmap)
==============================================

Weight-based migration relies on a stable identifier to determine which node a page "belongs to". For VA, this is the VMA offset - it remains constant regardless of which physical node backs the page. For PA without rmap, no such stable identifier exists.

Consider a two-node system with weights {Node 0: 40, Node 1: 60}:

Initial state:
Hot pages on Node 0: PFN 0x1000, 0x1001, 0x1002, 0x1003, 0x1004 Node 1: empty

Iteration 1 - Weight-based selection (no rmap):

    System tries to achieve 40/60 distribution
    Selects pages at PFN 0x1002, 0x1003, 0x1004 (60%) for Node 1
    After migration, these pages get NEW PFNs on Node 1:
      PFN 0x1002 → PFN 0x5000 (Node 1)
      PFN 0x1003 → PFN 0x5001 (Node 1)
      PFN 0x1004 → PFN 0x5002 (Node 1)

State after Iteration 1:

    Node 0: PFN 0x1000, 0x1001 (40%)
    Node 1: PFN 0x5000, 0x5001, 0x5002 (60%)

Iteration 2 - Weight-based selection runs again:
System sees pages at PFN 0x5000, 0x5001, 0x5002 on Node 1 These are "new" pages from the system's perspective NO MEMORY that these were just migrated FROM Node 0 Weight-based logic may select some for migration back to Node 0

Iteration 3, 4, 5...:
Same pages continue bouncing between nodes Each migration changes the PFN, erasing any "history" System never converges to stable state

The fundamental issue: weights define an ACTION RATE ("migrate X% of candidate pages to each node") rather than a TARGET STATE. Without stable page identity (which rmap provides via VMA offset), the system cannot determine which pages have already been "handled" and continues to reprocess the same logical pages indefinitely.

With rmap, the VMA offset provides stable identity - a page at file offset 0x1000 always hashes to the same target node regardless of its current PFN. Without rmap, we have no such anchor, and weights become meaningless.

Solution: bp-Based Target State Goals
=====================================

Instead of specifying action rates, node_sys_bp specifies a TARGET STATE:

"Node N should contain X basis points (X/10000) of system memory"

The migration control loop:

1. Calculate current_bp: sum bytes per node across monitored regions
2. Compare: if current_bp >= target_bp, STOP (goal satisfied)
3. Otherwise: continue migrating toward target

Example with target: "Node 0 should have 4000 bp (40%)"

    Iteration 1:
      current_bp = 10000 (100% on Node 0)
      target_bp  = 4000 (40%)
      current > target → migrate cold pages away from Node 0

    After Iteration 1:
      current_bp = 4000 (40% on Node 0)

    Iteration 2:
      current_bp = 4000
      target_bp  = 4000
      current >= target → STOP, goal satisfied

    No oscillation - migration stops when target state is reached.

No page identity tracking needed because we measure the END STATE, not which specific pages were moved. The early-exit prevents oscillation by stopping when the goal is met.

Why get_goal_metric() Ops Callback
==================================

The bp calculation requires iterating over monitored PA regions:

    for (pfn = start_pfn; pfn < end_pfn; pfn++) {
        if (page_to_nid(pfn_to_page(pfn)) == nid)
            node_bytes += PAGE_SIZE;
    }
    bp = node_bytes * 10000 / system_total;

This requires address-space knowledge that only the ops provider has. Existing goal metrics (PSI, node_mem_*, node_memcg_*) are computed in core using system-wide data that doesn't require iterating monitored regions.

The new get_goal_metric() callback in damon_operations allows:

1. Core to remain generic - handles all common metrics
2. Ops providers to implement metrics requiring region iteration
3. Clean separation - PA implements node_sys_bp, VA could add different metrics in future
4. Optional implementation - ops return 0 if metric unsupported

This design ensures node_sys_bp is only computed when using PA contexts where it makes sense, while keeping the core quota goal infrastructure unchanged for other metrics and ops providers.

The callback pattern allows each ops provider to implement metrics specific to its address space model without burdening the core with ops-specific knowledge.

Advantages of PA-Based Migration
================================

PA-based migration with DAMON enables integration of multiple hotness sources for migration decisions:

1. DAMON's native access pattern monitoring
2. Fault-based information (similar to NUMA Balancing)
3. Future: Hardware monitoring units (e.g., CXL CHMU)
4. Future: Instruction-Based Sampling (AMD IBS, Intel PEBS)

Unlike VA-based approaches tied to individual process address spaces, PA monitoring can aggregate hotness information from multiple sources to make system-wide migration decisions across the entire physical memory space.

Complementary to Existing vaddr Migration
=========================================

This series complements rather than replaces the vaddr weighted interleave migration merged in 6.18:

vaddr migration (weight-based):
- Per-process control
- Fine-grained interleave patterns via VMA offset
- Deterministic placement based on weights

paddr migration (bp-based, this series):
- System-wide control
- Target-state goals for node capacity management
- No rmap overhead
- Aggregates multiple hotness sources

Capacity Clamping
=================

The series also implements capacity clamping for node_sys_bp goals. In a system where Node 0 has 40% of total RAM, setting a target of 50% is impossible. The implementation clamps:

effective_target = min(user_target, node_capacity_bp)

This prevents the quota auto-tuning from chasing impossible targets and avoids thrashing in two-context DRAM↔CXL setups.

Patches
=======

1/5: mm/damon/core: add DAMOS_QUOTA_NODE_SYS_BP metric
Adds the enum value and documentation.

2/5: mm/damon: add get_goal_metric() op and PA provider
Introduces the ops callback and PA implementation that iterates monitored regions to calculate node_sys_bp without rmap.

3/5: mm/damon/core: add new ops-specific goal metric
Wires the new metric into core's quota goal evaluation, delegating to ops.get_goal_metric() for DAMOS_QUOTA_NODE_SYS_BP.

4/5: mm/damon/paddr: capacity clamp and directional early-exit
Adds capacity clamping and early-exit logic to prevent migration when goal is already satisfied.

5/5: mm/damon/sysfs-schemes: accept "node_sys_bp" in goal's target_metric
Exposes the new metric to userspace via sysfs.

Status
======

This is an early RFC for design review. The patches:
- Compile successfully with no errors or warnings
- Have NOT been tested on actual hardware

Feedback on the overall approach and design is appreciated.

References
==========

[1] mm/damon/vaddr: Allow interleaving in migrate_{hot,cold} actions (v4)
https://lore.kernel.org/linux-mm/[email protected]/
Merged in Linux 6.18

Ravi Jonnalagadda (5):
mm/damon/core: add DAMOS_QUOTA_NODE_SYS_BP metric mm/damon: add get_goal_metric() op and PA provider mm/damon/core: add new ops-specific goal metric mm/damon/paddr: capacity clamp and directional early-exit for
node_sys_bp
mm/damon/sysfs-schemes: accept "node_sys_bp" in goal's target_metric

 include/linux/damon.h    |   5 ++
 mm/damon/core.c          |  34 ++++++++++---
 mm/damon/paddr.c         | 102 +++++++++++++++++++++++++++++++++++++++
 mm/damon/sysfs-schemes.c |   7 +++
 4 files changed, 141 insertions(+), 7 deletions(-)
Signature · 1 line
2.43.0
#1 lore raw

RFC PATCH 1/5 mm/damon/core: add DAMOS_QUOTA_NODE_SYS_BP metric

Add a node-scoped "system bp" goal metric for PA-based migration control.

This metric represents basis points (bp, 1/10,000) for (scheme-eligible bytes on node nid) / (system total bytes)

---
 include/linux/damon.h | 2 ++
 1 file changed, 2 insertions(+)
include/linux/damon.h +2 -0
index 3813373a9200..ec5ed1a217fc 100644
@@ -155,6 +155,7 @@ enum damos_action {
  * @DAMOS_QUOTA_NODE_MEM_FREE_BP:	MemFree ratio of a node.
  * @DAMOS_QUOTA_NODE_MEMCG_USED_BP:	MemUsed ratio of a node for a cgroup.
  * @DAMOS_QUOTA_NODE_MEMCG_FREE_BP:	MemFree ratio of a node for a cgroup.
+ * @DAMOS_QUOTA_NODE_SYS_BP:		Scheme-eligible bytes ratio of a node.
  * @NR_DAMOS_QUOTA_GOAL_METRICS:	Number of DAMOS quota goal metrics.
  *
  * Metrics equal to larger than @NR_DAMOS_QUOTA_GOAL_METRICS are unsupported.
@@ -166,6 +167,7 @@ enum damos_quota_goal_metric {
 	DAMOS_QUOTA_NODE_MEM_FREE_BP,
 	DAMOS_QUOTA_NODE_MEMCG_USED_BP,
 	DAMOS_QUOTA_NODE_MEMCG_FREE_BP,
+	DAMOS_QUOTA_NODE_SYS_BP,
 	NR_DAMOS_QUOTA_GOAL_METRICS,
 };
 
Signature · 1 line
2.43.0
#2 lore raw

RFC PATCH 2/5 mm/damon: add get_goal_metric() op and PA provider

In reply to Ravi Jonnalagadda

Introduce an optional damon_operations callback get_goal_metric() that lets ops providers compute goal metrics requiring address-space knowledge.

Provide a PA implementation that handles DAMOS_QUOTA_NODE_SYS_BP by iterating the monitored PFN regions and attributing bytes to the goal's nid.

Core remains generic and asks ops only when needed.

---
 include/linux/damon.h |  3 +++
 mm/damon/paddr.c      | 58 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 61 insertions(+)
include/linux/damon.h +3 -0
index ec5ed1a217fc..67233898c27c 100644
@@ -649,6 +649,9 @@ struct damon_operations {
 	bool (*target_valid)(struct damon_target *t);
 	void (*cleanup_target)(struct damon_target *t);
 	void (*cleanup)(struct damon_ctx *context);
+	unsigned long (*get_goal_metric)(struct damon_ctx *ctx,
+				struct damos *scheme,
+				const struct damos_quota_goal *goal);
 };
 
 /*
mm/damon/paddr.c +58 -0

82 lines not on this page · raw message

Signature · 1 line
2.43.0
#3 lore raw

RFC PATCH 3/5 mm/damon/core: add new ops-specific goal metric

In reply to Ravi Jonnalagadda

Convert static functions in core.c to pass damon_ctx* and damos* down to damos_set_quota_goal_current_value(). This keeps all goal metrics (PSI, node_mem*, node_memcg*, and the new node_sys_bp) computed in one place, while core remains generic and ops-specific computation is delegated via ctx->ops.get_goal_metric().

Only static functions in this file are touched; no external ABI changes.

---
 mm/damon/core.c | 34 +++++++++++++++++++++++++++-------
 1 file changed, 27 insertions(+), 7 deletions(-)
mm/damon/core.c +27 -7
index 84f80a20f233..d898bfcbef72 100644
@@ -2119,8 +2119,20 @@ static unsigned long damos_get_node_memcg_used_bp(
 }
 #endif
 
+static unsigned long
+damos_get_goal_metric_from_ops(struct damon_ctx *ctx, struct damos *scheme,
+				       const struct damos_quota_goal *goal)
+{
+	if (!ctx || !goal)
+		return 0;
+	if (!ctx->ops.get_goal_metric)
+		return 0;
+	return ctx->ops.get_goal_metric(ctx, scheme, goal);
+}
 
-static void damos_set_quota_goal_current_value(struct damos_quota_goal *goal)
+static void damos_set_quota_goal_current_value(struct damon_ctx *ctx,
+				       struct damos *scheme,
+				       struct damos_quota_goal *goal)
 {
 	u64 now_psi_total;
 
@@ -2141,19 +2153,25 @@ static void damos_set_quota_goal_current_value(struct damos_quota_goal *goal)
 	case DAMOS_QUOTA_NODE_MEMCG_FREE_BP:
 		goal->current_value = damos_get_node_memcg_used_bp(goal);
 		break;
+	case DAMOS_QUOTA_NODE_SYS_BP:
+		goal->current_value = damos_get_goal_metric_from_ops(ctx,
+							scheme, goal);
+		break;
 	default:
 		break;
 	}
 }
 
 /* Return the highest score since it makes schemes least aggressive */
-static unsigned long damos_quota_score(struct damos_quota *quota)
+static unsigned long damos_quota_score(struct damon_ctx *ctx,
+				       struct damos *scheme,
+				       struct damos_quota *quota)
 {
 	struct damos_quota_goal *goal;
 	unsigned long highest_score = 0;
 
 	damos_for_each_quota_goal(goal, quota) {
-		damos_set_quota_goal_current_value(goal);
+		damos_set_quota_goal_current_value(ctx, scheme, goal);
 		highest_score = max(highest_score,
 				goal->current_value * 10000 /
 				goal->target_value);
@@ -2165,7 +2183,9 @@ static unsigned long damos_quota_score(struct damos_quota *quota)
 /*
  * Called only if quota->ms, or quota->sz are set, or quota->goals is not empty
  */
-static void damos_set_effective_quota(struct damos_quota *quota)
+static void damos_set_effective_quota(struct damon_ctx *ctx,
+				      struct damos *scheme,
+				      struct damos_quota *quota)
 {
 	unsigned long throughput;
 	unsigned long esz = ULONG_MAX;
@@ -2176,7 +2196,7 @@ static void damos_set_effective_quota(struct damos_quota *quota)
 	}
 
 	if (!list_empty(&quota->goals)) {
-		unsigned long score = damos_quota_score(quota);
+		unsigned long score = damos_quota_score(ctx, scheme, quota);
 
 		quota->esz_bp = damon_feed_loop_next_input(
 				max(quota->esz_bp, 10000UL),
@@ -2227,7 +2247,7 @@ static void damos_adjust_quota(struct damon_ctx *c, struct damos *s)
 	/* First charge window */
 	if (!quota->total_charged_sz && !quota->charged_from) {
 		quota->charged_from = jiffies;
-		damos_set_effective_quota(quota);
+		damos_set_effective_quota(c, s, quota);
 	}
 
 	/* New charge window starts */
@@ -2240,7 +2260,7 @@ static void damos_adjust_quota(struct damon_ctx *c, struct damos *s)
 		quota->charged_sz = 0;
 		if (trace_damos_esz_enabled())
 			cached_esz = quota->esz;
-		damos_set_effective_quota(quota);
+		damos_set_effective_quota(c, s, quota);
 		if (trace_damos_esz_enabled() && quota->esz != cached_esz)
 			damos_trace_esz(c, s, quota);
 	}
Signature · 1 line
2.43.0
#4 lore raw

RFC PATCH 4/5 mm/damon/paddr: capacity clamp and directional early-exit for node_sys_bp

In reply to Ravi Jonnalagadda

Clamp effective target to node capacity (bp) and skip in-migration if the node already meets/exceeds it. This avoids oscillation and unnecessary work in two-context DRAM/CXL setups when quota goals (e.g., node_sys_bp) are met.

---
 mm/damon/paddr.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 44 insertions(+)
mm/damon/paddr.c +44 -0
index 30e4e5663dcb..64dbdd2196a5 100644
@@ -300,10 +300,54 @@ static unsigned long damon_pa_deactivate_pages(struct damon_region *r,
 			sz_filter_passed);
 }
 
+static unsigned long damon_pa_node_capacity_bp(int nid)
+{
+	struct pglist_data *pgdat;
+	unsigned long sys_total = damon_pa_totalram_bytes();
+	unsigned long node_pages, node_total;
+
+	if (nid < 0 || !sys_total)
+		return 0;
+	pgdat = NODE_DATA(nid);
+	if (!pgdat)
+		return 0;
+	node_pages = pgdat->node_spanned_pages;
+	node_total = node_pages << PAGE_SHIFT;
+	return div64_u64((u64)node_total * 10000ULL, sys_total);
+}
+
 static unsigned long damon_pa_migrate(struct damon_region *r,
 		unsigned long addr_unit, struct damos *s,
 		unsigned long *sz_filter_passed)
 {
+	/*
+	 * Capacity clamp + directional early-exit for node_sys_bp goals:
+	 * If we are migrating INTO g->nid and the current bp for that node is
+	 * already >= min(target_bp, capacity_bp), skip work this interval.
+	 */
+	{
+		struct damos_quota_goal *g;
+
+		list_for_each_entry(g, &s->quota.goals, list) {
+			unsigned long cap_bp, effective_target_bp;
+
+			if (g->metric != DAMOS_QUOTA_NODE_SYS_BP)
+				continue;
+			if (g->nid < 0)
+				continue;
+
+			cap_bp = damon_pa_node_capacity_bp(g->nid);
+			if (!cap_bp)
+				break;
+
+			effective_target_bp = min(g->target_value, cap_bp);
+			if (s->target_nid == g->nid &&
+			    g->current_value >= effective_target_bp)
+				return 0;
+			break;
+		}
+	}
+
 	phys_addr_t addr, applied;
 	LIST_HEAD(folio_list);
 	struct folio *folio;
Signature · 1 line
2.43.0
#5 lore raw

RFC PATCH 5/5 mm/damon/sysfs-schemes: accept "node_sys_bp" in goal's target_metric

In reply to Ravi Jonnalagadda

Allow userspace to select the new goal metric "node_sys_bp" by writing it to goals/<G>/target_metric. Also set goal->nid for this metric when committing goals from sysfs to the running schemes.

---
 mm/damon/sysfs-schemes.c | 7 +++++++
 1 file changed, 7 insertions(+)
mm/damon/sysfs-schemes.c +7 -0
index 3a699dcd5a7f..37cd5d715821 100644
@@ -1038,6 +1038,10 @@ struct damos_sysfs_qgoal_metric_name damos_sysfs_qgoal_metric_names[] = {
 		.metric = DAMOS_QUOTA_NODE_MEMCG_FREE_BP,
 		.name = "node_memcg_free_bp",
 	},
+	{
+		.metric = DAMOS_QUOTA_NODE_SYS_BP,
+		.name = "node_sys_bp",
+	},
 };
 
 static ssize_t target_metric_show(struct kobject *kobj,
@@ -2566,6 +2570,9 @@ static int damos_sysfs_add_quota_score(
 			}
 			goal->nid = sysfs_goal->nid;
 			break;
+		case DAMOS_QUOTA_NODE_SYS_BP:
+			goal->nid = sysfs_goal->nid;
+			break;
 		default:
 			break;
 		}
Signature · 1 line
2.43.0
maintainer
#6 lore raw
In reply to Ravi Jonnalagadda

Cc-ing SK hynix folks (Honggyu and Yunjeong) for quota auto-tuning behavior confusion (not stop immediately after satisfying the goal) I discuss below.

Hello Ravi,

Thank you for sharing this great RFC!

I had a chance to be involved in a high level design of this series, off the mailing list. I'm disclosing the fact for others' context, since a few of my comments below are based on the previous off-list discussion.

Quoted 1 line · On Thu, 22 Jan 2026 20:57:23 -0800 Ravi Jonnalagadda <[email protected]>…

This series introduces a new DAMON quota goal metric,

As I discussed off the list, I believe the new goal can be useful. Thank you again for making this!

Quoted 4 lines

node_sys_bp, designed
for controlling memory migration in heterogeneous memory systems (e.g., DRAM↔CXL tiering). These patches are provided as an initial RFC and have not been tested on actual hardware.

I feel like the name is not very self explaining what it is, though. I'm also bit confused about what it does is what we discussed off list. I'll add more details below, on more detailed explanation about the new goal.

Quoted 10 lines

Background and Motivation
=========================

A previous patch series [1] by Bijan Tabatabai and myself added weighted interleave support for DAMON migrate_{hot,cold} actions. That series implemented the feature for vaddr (virtual address) schemes because the weight-based approach requires VMA offset information to determine target nodes:

Quoted 8 lines

target_node = (vma_offset % total_weight) → node_from_weights

For paddr (physical address) schemes, obtaining VMA offset requires costly rmap (reverse mapping) walks. As noted in that series:

"However, finding out how a folio is mapped inside of a VMA requires
a costly rmap walk when using a paddr scheme. As such, we have decided that this functionality makes more sense as a vaddr scheme."

Indeed the rmap overhead was one main concern. I'd like to also note that, however, another major concern from me was that the behavior sounds not easy to intuitively understand.

Quoted 69 lines

This new series takes a different approach that enables PA-based migration WITHOUT requiring rmap walks, by using basis points (bp) target-state goals instead of weight-based action rates.

The rmap Cost vs Oscillation Trade-off
======================================

For PA-based migration with weights, there are two possibilities:

1. Weight-based with rmap:
- Use rmap to find VMA offset for each physical page
- Apply weights based on VMA offset (same algorithm as VA)
- Works correctly: VMA offset provides stable identity
- Problem: rmap walks are expensive for every migration candidate

2. Weight-based without rmap:
- Attempt to apply weights using only physical address information
- No stable identity across migrations
- Results in page oscillation (see below)
- Not viable

The Oscillation Problem (Weights Without rmap)
==============================================

Weight-based migration relies on a stable identifier to determine which node a page "belongs to". For VA, this is the VMA offset - it remains constant regardless of which physical node backs the page. For PA without rmap, no such stable identifier exists.

Consider a two-node system with weights {Node 0: 40, Node 1: 60}:

Initial state:
Hot pages on Node 0: PFN 0x1000, 0x1001, 0x1002, 0x1003, 0x1004 Node 1: empty

Iteration 1 - Weight-based selection (no rmap):

    System tries to achieve 40/60 distribution
    Selects pages at PFN 0x1002, 0x1003, 0x1004 (60%) for Node 1
    After migration, these pages get NEW PFNs on Node 1:
      PFN 0x1002 → PFN 0x5000 (Node 1)
      PFN 0x1003 → PFN 0x5001 (Node 1)
      PFN 0x1004 → PFN 0x5002 (Node 1)

State after Iteration 1:

    Node 0: PFN 0x1000, 0x1001 (40%)
    Node 1: PFN 0x5000, 0x5001, 0x5002 (60%)

Iteration 2 - Weight-based selection runs again:
System sees pages at PFN 0x5000, 0x5001, 0x5002 on Node 1 These are "new" pages from the system's perspective NO MEMORY that these were just migrated FROM Node 0 Weight-based logic may select some for migration back to Node 0

Iteration 3, 4, 5...:
Same pages continue bouncing between nodes Each migration changes the PFN, erasing any "history" System never converges to stable state

The fundamental issue: weights define an ACTION RATE ("migrate X% of candidate pages to each node") rather than a TARGET STATE. Without stable page identity (which rmap provides via VMA offset), the system cannot determine which pages have already been "handled" and continues to reprocess the same logical pages indefinitely.

With rmap, the VMA offset provides stable identity - a page at file offset 0x1000 always hashes to the same target node regardless of its current PFN. Without rmap, we have no such anchor, and weights become meaningless.

Quoted 6 lines

Solution: bp-Based Target State Goals
=====================================

Instead of specifying action rates, node_sys_bp specifies a TARGET STATE:

"Node N should contain X basis points (X/10000) of system memory"

I'm not sure if this is exactly what we discussed off list. What I expected the goal would be based on our discussion is, what the first patch is saying. To quote the part,

@@ -155,6 +155,7 @@ enum damos_action {
* @DAMOS_QUOTA_NODE_MEM_FREE_BP: MemFree ratio of a node.
* @DAMOS_QUOTA_NODE_MEMCG_USED_BP: MemUsed ratio of a node for a cgroup.
* @DAMOS_QUOTA_NODE_MEMCG_FREE_BP: MemFree ratio of a node for a cgroup.
+ * @DAMOS_QUOTA_NODE_SYS_BP: Scheme-eligible bytes ratio of a node.
* @NR_DAMOS_QUOTA_GOAL_METRICS: Number of DAMOS quota goal metrics.

That is, my understanding of what we want to achive with the new goal is, letting users to ask DAMON "migrate hot pages of node A to node B, aiming X % of node B becomes hot, as a result of the migrations".

But your above description is not saying about the "scheme-eligibility". Also, the goal metric is the ratio of the memory to the entire system memory, not just a given node. My quick read of damon_pa_get_node_sys_bp() on the second patch of this series confirms the implementation is following your description, not what I imagined.

Have you found something we missed during the off list discussion, and therefore changed the definition of the goal to the current one? If so, could you share the details? Or, you might also wanted to implement what I expected, but something bothered your code and documentation?

Quoted 8 lines

The migration control loop:

1. Calculate current_bp: sum bytes per node across monitored regions
2. Compare: if current_bp >= target_bp, STOP (goal satisfied)
3. Otherwise: continue migrating toward target

Example with target: "Node 0 should have 4000 bp (40%)"

Regardless of why my expectation was different from this one, the goal I was expecting might still be used for your descripted case. That is, the scheme's target access pattern could be "any access pattern", and the target metric value could be adjusted based on the ratio between the node 0 memory and the total memory. For example, let's suppose there is 100 GiB memory in total, and 80 GiB memory in node 0 (80% of total memory in node 0). Then 40 % of total memory, which is the target of the above example, is 40 GiB, which is 50 % of node 0. So, you could use the goal I expected, with 50 % as the target value. Imaginable usage of that would look like,

    # --damos_quota_goal receives <metric name>, <target metric value>, and
    # <node id of the goal>
    damo tune --damos_action migrate_hot --damos_quota_goal new_goal_metric 50% 0

I sure you know that you could s/migrate_hot/migrate_cold/ if you prefer migrating cold pages first.

So, the metric of my expected definition can still be useful for your new use case. I'd prefer my expected definition if there is no concern, since it seems useful for more cases.

Quoted 3 lines
    Iteration 1:
      current_bp = 10000 (100% on Node 0)

How could 100% system memory can be on node 0? Are you saying 100% of "hot" memory? This makes me suspect you _might_ actually wanted to implement what I was expecting and the first patch of this series is documenting (Scheme-eligible bytes ratio of a node) but somehow something bothered you to drop the ball in a wrong place.

Quoted 10 lines
      target_bp  = 4000 (40%)
      current > target → migrate cold pages away from Node 0

    After Iteration 1:
      current_bp = 4000 (40% on Node 0)

    Iteration 2:
      current_bp = 4000
      target_bp  = 4000
      current >= target → STOP, goal satisfied

Please note that the goal-based quota auto-tuning works in proportional way, preferring small steps and "eventual" goal convergence. As a result, migration will occur a few more times until it is completely stopped after the goal is satisfied. Unless there is another scheme that migrates pages into node 0, you may end up having node 0 having a bit less than the 40% memory.

Quoted 2 lines

No oscillation - migration stops when target state is reached.

So, little bit of oscillation could still happen. Hopefully that shouldn't be significant, though.

IIRC, SK hynix people also confused with the behavior when they experimented migrate_{hot,cold} action with NODE_MEM_USED_BP goal based quota auto-tuning, but using only a single scheme that does migration in a single direction. Because this is at least second time it made confusion, if you need, maybe I can try to add a feature for making DAMOS immediately stops after the goal is satisfied. Let me know if such new feature can be useful for you. Cc-ing SK hynix people (Honggyu and Yunjeong) so that they can correct me if my memory is broken, or answer if the new feature I described here can be useful for them.

Quoted 16 lines

No page identity tracking needed because we measure the END STATE, not which specific pages were moved. The early-exit prevents oscillation by stopping when the goal is met.

Why get_goal_metric() Ops Callback
==================================

The bp calculation requires iterating over monitored PA regions:

    for (pfn = start_pfn; pfn < end_pfn; pfn++) {
        if (page_to_nid(pfn_to_page(pfn)) == nid)
            node_bytes += PAGE_SIZE;
    }
    bp = node_bytes * 10000 / system_total;
Quoted 8 lines

This requires address-space knowledge that only the ops provider has. Existing goal metrics (PSI, node_mem_*, node_memcg_*) are computed in core using system-wide data that doesn't require iterating monitored regions.

The new get_goal_metric() callback in damon_operations allows:

1. Core to remain generic - handles all common metrics

I agree this is indeed making the design clean. But, we already having such exception, like core.c code using 'damon_target_has_pid(). Having just one more exception seems ok to me, unless it makes code too ugly.

Quoted 3 lines

2. Ops providers to implement metrics requiring region iteration
3. Clean separation - PA implements node_sys_bp, VA could add different metrics in future

I agree it could be useful for clean support of virtual address mode in future. But, I'd prefer making this as simple and small as possible for the support we will use at the moment.

Quoted 1 line

4. Optional implementation - ops return 0 if metric unsupported

Again, letting core logic having a bit of ops layer information is not a big problem to my humble perspective.

So, I'd more prefer not adding the Ops callback, unless you have some other concerns.

Quoted 15 lines

This design ensures node_sys_bp is only computed when using PA contexts where it makes sense, while keeping the core quota goal infrastructure unchanged for other metrics and ops providers.

The callback pattern allows each ops provider to implement metrics specific to its address space model without burdening the core with ops-specific knowledge.

Advantages of PA-Based Migration
================================

PA-based migration with DAMON enables integration of multiple hotness sources for migration decisions:

Quoted 8 lines

1. DAMON's native access pattern monitoring
2. Fault-based information (similar to NUMA Balancing)
3. Future: Hardware monitoring units (e.g., CXL CHMU)
4. Future: Instruction-Based Sampling (AMD IBS, Intel PEBS)

Unlike VA-based approaches tied to individual process address spaces, PA monitoring can aggregate hotness information from multiple sources to make system-wide migration decisions across the entire physical memory space.

Maybe you are saying about the damon_report_access() based DAMON extension project [1]? Since it is not yet upstreamed, and the long term plan is to support not only physical address but also virtual address space, I think this section is better to be removed, unless the DAMON extension project is merged before this patch series. I expect this patch series will be merged much earlier than the extension project.

Quoted 12 lines

Complementary to Existing vaddr Migration
=========================================

This series complements rather than replaces the vaddr weighted interleave migration merged in 6.18:

vaddr migration (weight-based):
- Per-process control
- Fine-grained interleave patterns via VMA offset
- Deterministic placement based on weights

Quoted 5 lines

paddr migration (bp-based, this series):
- System-wide control
- Target-state goals for node capacity management
- No rmap overhead
- Aggregates multiple hotness sources

As I mentioned above, the last item could be dropped until the extension project [1] is upstreamed.

Quoted 34 lines

Capacity Clamping
=================

The series also implements capacity clamping for node_sys_bp goals. In a system where Node 0 has 40% of total RAM, setting a target of 50% is impossible. The implementation clamps:

effective_target = min(user_target, node_capacity_bp)

This prevents the quota auto-tuning from chasing impossible targets and avoids thrashing in two-context DRAM↔CXL setups.

Patches
=======

1/5: mm/damon/core: add DAMOS_QUOTA_NODE_SYS_BP metric
Adds the enum value and documentation.

2/5: mm/damon: add get_goal_metric() op and PA provider
Introduces the ops callback and PA implementation that iterates monitored regions to calculate node_sys_bp without rmap.

3/5: mm/damon/core: add new ops-specific goal metric
Wires the new metric into core's quota goal evaluation, delegating to ops.get_goal_metric() for DAMOS_QUOTA_NODE_SYS_BP.

4/5: mm/damon/paddr: capacity clamp and directional early-exit
Adds capacity clamping and early-exit logic to prevent migration when goal is already satisfied.

5/5: mm/damon/sysfs-schemes: accept "node_sys_bp" in goal's target_metric
Exposes the new metric to userspace via sysfs.

Quoted 6 lines

Status
======

This is an early RFC for design review. The patches:
- Compile successfully with no errors or warnings
- Have NOT been tested on actual hardware

I understand you plan to run tests, and that would be useful, appreciate. Note that I think having no test results is blocker for RFC patch series. So, again appreciate for sharing this RFC! :)

Quoted 2 lines

Feedback on the overall approach and design is appreciated.

I hope my above comments helps the forward progress of this nice series.

Quoted 17 lines

References
==========

[1] mm/damon/vaddr: Allow interleaving in migrate_{hot,cold} actions (v4)
https://lore.kernel.org/linux-mm/[email protected]/
Merged in Linux 6.18

Ravi Jonnalagadda (5):
mm/damon/core: add DAMOS_QUOTA_NODE_SYS_BP metric mm/damon: add get_goal_metric() op and PA provider mm/damon/core: add new ops-specific goal metric mm/damon/paddr: capacity clamp and directional early-exit for
node_sys_bp
mm/damon/sysfs-schemes: accept "node_sys_bp" in goal's target_metric

Quoted 8 lines
 include/linux/damon.h    |   5 ++
 mm/damon/core.c          |  34 ++++++++++---
 mm/damon/paddr.c         | 102 +++++++++++++++++++++++++++++++++++++++
 mm/damon/sysfs-schemes.c |   7 +++
 4 files changed, 141 insertions(+), 7 deletions(-)
Signature · 1 line
2.43.0

[1] https://lore.kernel.org/damon/[email protected]/

Thanks,
SJ

#7 lore raw
Quoted 6 lines · SeongJae Park

Cc-ing SK hynix folks (Honggyu and Yunjeong) for quota auto-tuning behavior confusion (not stop immediately after satisfying the goal) I discuss below.

Hello Ravi,

Hi SJ,

Thank you for the detailed review and for Cc'ing SK hynix folks.

Quoted 14 lines

Thank you for sharing this great RFC!

I had a chance to be involved in a high level design of this series, off the mailing list. I'm disclosing the fact for others' context, since a few of my comments below are based on the previous off-list discussion.

Quoted 1 line · On Thu, 22 Jan 2026 20:57:23 -0800 Ravi Jonnalagadda <[email protected]>…

This series introduces a new DAMON quota goal metric,

As I discussed off the list, I believe the new goal can be useful. Thank you again for making this!

Quoted 9 lines
Quoted 4 lines

node_sys_bp, designed
for controlling memory migration in heterogeneous memory systems (e.g., DRAM↔CXL tiering). These patches are provided as an initial RFC and have not been tested on actual hardware.

I feel like the name is not very self explaining what it is, though. I'm also bit confused about what it does is what we discussed off list. I'll add more details below, on more detailed explanation about the new goal.

Will change the name to damos_target_mem_node_sys_bp in v2.

Quoted 110 lines
Quoted 10 lines

Background and Motivation
=========================

A previous patch series [1] by Bijan Tabatabai and myself added weighted interleave support for DAMON migrate_{hot,cold} actions. That series implemented the feature for vaddr (virtual address) schemes because the weight-based approach requires VMA offset information to determine target nodes:

Quoted 8 lines

target_node = (vma_offset % total_weight) → node_from_weights

For paddr (physical address) schemes, obtaining VMA offset requires costly rmap (reverse mapping) walks. As noted in that series:

"However, finding out how a folio is mapped inside of a VMA requires
a costly rmap walk when using a paddr scheme. As such, we have decided that this functionality makes more sense as a vaddr scheme."

Indeed the rmap overhead was one main concern. I'd like to also note that, however, another major concern from me was that the behavior sounds not easy to intuitively understand.

Quoted 69 lines

This new series takes a different approach that enables PA-based migration WITHOUT requiring rmap walks, by using basis points (bp) target-state goals instead of weight-based action rates.

The rmap Cost vs Oscillation Trade-off
======================================

For PA-based migration with weights, there are two possibilities:

1. Weight-based with rmap:
- Use rmap to find VMA offset for each physical page
- Apply weights based on VMA offset (same algorithm as VA)
- Works correctly: VMA offset provides stable identity
- Problem: rmap walks are expensive for every migration candidate

2. Weight-based without rmap:
- Attempt to apply weights using only physical address information
- No stable identity across migrations
- Results in page oscillation (see below)
- Not viable

The Oscillation Problem (Weights Without rmap)
==============================================

Weight-based migration relies on a stable identifier to determine which node a page "belongs to". For VA, this is the VMA offset - it remains constant regardless of which physical node backs the page. For PA without rmap, no such stable identifier exists.

Consider a two-node system with weights {Node 0: 40, Node 1: 60}:

Initial state:
Hot pages on Node 0: PFN 0x1000, 0x1001, 0x1002, 0x1003, 0x1004 Node 1: empty

Iteration 1 - Weight-based selection (no rmap):

    System tries to achieve 40/60 distribution
    Selects pages at PFN 0x1002, 0x1003, 0x1004 (60%) for Node 1
    After migration, these pages get NEW PFNs on Node 1:
      PFN 0x1002 → PFN 0x5000 (Node 1)
      PFN 0x1003 → PFN 0x5001 (Node 1)
      PFN 0x1004 → PFN 0x5002 (Node 1)

State after Iteration 1:

    Node 0: PFN 0x1000, 0x1001 (40%)
    Node 1: PFN 0x5000, 0x5001, 0x5002 (60%)

Iteration 2 - Weight-based selection runs again:
System sees pages at PFN 0x5000, 0x5001, 0x5002 on Node 1 These are "new" pages from the system's perspective NO MEMORY that these were just migrated FROM Node 0 Weight-based logic may select some for migration back to Node 0

Iteration 3, 4, 5...:
Same pages continue bouncing between nodes Each migration changes the PFN, erasing any "history" System never converges to stable state

The fundamental issue: weights define an ACTION RATE ("migrate X% of candidate pages to each node") rather than a TARGET STATE. Without stable page identity (which rmap provides via VMA offset), the system cannot determine which pages have already been "handled" and continues to reprocess the same logical pages indefinitely.

With rmap, the VMA offset provides stable identity - a page at file offset 0x1000 always hashes to the same target node regardless of its current PFN. Without rmap, we have no such anchor, and weights become meaningless.

Quoted 6 lines

Solution: bp-Based Target State Goals
=====================================

Instead of specifying action rates, node_sys_bp specifies a TARGET STATE:

"Node N should contain X basis points (X/10000) of system memory"

I'm not sure if this is exactly what we discussed off list. What I expected the goal would be based on our discussion is, what the first patch is saying. To quote the part,

@@ -155,6 +155,7 @@ enum damos_action {
* @DAMOS_QUOTA_NODE_MEM_FREE_BP: MemFree ratio of a node.
* @DAMOS_QUOTA_NODE_MEMCG_USED_BP: MemUsed ratio of a node for a cgroup.
* @DAMOS_QUOTA_NODE_MEMCG_FREE_BP: MemFree ratio of a node for a cgroup.
+ * @DAMOS_QUOTA_NODE_SYS_BP: Scheme-eligible bytes ratio of a node.
* @NR_DAMOS_QUOTA_GOAL_METRICS: Number of DAMOS quota goal metrics.

Quoted 4 lines

That is, my understanding of what we want to achive with the new goal is, letting users to ask DAMON "migrate hot pages of node A to node B, aiming X % of node B becomes hot, as a result of the migrations".

Yes, this is the intent. Looking back at my implementation, I see the mismatch:

1. **Numerator**: Should count only scheme-eligible bytes.

2. **Denominator**: Should use node capacity, but I used total system memory.

Quoted 6 lines

But your above description is not saying about the "scheme-eligibility". Also, the goal metric is the ratio of the memory to the entire system memory, not just a given node. My quick read of damon_pa_get_node_sys_bp() on the second patch of this series confirms the implementation is following your description, not what I imagined.

You're right. The implementation diverged from what we discussed. I'll fix both the numerator and denominator in v2.

Quoted 5 lines

Have you found something we missed during the off list discussion, and therefore changed the definition of the goal to the current one? If so, could you share the details? Or, you might also wanted to implement what I expected, but something bothered your code and documentation?

While implementing, I was thinking about how the metric would behave in a
system-wide context across multiple nodes and ended up using total system
memory as the denominator, thinking it would simplify distribution calculations. However, as you pointed out, using node capacity as the denominator is the right approach. I'll fix this in v2.

Quoted 9 lines
Quoted 8 lines

The migration control loop:

1. Calculate current_bp: sum bytes per node across monitored regions
2. Compare: if current_bp >= target_bp, STOP (goal satisfied)
3. Otherwise: continue migrating toward target

Example with target: "Node 0 should have 4000 bp (40%)"

Quoted 8 lines

Regardless of why my expectation was different from this one, the goal I was expecting might still be used for your descripted case. That is, the scheme's target access pattern could be "any access pattern", and the target metric value could be adjusted based on the ratio between the node 0 memory and the total memory. For example, let's suppose there is 100 GiB memory in total, and 80 GiB memory in node 0 (80% of total memory in node 0). Then 40 % of total memory, which is the target of the above example, is 40 GiB, which is 50 % of node 0. So, you could use the goal I expected, with 50 % as the target value.

Agreed.

Quoted 6 lines

Imaginable usage of that would look like,

    # --damos_quota_goal receives <metric name>, <target metric value>, and
    # <node id of the goal>
    damo tune --damos_action migrate_hot --damos_quota_goal new_goal_metric 50% 0
Quoted 11 lines

I sure you know that you could s/migrate_hot/migrate_cold/ if you prefer migrating cold pages first.

So, the metric of my expected definition can still be useful for your new use case. I'd prefer my expected definition if there is no concern, since it seems useful for more cases.

Quoted 3 lines
    Iteration 1:
      current_bp = 10000 (100% on Node 0)

Got it.

Quoted 17 lines

How could 100% system memory can be on node 0? Are you saying 100% of "hot" memory? This makes me suspect you _might_ actually wanted to implement what I was expecting and the first patch of this series is documenting (Scheme-eligible bytes ratio of a node) but somehow something bothered you to drop the ball in a wrong place.

Quoted 10 lines
      target_bp  = 4000 (40%)
      current > target → migrate cold pages away from Node 0

    After Iteration 1:
      current_bp = 4000 (40% on Node 0)

    Iteration 2:
      current_bp = 4000
      target_bp  = 4000
      current >= target → STOP, goal satisfied

Right, that example was misleading. With node capacity as denominator, "current_bp = 10000" would mean "100% of the node's capacity is scheme-eligible pages" which would be a valid state.

Quoted 7 lines

Please note that the goal-based quota auto-tuning works in proportional way, preferring small steps and "eventual" goal convergence. As a result, migration will occur a few more times until it is completely stopped after the goal is satisfied. Unless there is another scheme that migrates pages into node 0, you may end up having node 0 having a bit less than the 40% memory.

Quoted 1 line
Quoted 5 lines
Quoted 1 line

No oscillation - migration stops when target state is reached.

So, little bit of oscillation could still happen. Hopefully that shouldn't be significant, though.

Yes, As we discussed offline, for a two-context approach:

Context 0: monitors node 0, migrate_hot → node 1
goal: damos_target_mem_node_sys_bp, nid=1, target=4000 "Stop when node 1 is 40% hot"

Context 1: monitors node 1, migrate_hot → node 0
goal: damos_target_mem_node_sys_bp, nid=0, target=6000 "Stop when node 0 is 60% hot"

Each context eventually stops when its target node reaches the desired threshold,
and the reverse direction is handled by the other context. For my use case, eventual convergence with this setup could be acceptable. An immediate-stop feature could still be useful for the broader community. Will test and post results after the next iteration.

Quoted 46 lines

IIRC, SK hynix people also confused with the behavior when they experimented migrate_{hot,cold} action with NODE_MEM_USED_BP goal based quota auto-tuning, but using only a single scheme that does migration in a single direction. Because this is at least second time it made confusion, if you need, maybe I can try to add a feature for making DAMOS immediately stops after the goal is satisfied. Let me know if such new feature can be useful for you. Cc-ing SK hynix people (Honggyu and Yunjeong) so that they can correct me if my memory is broken, or answer if the new feature I described here can be useful for them.

Quoted 16 lines

No page identity tracking needed because we measure the END STATE, not which specific pages were moved. The early-exit prevents oscillation by stopping when the goal is met.

Why get_goal_metric() Ops Callback
==================================

The bp calculation requires iterating over monitored PA regions:

    for (pfn = start_pfn; pfn < end_pfn; pfn++) {
        if (page_to_nid(pfn_to_page(pfn)) == nid)
            node_bytes += PAGE_SIZE;
    }
    bp = node_bytes * 10000 / system_total;
Quoted 8 lines

This requires address-space knowledge that only the ops provider has. Existing goal metrics (PSI, node_mem_*, node_memcg_*) are computed in core using system-wide data that doesn't require iterating monitored regions.

The new get_goal_metric() callback in damon_operations allows:

1. Core to remain generic - handles all common metrics

I agree this is indeed making the design clean. But, we already having such exception, like core.c code using 'damon_target_has_pid(). Having just one more exception seems ok to me, unless it makes code too ugly.

Quoted 3 lines

2. Ops providers to implement metrics requiring region iteration
3. Clean separation - PA implements node_sys_bp, VA could add different metrics in future

I agree it could be useful for clean support of virtual address mode in future. But, I'd prefer making this as simple and small as possible for the support we will use at the moment.

Quoted 8 lines
Quoted 1 line

4. Optional implementation - ops return 0 if metric unsupported

Again, letting core logic having a bit of ops layer information is not a big problem to my humble perspective.

So, I'd more prefer not adding the Ops callback, unless you have some other concerns.

Agreed. I'll remove the get_goal_metric() ops callback in v2

Quoted 24 lines
Quoted 15 lines

This design ensures node_sys_bp is only computed when using PA contexts where it makes sense, while keeping the core quota goal infrastructure unchanged for other metrics and ops providers.

The callback pattern allows each ops provider to implement metrics specific to its address space model without burdening the core with ops-specific knowledge.

Advantages of PA-Based Migration
================================

PA-based migration with DAMON enables integration of multiple hotness sources for migration decisions:

Quoted 8 lines

1. DAMON's native access pattern monitoring
2. Fault-based information (similar to NUMA Balancing)
3. Future: Hardware monitoring units (e.g., CXL CHMU)
4. Future: Instruction-Based Sampling (AMD IBS, Intel PEBS)

Unlike VA-based approaches tied to individual process address spaces, PA monitoring can aggregate hotness information from multiple sources to make system-wide migration decisions across the entire physical memory space.

Quoted 7 lines

Maybe you are saying about the damon_report_access() based DAMON extension project [1]? Since it is not yet upstreamed, and the long term plan is to support not only physical address but also virtual address space, I think this section is better to be removed, unless the DAMON extension project is merged before this patch series. I expect this patch series will be merged much earlier than the extension project.

Agreed. I'll remove the references to future hotness sources.

Quoted 62 lines
Quoted 12 lines

Complementary to Existing vaddr Migration
=========================================

This series complements rather than replaces the vaddr weighted interleave migration merged in 6.18:

vaddr migration (weight-based):
- Per-process control
- Fine-grained interleave patterns via VMA offset
- Deterministic placement based on weights

Quoted 5 lines

paddr migration (bp-based, this series):
- System-wide control
- Target-state goals for node capacity management
- No rmap overhead
- Aggregates multiple hotness sources

As I mentioned above, the last item could be dropped until the extension project [1] is upstreamed.

Quoted 34 lines

Capacity Clamping
=================

The series also implements capacity clamping for node_sys_bp goals. In a system where Node 0 has 40% of total RAM, setting a target of 50% is impossible. The implementation clamps:

effective_target = min(user_target, node_capacity_bp)

This prevents the quota auto-tuning from chasing impossible targets and avoids thrashing in two-context DRAM↔CXL setups.

Patches
=======

1/5: mm/damon/core: add DAMOS_QUOTA_NODE_SYS_BP metric
Adds the enum value and documentation.

2/5: mm/damon: add get_goal_metric() op and PA provider
Introduces the ops callback and PA implementation that iterates monitored regions to calculate node_sys_bp without rmap.

3/5: mm/damon/core: add new ops-specific goal metric
Wires the new metric into core's quota goal evaluation, delegating to ops.get_goal_metric() for DAMOS_QUOTA_NODE_SYS_BP.

4/5: mm/damon/paddr: capacity clamp and directional early-exit
Adds capacity clamping and early-exit logic to prevent migration when goal is already satisfied.

5/5: mm/damon/sysfs-schemes: accept "node_sys_bp" in goal's target_metric
Exposes the new metric to userspace via sysfs.

Quoted 6 lines

Status
======

This is an early RFC for design review. The patches:
- Compile successfully with no errors or warnings
- Have NOT been tested on actual hardware

Quoted 9 lines

I understand you plan to run tests, and that would be useful, appreciate. Note that I think having no test results is blocker for RFC patch series. So, again appreciate for sharing this RFC! :)

Quoted 2 lines

Feedback on the overall approach and design is appreciated.

I hope my above comments helps the forward progress of this nice series.

They are very helpful. Thanks a lot.

Quoted 23 lines
Quoted 23 lines

References
==========

[1] mm/damon/vaddr: Allow interleaving in migrate_{hot,cold} actions (v4)
https://lore.kernel.org/linux-mm/[email protected]/
Merged in Linux 6.18

Ravi Jonnalagadda (5):
mm/damon/core: add DAMOS_QUOTA_NODE_SYS_BP metric mm/damon: add get_goal_metric() op and PA provider mm/damon/core: add new ops-specific goal metric mm/damon/paddr: capacity clamp and directional early-exit for
node_sys_bp
mm/damon/sysfs-schemes: accept "node_sys_bp" in goal's target_metric

 include/linux/damon.h    |   5 ++
 mm/damon/core.c          |  34 ++++++++++---
 mm/damon/paddr.c         | 102 +++++++++++++++++++++++++++++++++++++++
 mm/damon/sysfs-schemes.c |   7 +++
 4 files changed, 141 insertions(+), 7 deletions(-)
Quoted 8 lines
Quoted 2 lines

--
2.43.0

[1] https://lore.kernel.org/damon/[email protected]/

Thanks,
SJ

Thanks,
Ravi.

maintainer
#8 lore raw
Quoted 31 lines · On Tue, 27 Jan 2026 10:52:53 -0800 Ravi Jonnalagadda <[email protected]>…
Quoted 6 lines · SeongJae Park

Cc-ing SK hynix folks (Honggyu and Yunjeong) for quota auto-tuning behavior confusion (not stop immediately after satisfying the goal) I discuss below.

Hello Ravi,

Hi SJ,

Thank you for the detailed review and for Cc'ing SK hynix folks.

Quoted 19 lines

Thank you for sharing this great RFC!

I had a chance to be involved in a high level design of this series, off the mailing list. I'm disclosing the fact for others' context, since a few of my comments below are based on the previous off-list discussion.

Quoted 1 line · On Thu, 22 Jan 2026 20:57:23 -0800 Ravi Jonnalagadda <[email protected]>…

This series introduces a new DAMON quota goal metric,

As I discussed off the list, I believe the new goal can be useful. Thank you again for making this!

Quoted 4 lines

node_sys_bp, designed
for controlling memory migration in heterogeneous memory systems (e.g., DRAM↔CXL tiering). These patches are provided as an initial RFC and have not been tested on actual hardware.

Quoted 6 lines
Quoted 4 lines

I feel like the name is not very self explaining what it is, though. I'm also bit confused about what it does is what we discussed off list. I'll add more details below, on more detailed explanation about the new goal.

Will change the name to damos_target_mem_node_sys_bp in v2.

Sounds good name. But, could it be more consistent with existing node-related quota goal metrics such as 'node_mem_used_bp' and 'node_mem_free_bp'? What about... say... 'node_target_mem_bp'? Please feel free to suggest a better one.

[...]

Quoted 38 lines
Quoted 18 lines
Quoted 6 lines

Solution: bp-Based Target State Goals
=====================================

Instead of specifying action rates, node_sys_bp specifies a TARGET STATE:

"Node N should contain X basis points (X/10000) of system memory"

I'm not sure if this is exactly what we discussed off list. What I expected the goal would be based on our discussion is, what the first patch is saying. To quote the part,

@@ -155,6 +155,7 @@ enum damos_action {
* @DAMOS_QUOTA_NODE_MEM_FREE_BP: MemFree ratio of a node.
* @DAMOS_QUOTA_NODE_MEMCG_USED_BP: MemUsed ratio of a node for a cgroup.
* @DAMOS_QUOTA_NODE_MEMCG_FREE_BP: MemFree ratio of a node for a cgroup.
+ * @DAMOS_QUOTA_NODE_SYS_BP: Scheme-eligible bytes ratio of a node.
* @NR_DAMOS_QUOTA_GOAL_METRICS: Number of DAMOS quota goal metrics.

Quoted 4 lines

That is, my understanding of what we want to achive with the new goal is, letting users to ask DAMON "migrate hot pages of node A to node B, aiming X % of node B becomes hot, as a result of the migrations".

Yes, this is the intent. Looking back at my implementation, I see the mismatch:

1. **Numerator**: Should count only scheme-eligible bytes.

2. **Denominator**: Should use node capacity, but I used total system memory.

Quoted 6 lines

But your above description is not saying about the "scheme-eligibility". Also, the goal metric is the ratio of the memory to the entire system memory, not just a given node. My quick read of damon_pa_get_node_sys_bp() on the second patch of this series confirms the implementation is following your description, not what I imagined.

Quoted 2 lines

You're right. The implementation diverged from what we discussed. I'll fix both the numerator and denominator in v2.

Thank you for clarifying it and sharing the next plan, all sounds good!

[...]

Quoted 19 lines
Quoted 7 lines

Please note that the goal-based quota auto-tuning works in proportional way, preferring small steps and "eventual" goal convergence. As a result, migration will occur a few more times until it is completely stopped after the goal is satisfied. Unless there is another scheme that migrates pages into node 0, you may end up having node 0 having a bit less than the 40% memory.

Quoted 1 line
Quoted 5 lines
Quoted 1 line

No oscillation - migration stops when target state is reached.

So, little bit of oscillation could still happen. Hopefully that shouldn't be significant, though.

Yes, As we discussed offline, for a two-context approach:

Context 0: monitors node 0, migrate_hot → node 1
goal: damos_target_mem_node_sys_bp, nid=1, target=4000 "Stop when node 1 is 40% hot"

Quoted 8 lines

Context 1: monitors node 1, migrate_hot → node 0
goal: damos_target_mem_node_sys_bp, nid=0, target=6000 "Stop when node 0 is 60% hot"

Each context eventually stops when its target node reaches the desired threshold,
and the reverse direction is handled by the other context. For my use case, eventual convergence with this setup could be acceptable.

Thank you for clarifying this! I'm relieved we don't have a concern here.

Quoted 1 line

An immediate-stop feature could still be useful for the broader community.

Thank you for the feedback. I will take more time on thinking about how to implement this.

Quoted 10 lines

Will test and post results after the next iteration.

Quoted 8 lines

IIRC, SK hynix people also confused with the behavior when they experimented migrate_{hot,cold} action with NODE_MEM_USED_BP goal based quota auto-tuning, but using only a single scheme that does migration in a single direction. Because this is at least second time it made confusion, if you need, maybe I can try to add a feature for making DAMOS immediately stops after the goal is satisfied. Let me know if such new feature can be useful for you. Cc-ing SK hynix people (Honggyu and Yunjeong) so that they can correct me if my memory is broken, or answer if the new feature I described here can be useful for them.

[...]

Quoted 34 lines
Quoted 34 lines
Quoted 11 lines

Why get_goal_metric() Ops Callback
==================================

The bp calculation requires iterating over monitored PA regions:

    for (pfn = start_pfn; pfn < end_pfn; pfn++) {
        if (page_to_nid(pfn_to_page(pfn)) == nid)
            node_bytes += PAGE_SIZE;
    }
    bp = node_bytes * 10000 / system_total;
Quoted 8 lines

This requires address-space knowledge that only the ops provider has. Existing goal metrics (PSI, node_mem_*, node_memcg_*) are computed in core using system-wide data that doesn't require iterating monitored regions.

The new get_goal_metric() callback in damon_operations allows:

1. Core to remain generic - handles all common metrics

I agree this is indeed making the design clean. But, we already having such exception, like core.c code using 'damon_target_has_pid(). Having just one more exception seems ok to me, unless it makes code too ugly.

Quoted 3 lines

2. Ops providers to implement metrics requiring region iteration
3. Clean separation - PA implements node_sys_bp, VA could add different metrics in future

I agree it could be useful for clean support of virtual address mode in future. But, I'd prefer making this as simple and small as possible for the support we will use at the moment.

Quoted 1 line

4. Optional implementation - ops return 0 if metric unsupported

Quoted 8 lines
Quoted 6 lines

Again, letting core logic having a bit of ops layer information is not a big problem to my humble perspective.

So, I'd more prefer not adding the Ops callback, unless you have some other concerns.

Agreed. I'll remove the get_goal_metric() ops callback in v2

Thank you for flexibly accepting my suggestion.

[...]

Quoted 23 lines
Quoted 15 lines
Quoted 6 lines

Advantages of PA-Based Migration
================================

PA-based migration with DAMON enables integration of multiple hotness sources for migration decisions:

Quoted 8 lines

1. DAMON's native access pattern monitoring
2. Fault-based information (similar to NUMA Balancing)
3. Future: Hardware monitoring units (e.g., CXL CHMU)
4. Future: Instruction-Based Sampling (AMD IBS, Intel PEBS)

Unlike VA-based approaches tied to individual process address spaces, PA monitoring can aggregate hotness information from multiple sources to make system-wide migration decisions across the entire physical memory space.

Quoted 7 lines

Maybe you are saying about the damon_report_access() based DAMON extension project [1]? Since it is not yet upstreamed, and the long term plan is to support not only physical address but also virtual address space, I think this section is better to be removed, unless the DAMON extension project is merged before this patch series. I expect this patch series will be merged much earlier than the extension project.

Quoted 1 line

Agreed. I'll remove the references to future hotness sources.

Thank you!

[...]

Quoted 6 lines
Quoted 4 lines
Quoted 1 line

Feedback on the overall approach and design is appreciated.

I hope my above comments helps the forward progress of this nice series.

They are very helpful. Thanks a lot.

The pleasure is mine, looking forward to the next version of this patch series :)

Thanks,
SJ

[...]

maintainer
#9 lore raw
Quoted 2 lines · On Tue, 27 Jan 2026 17:25:37 -0800 SeongJae Park <[email protected]> wrote:

On Tue, 27 Jan 2026 10:52:53 -0800 Ravi Jonnalagadda <[email protected]> wrote:

[...]

Quoted 4 lines
Quoted 1 line

An immediate-stop feature could still be useful for the broader community.

Thank you for the feedback. I will take more time on thinking about how to implement this.

I just posted an RFC patch series for this:
https://lore.kernel.org/[email protected]

Thanks,
SJ

[...]

#10 lore raw

Re: [RFC PATCH 0/5] mm/damon: Add node_sys_bp quota goal metric for

In reply to SeongJae Park
Quoted 3 lines · SeongJae Park

Cc-ing SK hynix folks (Honggyu and Yunjeong) for quota auto-tuning behavior confusion (not stop immediately after satisfying the goal) I discuss below.

Hi Seonjae, thanks for Cc-ing us :)

Quoted 13 lines

Please note that the goal-based quota auto-tuning works in proportional way, preferring small steps and "eventual" goal convergence. As a result, migration will occur a few more times until it is completely stopped after the goal is satisfied. Unless there is another scheme that migrates pages into node 0, you may end up having node 0 having a bit less than the 40% memory.

Quoted 2 lines

No oscillation - migration stops when target state is reached.

So, little bit of oscillation could still happen. Hopefully that shouldn't be significant, though.

Quoted 9 lines

IIRC, SK hynix people also confused with the behavior when they experimented migrate_{hot,cold} action with NODE_MEM_USED_BP goal based quota auto-tuning, but using only a single scheme that does migration in a single direction. Because this is at least second time it made confusion, if you need, maybe I can try to add a feature for making DAMOS immediately stops after the goal is satisfied. Let me know if such new feature can be useful for you. Cc-ing SK hynix people (Honggyu and Yunjeong) so that they can correct me if my memory is broken, or answer if the new feature I described here can be useful for them.

Yes, you're absolutely right. Currently, esz(effective size) starts from 0 and esz gradually increases as current approaches target.
Once current reaches target, esz then begins to decrease.

However, we observed that even after current hits target, migration still continues relatively aggressively - because esz remains high, and it takes time for it to decrease.

To address this, we previously suggested that initializing esz at target (or something suitably large value, rather than 0) and letting it gradually decrease as current gets closer to target.
This would allow for stronger migration when current is far form target, and gradually weaken migration as current approaches target.

Such a feature would be useful for us to experiment with tiered memory system :)

Best Regards,
Yunjeong

maintainer
#11 lore raw

Re: [RFC PATCH 0/5] mm/damon: Add node_sys_bp quota goal metric for

Quoted 38 lines · On Wed, 4 Feb 2026 11:25:35 +0900 Yunjeong Mun <[email protected]> wrote:
Quoted 3 lines · SeongJae Park

Cc-ing SK hynix folks (Honggyu and Yunjeong) for quota auto-tuning behavior confusion (not stop immediately after satisfying the goal) I discuss below.

Hi Seonjae, thanks for Cc-ing us :)

Quoted 13 lines

Please note that the goal-based quota auto-tuning works in proportional way, preferring small steps and "eventual" goal convergence. As a result, migration will occur a few more times until it is completely stopped after the goal is satisfied. Unless there is another scheme that migrates pages into node 0, you may end up having node 0 having a bit less than the 40% memory.

Quoted 2 lines

No oscillation - migration stops when target state is reached.

So, little bit of oscillation could still happen. Hopefully that shouldn't be significant, though.

Quoted 9 lines

IIRC, SK hynix people also confused with the behavior when they experimented migrate_{hot,cold} action with NODE_MEM_USED_BP goal based quota auto-tuning, but using only a single scheme that does migration in a single direction. Because this is at least second time it made confusion, if you need, maybe I can try to add a feature for making DAMOS immediately stops after the goal is satisfied. Let me know if such new feature can be useful for you. Cc-ing SK hynix people (Honggyu and Yunjeong) so that they can correct me if my memory is broken, or answer if the new feature I described here can be useful for them.

Yes, you're absolutely right. Currently, esz(effective size) starts from 0 and esz gradually increases as current approaches target.
Once current reaches target, esz then begins to decrease.

However, we observed that even after current hits target, migration still continues relatively aggressively - because esz remains high, and it takes time for it to decrease.

Quoted 7 lines

To address this, we previously suggested that initializing esz at target (or something suitably large value, rather than 0) and letting it gradually decrease as current gets closer to target.
This would allow for stronger migration when current is far form target, and gradually weaken migration as current approaches target.

Such a feature would be useful for us to experiment with tiered memory system :)

Thank you for confirming, Yunjeong. I now agree this is what really need to be implemented. And I agree your suggestion makes sense for the use case. I want to take sufficient time for good design of it, though. I will share update as soon as I get some idea.

Thanks,
SJ

[...]

maintainer
#12 lore raw

Re: [RFC PATCH 0/5] mm/damon: Add node_sys_bp quota goal metric for

Quoted 3 lines · On Tue, 3 Feb 2026 22:06:40 -0800 SeongJae Park <[email protected]> wrote:
Quoted 1 line · On Wed, 4 Feb 2026 11:25:35 +0900 Yunjeong Mun <[email protected]> wrote:

On Fri, Jan 23, 2026 at 05:50:43PM -0800, SeongJae Park wrote:

[...]

Quoted 24 lines
Quoted 24 lines
Quoted 9 lines

IIRC, SK hynix people also confused with the behavior when they experimented migrate_{hot,cold} action with NODE_MEM_USED_BP goal based quota auto-tuning, but using only a single scheme that does migration in a single direction. Because this is at least second time it made confusion, if you need, maybe I can try to add a feature for making DAMOS immediately stops after the goal is satisfied. Let me know if such new feature can be useful for you. Cc-ing SK hynix people (Honggyu and Yunjeong) so that they can correct me if my memory is broken, or answer if the new feature I described here can be useful for them.

Yes, you're absolutely right. Currently, esz(effective size) starts from 0 and esz gradually increases as current approaches target.
Once current reaches target, esz then begins to decrease.

However, we observed that even after current hits target, migration still continues relatively aggressively - because esz remains high, and it takes time for it to decrease.

To address this, we previously suggested that initializing esz at target (or something suitably large value, rather than 0) and letting it gradually decrease as current gets closer to target.
This would allow for stronger migration when current is far form target, and gradually weaken migration as current approaches target.

Quoted 6 lines
Quoted 1 line

Such a feature would be useful for us to experiment with tiered memory system :)

Thank you for confirming, Yunjeong. I now agree this is what really need to be implemented. And I agree your suggestion makes sense for the use case. I want to take sufficient time for good design of it, though. I will share update as soon as I get some idea.

I just posted an RFC patch series for this:
https://lore.kernel.org/[email protected]

Thanks,
SJ

[...]