Skip to contentSkip to conversation

mm/damon: add node_eligible_mem_bp and node_ineligible_mem_bp goal metrics

PATCH In review Ravi Jonnalagadda 2 participants 10 messages Apr 5 – Apr 8

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

v6 cover mm/damon: add node_eligible_mem_bp and node_ineligible_mem_bp goal metrics

Changes since v5:
=================
https://lore.kernel.org/linux-mm/[email protected]/

- Rebased onto mm-new instead of damon/next for sashiko review
- Removed Reported-by/Closes tags per maintainer feedback (not needed for bugs found before merge)

Changes since v4:
=================
https://lore.kernel.org/linux-mm/[email protected]/

- Fixed commit message description for DAMOS_QUOTA_NODE_INELIGIBLE_MEM_BP per review feedback
- Added clarifying comment for ops-common.h include (for damon_get_folio())
- Fixed build error when CONFIG_DAMON_PADDR is disabled by adding #ifdef CONFIG_DAMON_PADDR guards around functions using damon_get_folio()
- Dropped RFC tag per maintainer feedback

This patch is based on top of mm-new.

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

In heterogeneous memory systems, controlling memory distribution across NUMA nodes is essential for performance optimization. This patch enables system-wide page distribution with target-state goals such as "maintain 30% of scheme-eligible memory on CXL" using PA-mode DAMON schemes.

What These Metrics Measure
==========================

node_eligible_mem_bp:
scheme_eligible_bytes_on_node / total_scheme_eligible_bytes * 10000

node_ineligible_mem_bp:

    (total - scheme_eligible_bytes_on_node) / total * 10000

These metrics are complementary: eligible_bp + ineligible_bp = 10000 bp.

Two-Scheme Setup for Hot Page Distribution
==========================================

For maintaining hot memory on DRAM (node 0) and CXL (node 1) in a 7:3 ratio:

    PUSH scheme: migrate_hot from node 0 -> node 1
      goal: node_ineligible_mem_bp, nid=0, target=3000
      "Move hot pages from DRAM to CXL if more than 70% of hot data is
       in DRAM"

    PULL scheme: migrate_hot from node 1 -> node 0
      goal: node_eligible_mem_bp, nid=0, target=7000
      "Move hot pages from CXL to DRAM if less than 70% of hot data is
       in DRAM"

The complementary goals create a feedback loop that converges to the target distribution.

Testing Results
===============

Functionally tested on a two-node heterogeneous memory system with DRAM (node 0) and CXL memory (node 1). A PUSH+PULL scheme configuration using migrate_hot actions was used to reach a target hot memory ratio between the two tiers. Testing used the TEMPORAL goal tuner available in damon/next and mm-unstable.

With the TEMPORAL tuner, the system converges quickly to the target distribution. The tuner drives esz to maximum when under goal and to zero once the goal is met, forming a simple on/off feedback loop that stabilizes at the desired ratio.

With the CONSIST tuner, the scheme still converges but more slowly, as it migrates and then throttles itself based on quota feedback. The time to reach the goal varies depending on workload intensity.

Note: These metrics work with both TEMPORAL and CONSIST goal tuners.

Ravi Jonnalagadda (1):
mm/damon: add node_eligible_mem_bp and node_ineligible_mem_bp goal
metrics

 include/linux/damon.h    |   6 ++
 mm/damon/core.c          | 188 ++++++++++++++++++++++++++++++++++++---
 mm/damon/sysfs-schemes.c |  12 +++
 3 files changed, 192 insertions(+), 14 deletions(-)

base-commit: b47b4fa4c232ee36aae58630e9d6520e35d33f3a

Signature · 1 line
2.43.0
#1 lore raw

PATCH v6 1/1 mm/damon: add node_eligible_mem_bp and node_ineligible_mem_bp goal metrics

Add new quota goal metrics for memory tiering that track scheme-eligible memory distribution across NUMA nodes:

- DAMOS_QUOTA_NODE_ELIGIBLE_MEM_BP: ratio of eligible memory on a node
- DAMOS_QUOTA_NODE_INELIGIBLE_MEM_BP: ratio of ineligible memory on a node

These complementary metrics enable push-pull migration schemes that maintain a target memory distribution across different NUMA nodes representing different memory tiers, based on access patterns defined by each scheme.

The metrics iterate scheme-eligible regions and use damon_get_folio() to determine NUMA node placement of each folio, calculating the ratio of eligible memory on the specified node versus total eligible memory.

The implementation is guarded by CONFIG_DAMON_PADDR since damon_get_folio() is only available when physical address space monitoring is enabled.

---
 include/linux/damon.h    |   6 ++
 mm/damon/core.c          | 188 ++++++++++++++++++++++++++++++++++++---
 mm/damon/sysfs-schemes.c |  12 +++
 3 files changed, 192 insertions(+), 14 deletions(-)
include/linux/damon.h +6 -0
index f2cdb7c3f5e6..a268f44beabf 100644
@@ -159,6 +159,10 @@ enum damos_action {
  * @DAMOS_QUOTA_NODE_MEMCG_FREE_BP:	MemFree ratio of a node for a cgroup.
  * @DAMOS_QUOTA_ACTIVE_MEM_BP:		Active to total LRU memory ratio.
  * @DAMOS_QUOTA_INACTIVE_MEM_BP:	Inactive to total LRU memory ratio.
+ * @DAMOS_QUOTA_NODE_ELIGIBLE_MEM_BP:	Scheme-eligible memory ratio of a
+ *					node.
+ * @DAMOS_QUOTA_NODE_INELIGIBLE_MEM_BP:	Scheme-ineligible memory 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.
@@ -172,6 +176,8 @@ enum damos_quota_goal_metric {
 	DAMOS_QUOTA_NODE_MEMCG_FREE_BP,
 	DAMOS_QUOTA_ACTIVE_MEM_BP,
 	DAMOS_QUOTA_INACTIVE_MEM_BP,
+	DAMOS_QUOTA_NODE_ELIGIBLE_MEM_BP,
+	DAMOS_QUOTA_NODE_INELIGIBLE_MEM_BP,
 	NR_DAMOS_QUOTA_GOAL_METRICS,
 };
 
mm/damon/core.c +174 -14

268 lines not on this page · raw message

mm/damon/sysfs-schemes.c +12 -0
index 5186966dafb3..aade681b4868 100644
@@ -1084,6 +1084,14 @@ struct damos_sysfs_qgoal_metric_name damos_sysfs_qgoal_metric_names[] = {
 		.metric = DAMOS_QUOTA_INACTIVE_MEM_BP,
 		.name = "inactive_mem_bp",
 	},
+	{
+		.metric = DAMOS_QUOTA_NODE_ELIGIBLE_MEM_BP,
+		.name = "node_eligible_mem_bp",
+	},
+	{
+		.metric = DAMOS_QUOTA_NODE_INELIGIBLE_MEM_BP,
+		.name = "node_ineligible_mem_bp",
+	},
 };
 
 static ssize_t target_metric_show(struct kobject *kobj,
@@ -2655,6 +2663,10 @@ static int damos_sysfs_add_quota_score(
 		case DAMOS_QUOTA_NODE_MEM_FREE_BP:
 			goal->nid = sysfs_goal->nid;
 			break;
+		case DAMOS_QUOTA_NODE_ELIGIBLE_MEM_BP:
+		case DAMOS_QUOTA_NODE_INELIGIBLE_MEM_BP:
+			goal->nid = sysfs_goal->nid;
+			break;
 		case DAMOS_QUOTA_NODE_MEMCG_USED_BP:
 		case DAMOS_QUOTA_NODE_MEMCG_FREE_BP:
 			err = damon_sysfs_memcg_path_to_id(
Signature · 1 line
2.43.0
maintainer
#2 lore raw

Re: (sashiko review) [PATCH v6 1/1] mm/damon: add node_eligible_mem_bp and node_ineligible_mem_bp goal metrics

Ravi, thank you for reposting this patch after the rebase. This time sashiko was able to review this, and found good points including things that deserve another revision of this patch.

Forwarding full sashiko review in a reply format with my inline comments below, for sharing details of my view and doing followup discussions via mails. Ravi, could you please reply?

Quoted 16 lines
# review url: https://sashiko.dev/#/patchset/[email protected]
# start of sashiko.dev inline review
commit 5f6ea8da06d136b84d97827777acf2b07047d42c
Author: Ravi Jonnalagadda <[email protected]>
mm/damon: add node_eligible_mem_bp and node_ineligible_mem_bp goal metrics

This commit adds quota goal metrics for tracking scheme-eligible and ineligible memory distribution across NUMA nodes. It iterates over scheme-eligible regions and uses damon_get_folio() to determine placement.

Quoted 3 lines
mm/damon/core.c +0 -0

[ ... ]

Quoted 12 lines

mm/damon/core.c

Quoted 7 lines
mm/damon/core.c +7 -0
+static unsigned long damos_calc_eligible_bytes(struct damon_ctx *c,
+		struct damos *s, int nid, unsigned long *total)
+{
+	struct damon_target *t;
+	struct damon_region *r;
+	unsigned long total_eligible = 0;
+	unsigned long node_eligible = 0;

Can total_eligible and node_eligible overflow on 32-bit architectures? Since these accumulators track raw physical bytes, regions spanning multiple gigabytes might wrap around the 32-bit unsigned long limit. Would it be safer to use u64 or track the counts in pages instead?

Makes sense. Ravi, could we update these to use phys_addr_t?

Quoted 13 lines

mm/damon/core.c

Quoted 8 lines
mm/damon/core.c +7 -0
+
+	damon_for_each_target(t, c) {
+		damon_for_each_region(r, t) {
+			phys_addr_t addr, end_addr;
+
+			if (!__damos_valid_target(r, s))
+				continue;

Should this loop also check damos_core_filter_out()? By only checking __damos_valid_target(), it seems memory explicitly excluded by user-defined region filters (like target ID or memcg limits) is still counted as eligible, which might inflate the total and affect the quota tuning loop.

No, we intentionally defined "eligible" based on only the access pattern. We may be able to extend this to apply filters in future, if we find a use case. But, for now, I'd suggest keeping this as is for now.

Quoted 10 lines

mm/damon/core.c

Quoted 6 lines
mm/damon/core.c +5 -0
+
+			/* Convert from core address units to physical bytes */
+			addr = r->ar.start * c->addr_unit;
+			end_addr = r->ar.end * c->addr_unit;
+			while (addr < end_addr) {

Could this unbounded loop cause CPU soft lockups? Since DAMON regions can span hundreds of gigabytes, executing millions of folio lookups sequentially without a cond_resched() could monopolize the CPU.

Good point. Most DAMOS action implementations are also doing cond_resched() per region. Ravi, could we add cond_resched() per region to this function, too?

Quoted 11 lines

mm/damon/core.c

Quoted 5 lines
mm/damon/core.c +4 -0
+				struct folio *folio;
+				unsigned long folio_sz, counted;
+
+				folio = damon_get_folio(PHYS_PFN(addr));

What happens if this metric is assigned to a DAMON context configured for virtual address space monitoring? If the context uses DAMON_OPS_VADDR, passing a user-space virtual address to PHYS_PFN() might cause invalid memory accesses or out-of-bounds page struct reads. Should this code explicitly verify the operations type first?

Good finding. We intend to support only paddr ops. But there is no guard for using this on vaddr ops configuration. Ravi, could we add underlying ops check? I think damon_commit_ctx() is a good place to add that. The check could be something like below?

'''

mm/damon/core.c +13 -0
@@ -1515,10 +1515,23 @@ static int damon_commit_sample_control(
 int damon_commit_ctx(struct damon_ctx *dst, struct damon_ctx *src)
 {
        int err;
+       struct damos *scheme;
+       struct damos_quota_goal *goal;
 
        dst->maybe_corrupted = true;
        if (!is_power_of_2(src->min_region_sz))
                return -EINVAL;
+       if (src->ops.id != DAMON_OPS_PADDR) {
+               damon_for_each_scheme(scheme, src) {
+                       damos_for_each_quota_goal(goal, &scheme->quota) {
+                               switch (goal->metric) {
+                               case DAMOS_QUOTA_NODE_ELIGIBLE_MEM_BP:
+                               case DAMOS_QUOTA_NODE_INELIGIBLE_MEMPBP:
+                                       return -EINVAL;
+                               }
+                       }
+               }
+       }
 
        err = damon_commit_schemes(dst, src);
        if (err)

'''

Quoted 19 lines

mm/damon/core.c

Quoted 13 lines
mm/damon/core.c +12 -0
+				if (!folio) {
+					addr += PAGE_SIZE;
+					continue;
+				}
+
+				folio_sz = folio_size(folio);
+				/*
+				 * Clip to region boundaries to avoid counting
+				 * bytes outside the region when folio spans
+				 * region boundaries.
+				 */
+				counted = min(folio_sz, (unsigned long)(end_addr - addr));

Does this accurately calculate the remaining bytes if the region starts at an unaligned offset within a large folio? folio_size() returns the full size of the folio, so this calculation might overestimate the eligible bytes by not accounting for the initial offset within the folio.

Quoted 9 lines

mm/damon/core.c

Quoted 5 lines
mm/damon/core.c +5 -0
+				total_eligible += counted;
+				if (folio_nid(folio) == nid)
+					node_eligible += counted;
+
+				addr += folio_sz;

Can this pointer advancement overshoot the end of the current folio? If addr was unaligned, adding the full folio_sz will skip past the beginning of the next contiguous physical memory folio.

Above two points make sense to me. Ravi, could we fix these?

Quoted 28 lines
Quoted 9 lines
mm/damon/core.c +8 -0
+				folio_put(folio);
+			}
+		}
+	}
+
+	*total = total_eligible;
+	return node_eligible;
+}

[ ... ]

Quoted 16 lines
mm/damon/core.c +16 -0
+static unsigned long damos_get_node_ineligible_mem_bp(struct damon_ctx *c,
+		struct damos *s, int nid)
+{
+	unsigned long total_eligible = 0;
+	unsigned long node_eligible;
+
+	if (nid < 0 || nid >= MAX_NUMNODES || !node_online(nid))
+		return 0;
+
+	node_eligible = damos_calc_eligible_bytes(c, s, nid, &total_eligible);
+
+	/* No eligible memory anywhere - ratio is undefined, return 0 */
+	if (!total_eligible)
+		return 0;
+
+	/* Compute ineligible ratio directly: 10000 - eligible_bp */
Quoted 8 lines

mm/damon/core.c

Quoted 2 lines
mm/damon/core.c +2 -0
+	return 10000 - mult_frac(node_eligible, 10000, total_eligible);
+}

Does this return value match the documented metric? The formula computes the percentage of the system's eligible memory located on other NUMA nodes, rather than the amount of actual ineligible (filtered out) memory residing on the target node. Could this semantic mismatch cause confusion when configuring quota policies?

Nice catch. The name and the documentation are confusing. We actually confused a few times in previous revisions, and I'm again confused now. IIUC, the current implementation is the intended and right one for the given use case, though. If my understanding is correct, how about renaming DAMOS_QUOTA_NODE_INELIGIBLE_MEM_BP to DAMOS_QUOTA_NODE_ELIGIBLE_MEM_BP_COMPLEMENT, and updating the documentation together? Ravi, what do you think?

Quoted 4 lines

# end of sashiko.dev inline review
# review url: https://sashiko.dev/#/patchset/[email protected]

Thanks,
SJ

# hkml [1] generated a draft of this mail.  You can regenerate
# this using below command:
#
#     hkml patch sashiko_dev --for_forwarding \
#             [email protected]
#
#3 lore raw

Re: (sashiko review) [PATCH v6 1/1] mm/damon: add node_eligible_mem_bp and node_ineligible_mem_bp goal metrics

Quoted 10 lines · SeongJae Park

Ravi, thank you for reposting this patch after the rebase. This time sashiko was able to review this, and found good points including things that deserve another revision of this patch.

Forwarding full sashiko review in a reply format with my inline comments below, for sharing details of my view and doing followup discussions via mails. Ravi, could you please reply?

Thanks SJ, providing your comments on top of sashiko's review is very helpful.

Quoted 29 lines
Quoted 16 lines
# review url: https://sashiko.dev/#/patchset/[email protected]
# start of sashiko.dev inline review
commit 5f6ea8da06d136b84d97827777acf2b07047d42c
Author: Ravi Jonnalagadda <[email protected]>
mm/damon: add node_eligible_mem_bp and node_ineligible_mem_bp goal metrics

This commit adds quota goal metrics for tracking scheme-eligible and ineligible memory distribution across NUMA nodes. It iterates over scheme-eligible regions and uses damon_get_folio() to determine placement.

Quoted 3 lines
mm/damon/core.c +0 -0

[ ... ]

Quoted 12 lines

mm/damon/core.c

Quoted 7 lines
mm/damon/core.c +7 -0
+static unsigned long damos_calc_eligible_bytes(struct damon_ctx *c,
+           struct damos *s, int nid, unsigned long *total)
+{
+   struct damon_target *t;
+   struct damon_region *r;
+   unsigned long total_eligible = 0;
+   unsigned long node_eligible = 0;

Can total_eligible and node_eligible overflow on 32-bit architectures? Since these accumulators track raw physical bytes, regions spanning multiple gigabytes might wrap around the 32-bit unsigned long limit. Would it be safer to use u64 or track the counts in pages instead?

Quoted 1 line

Makes sense. Ravi, could we update these to use phys_addr_t?

Agreed. I'll update total_eligible, node_eligible, and the function return type to use phys_addr_t throughout damos_calc_eligible_bytes().

Quoted 15 lines
Quoted 14 lines

mm/damon/core.c

Quoted 9 lines
mm/damon/core.c +7 -0
+
+   damon_for_each_target(t, c) {
+           damon_for_each_region(r, t) {
+                   phys_addr_t addr, end_addr;
+
+                   if (!__damos_valid_target(r, s))
+                           continue;

Should this loop also check damos_core_filter_out()? By only checking __damos_valid_target(), it seems memory explicitly excluded by user-defined region filters (like target ID or memcg limits) is still counted as eligible, which might inflate the total and affect the quota tuning loop.

Quoted 4 lines

No, we intentionally defined "eligible" based on only the access pattern. We may be able to extend this to apply filters in future, if we find a use case. But, for now, I'd suggest keeping this as is for now.

Got it. Will keep it as is.

Quoted 7 lines
Quoted 7 lines
Quoted 6 lines
mm/damon/core.c +5 -0
+
+                   /* Convert from core address units to physical bytes */
+                   addr = r->ar.start * c->addr_unit;
+                   end_addr = r->ar.end * c->addr_unit;
+                   while (addr < end_addr) {
Quoted 7 lines
Quoted 3 lines

Could this unbounded loop cause CPU soft lockups? Since DAMON regions can span hundreds of gigabytes, executing millions of folio lookups sequentially without a cond_resched() could monopolize the CPU.

Good point. Most DAMOS action implementations are also doing cond_resched() per region. Ravi, could we add cond_resched() per region to this function, too?

Will add cond_resched() at the end of each region's processing.

Quoted 13 lines
Quoted 7 lines

mm/damon/core.c

Quoted 6 lines
mm/damon/core.c +4 -0
+                           struct folio *folio;
+                           unsigned long folio_sz, counted;
+
+                           folio = damon_get_folio(PHYS_PFN(addr));
Quoted 5 lines

What happens if this metric is assigned to a DAMON context configured for virtual address space monitoring? If the context uses DAMON_OPS_VADDR, passing a user-space virtual address to PHYS_PFN() might cause invalid memory accesses or out-of-bounds page struct reads. Should this code explicitly verify the operations type first?

Quoted 5 lines

Good finding. We intend to support only paddr ops. But there is no guard for using this on vaddr ops configuration. Ravi, could we add underlying ops check? I think damon_commit_ctx() is a good place to add that. The check could be something like below?

I plan to add the ops type check directly in the metric functions (damos_get_node_eligible_mem_bp and its counterpart) rather than in damon_commit_ctx(). The functions will return 0 early
if c->ops.id != DAMON_OPS_PADDR.

That said, if you prefer the damon_commit_ctx() validation approach to reject the configuration outright, I can implement it that way instead. Please let me know your preference.

Quoted 54 lines

'''

mm/damon/core.c +13 -0
@@ -1515,10 +1515,23 @@ static int damon_commit_sample_control(
 int damon_commit_ctx(struct damon_ctx *dst, struct damon_ctx *src)
 {
        int err;
+       struct damos *scheme;
+       struct damos_quota_goal *goal;
 
        dst->maybe_corrupted = true;
        if (!is_power_of_2(src->min_region_sz))
                return -EINVAL;
+       if (src->ops.id != DAMON_OPS_PADDR) {
+               damon_for_each_scheme(scheme, src) {
+                       damos_for_each_quota_goal(goal, &scheme->quota) {
+                               switch (goal->metric) {
+                               case DAMOS_QUOTA_NODE_ELIGIBLE_MEM_BP:
+                               case DAMOS_QUOTA_NODE_INELIGIBLE_MEMPBP:
+                                       return -EINVAL;
+                               }
+                       }
+               }
+       }
 
        err = damon_commit_schemes(dst, src);
        if (err)

'''

Quoted 25 lines

mm/damon/core.c

Quoted 13 lines
mm/damon/core.c +12 -0
+                           if (!folio) {
+                                   addr += PAGE_SIZE;
+                                   continue;
+                           }
+
+                           folio_sz = folio_size(folio);
+                           /*
+                            * Clip to region boundaries to avoid counting
+                            * bytes outside the region when folio spans
+                            * region boundaries.
+                            */
+                           counted = min(folio_sz, (unsigned long)(end_addr - addr));

Does this accurately calculate the remaining bytes if the region starts at an unaligned offset within a large folio? folio_size() returns the full size of the folio, so this calculation might overestimate the eligible bytes by not accounting for the initial offset within the folio.

Quoted 5 lines
mm/damon/core.c +5 -0
+                           total_eligible += counted;
+                           if (folio_nid(folio) == nid)
+                                   node_eligible += counted;
+
+                           addr += folio_sz;
Quoted 6 lines
Quoted 3 lines

Can this pointer advancement overshoot the end of the current folio? If addr was unaligned, adding the full folio_sz will skip past the beginning of the next contiguous physical memory folio.

Above two points make sense to me. Ravi, could we fix these?

Will fix it. Will calculate the exact overlap between the region [addr, end_addr) and the folio's actual range.

Quoted 37 lines
Quoted 28 lines
Quoted 9 lines
mm/damon/core.c +8 -0
+                           folio_put(folio);
+                   }
+           }
+   }
+
+   *total = total_eligible;
+   return node_eligible;
+}

[ ... ]

Quoted 16 lines
mm/damon/core.c +16 -0
+static unsigned long damos_get_node_ineligible_mem_bp(struct damon_ctx *c,
+           struct damos *s, int nid)
+{
+   unsigned long total_eligible = 0;
+   unsigned long node_eligible;
+
+   if (nid < 0 || nid >= MAX_NUMNODES || !node_online(nid))
+           return 0;
+
+   node_eligible = damos_calc_eligible_bytes(c, s, nid, &total_eligible);
+
+   /* No eligible memory anywhere - ratio is undefined, return 0 */
+   if (!total_eligible)
+           return 0;
+
+   /* Compute ineligible ratio directly: 10000 - eligible_bp */
Quoted 8 lines

mm/damon/core.c

Quoted 2 lines
mm/damon/core.c +2 -0
+   return 10000 - mult_frac(node_eligible, 10000, total_eligible);
+}

Does this return value match the documented metric? The formula computes the percentage of the system's eligible memory located on other NUMA nodes, rather than the amount of actual ineligible (filtered out) memory residing on the target node. Could this semantic mismatch cause confusion when configuring quota policies?

Quoted 8 lines

Nice catch. The name and the documentation are confusing. We actually confused a few times in previous revisions, and I'm again confused now. IIUC, the current implementation is the intended and right one for the given use case, though. If my understanding is correct, how about renaming DAMOS_QUOTA_NODE_INELIGIBLE_MEM_BP to DAMOS_QUOTA_NODE_ELIGIBLE_MEM_BP_COMPLEMENT, and updating the documentation together? Ravi, what do you think?

Agreed, the current name is confusing. How about
DAMOS_QUOTA_NODE_ELIGIBLE_MEM_BP_OFFNODE?

The rationale is that this metric measures "eligible memory that is off this node" (i.e., on other nodes).

I think "offnode" conveys the physical meaning more directly than "complement".
That said, I'm happy to go with "complement" if you prefer.
both are clearer than "ineligible".

Quoted 9 lines
Quoted 4 lines

# end of sashiko.dev inline review
# review url: https://sashiko.dev/#/patchset/[email protected]

Thanks,
SJ

Best Regards,
Ravi.

Quoted 6 lines
# hkml [1] generated a draft of this mail.  You can regenerate
# this using below command:
#
#     hkml patch sashiko_dev --for_forwarding \
#             [email protected]
#
maintainer
#4 lore raw

Re: (sashiko review) [PATCH v6 1/1] mm/damon: add node_eligible_mem_bp and node_ineligible_mem_bp goal metrics

Quoted 7 lines · On Mon, 6 Apr 2026 12:47:56 -0700 Ravi Jonnalagadda <[email protected]>…
Quoted 6 lines · SeongJae Park

Ravi, thank you for reposting this patch after the rebase. This time sashiko was able to review this, and found good points including things that deserve another revision of this patch.

Quoted 6 lines
Quoted 4 lines

Forwarding full sashiko review in a reply format with my inline comments below, for sharing details of my view and doing followup discussions via mails. Ravi, could you please reply?

Thanks SJ, providing your comments on top of sashiko's review is very helpful.

I'm glad to hear that it is working for you :)

[...]

Quoted 3 lines
Quoted 3 lines
Quoted 3 lines

+static unsigned long damos_calc_eligible_bytes(struct damon_ctx *c,

Quoted 2 lines
(quoted diff) +2 -0
+           struct damos *s, int nid, unsigned long *total)
+{

[...]

Quoted 17 lines
Quoted 11 lines
Quoted 10 lines

Review

Quoted 4 lines
(quoted diff) +4 -0
+                           struct folio *folio;
+                           unsigned long folio_sz, counted;
+
+                           folio = damon_get_folio(PHYS_PFN(addr));

What happens if this metric is assigned to a DAMON context configured for virtual address space monitoring? If the context uses DAMON_OPS_VADDR, passing a user-space virtual address to PHYS_PFN() might cause invalid memory accesses or out-of-bounds page struct reads. Should this code explicitly verify the operations type first?

Quoted 5 lines

Good finding. We intend to support only paddr ops. But there is no guard for using this on vaddr ops configuration. Ravi, could we add underlying ops check? I think damon_commit_ctx() is a good place to add that. The check could be something like below?

Quoted 8 lines

I plan to add the ops type check directly in the metric functions (damos_get_node_eligible_mem_bp and its counterpart) rather than in damon_commit_ctx(). The functions will return 0 early
if c->ops.id != DAMON_OPS_PADDR.

That said, if you prefer the damon_commit_ctx() validation approach to reject the configuration outright, I can implement it that way instead. Please let me know your preference.

I'd prefer damon_commit_ctx() validation approach since it would give users more clear message of the failure.

Quoted 29 lines
Quoted 29 lines

'''

mm/damon/core.c +13 -0
@@ -1515,10 +1515,23 @@ static int damon_commit_sample_control(
 int damon_commit_ctx(struct damon_ctx *dst, struct damon_ctx *src)
 {
        int err;
+       struct damos *scheme;
+       struct damos_quota_goal *goal;
 
        dst->maybe_corrupted = true;
        if (!is_power_of_2(src->min_region_sz))
                return -EINVAL;
+       if (src->ops.id != DAMON_OPS_PADDR) {
+               damon_for_each_scheme(scheme, src) {
+                       damos_for_each_quota_goal(goal, &scheme->quota) {
+                               switch (goal->metric) {
+                               case DAMOS_QUOTA_NODE_ELIGIBLE_MEM_BP:
+                               case DAMOS_QUOTA_NODE_INELIGIBLE_MEMPBP:
+                                       return -EINVAL;
+                               }
+                       }
+               }
+       }
 
        err = damon_commit_schemes(dst, src);
        if (err)

'''

[...]

Quoted 22 lines
Quoted 10 lines
Quoted 9 lines

Review

Quoted 3 lines
(quoted diff) +3 -0
+   /* Compute ineligible ratio directly: 10000 - eligible_bp */
+   return 10000 - mult_frac(node_eligible, 10000, total_eligible);
+}

Does this return value match the documented metric? The formula computes the percentage of the system's eligible memory located on other NUMA nodes, rather than the amount of actual ineligible (filtered out) memory residing on the target node. Could this semantic mismatch cause confusion when configuring quota policies?

Quoted 8 lines

Nice catch. The name and the documentation are confusing. We actually confused a few times in previous revisions, and I'm again confused now. IIUC, the current implementation is the intended and right one for the given use case, though. If my understanding is correct, how about renaming DAMOS_QUOTA_NODE_INELIGIBLE_MEM_BP to DAMOS_QUOTA_NODE_ELIGIBLE_MEM_BP_COMPLEMENT, and updating the documentation together? Ravi, what do you think?

Agreed, the current name is confusing. How about
DAMOS_QUOTA_NODE_ELIGIBLE_MEM_BP_OFFNODE?

Quoted 6 lines

The rationale is that this metric measures "eligible memory that is off this node" (i.e., on other nodes).

I think "offnode" conveys the physical meaning more directly than "complement".
That said, I'm happy to go with "complement" if you prefer.
both are clearer than "ineligible".

Thank you for the nice suggestion. I like "offnode" term. But I think having "node" twice on the name is not really efficient for people who print code on papers. What about DAMOS_QUOTA_OFFNODE_ELIGIBLE_MEM_BP?

But... Maybe more importantly... Now I realize this means that offnode_eligible_mem_bp with target nid 0 is just same to node_eligible_mem_bp with target nid 1, on your test setup. Maybe we don't really need offnode_eligible_mem_bp? That is, your test setup could be like below.

'''
For maintaining hot memory on DRAM (node 0) and CXL (node 1) in a 7:3 ratio:

    PUSH scheme: migrate_hot from node 0 -> node 1
      goal: node_eligible_mem_bp, nid=1, target=3000
      "Move hot pages from DRAM to CXL if less thatn 30% of hot data is
       in CXL"

    PULL scheme: migrate_hot from node 1 -> node 0
      goal: node_eligible_mem_bp, nid=0, target=7000
      "Move hot pages from CXL to DRAM if less than 70% of hot data is
       in DRAM"

'''

And the schemes are more easy to read and understand for me. This seems even straightforward to scale for >2 nodes. For example, if we want hot memory distribution of 5:3:2 to nodes 0:1:2,

Two schemes for migrating hot pages out of node 0
- migrate_hot from node 0 -> node 1
- goal: node_eligible_mem_bp, nid=1, target=3000
- migrate_hot from node 0 -> node 2
- goal: node_eligible_mem_bp, nid=2, target=2000

Two schemes for migrating hot pages out of node 1
- migrate_hot from node 1 -> node 0
- goal: node_eligible_mem_bp, nid=0, target=5000
- migrate_hot from node 1 -> node 2
- goal: node_eligible_mem_bp, nid=2, target=2000

Two schemes for migrating hot pages out of node 2
- migrate_hot from node 2 -> node 0
- goal: node_eligible_mem_bp, nid=0, target=5000
- migrate_hot from node 2 -> node 1
- goal: node_eligible_mem_bp, nid=1, target=3000

Do you think this makes sense? If it makes sense and works for your use case, what about dropping the offnode goal type?

Thanks,
SJ

[...]

maintainer
#5 lore raw

Re: (sashiko review) [PATCH v6 1/1] mm/damon: add node_eligible_mem_bp and node_ineligible_mem_bp goal metrics

Adding another thought at the end of the mail without cutting the previous unrelated questions, so that Ravi can answer all my questions at once.

Quoted 151 lines · On Mon, 6 Apr 2026 17:13:08 -0700 SeongJae Park <[email protected]> wrote:
Quoted 7 lines · On Mon, 6 Apr 2026 12:47:56 -0700 Ravi Jonnalagadda <[email protected]>…
Quoted 6 lines · SeongJae Park

Ravi, thank you for reposting this patch after the rebase. This time sashiko was able to review this, and found good points including things that deserve another revision of this patch.

Quoted 6 lines
Quoted 4 lines

Forwarding full sashiko review in a reply format with my inline comments below, for sharing details of my view and doing followup discussions via mails. Ravi, could you please reply?

Thanks SJ, providing your comments on top of sashiko's review is very helpful.

I'm glad to hear that it is working for you :)

[...]

Quoted 3 lines
Quoted 3 lines
Quoted 3 lines

+static unsigned long damos_calc_eligible_bytes(struct damon_ctx *c,

Quoted 2 lines
(quoted diff) +2 -0
+           struct damos *s, int nid, unsigned long *total)
+{

[...]

Quoted 17 lines
Quoted 11 lines
Quoted 10 lines

Review

Quoted 4 lines
(quoted diff) +4 -0
+                           struct folio *folio;
+                           unsigned long folio_sz, counted;
+
+                           folio = damon_get_folio(PHYS_PFN(addr));

What happens if this metric is assigned to a DAMON context configured for virtual address space monitoring? If the context uses DAMON_OPS_VADDR, passing a user-space virtual address to PHYS_PFN() might cause invalid memory accesses or out-of-bounds page struct reads. Should this code explicitly verify the operations type first?

Quoted 5 lines

Good finding. We intend to support only paddr ops. But there is no guard for using this on vaddr ops configuration. Ravi, could we add underlying ops check? I think damon_commit_ctx() is a good place to add that. The check could be something like below?

Quoted 8 lines

I plan to add the ops type check directly in the metric functions (damos_get_node_eligible_mem_bp and its counterpart) rather than in damon_commit_ctx(). The functions will return 0 early
if c->ops.id != DAMON_OPS_PADDR.

That said, if you prefer the damon_commit_ctx() validation approach to reject the configuration outright, I can implement it that way instead. Please let me know your preference.

I'd prefer damon_commit_ctx() validation approach since it would give users more clear message of the failure.

Quoted 29 lines
Quoted 29 lines

'''

mm/damon/core.c +13 -0
@@ -1515,10 +1515,23 @@ static int damon_commit_sample_control(
 int damon_commit_ctx(struct damon_ctx *dst, struct damon_ctx *src)
 {
        int err;
+       struct damos *scheme;
+       struct damos_quota_goal *goal;
 
        dst->maybe_corrupted = true;
        if (!is_power_of_2(src->min_region_sz))
                return -EINVAL;
+       if (src->ops.id != DAMON_OPS_PADDR) {
+               damon_for_each_scheme(scheme, src) {
+                       damos_for_each_quota_goal(goal, &scheme->quota) {
+                               switch (goal->metric) {
+                               case DAMOS_QUOTA_NODE_ELIGIBLE_MEM_BP:
+                               case DAMOS_QUOTA_NODE_INELIGIBLE_MEMPBP:
+                                       return -EINVAL;
+                               }
+                       }
+               }
+       }
 
        err = damon_commit_schemes(dst, src);
        if (err)

'''

[...]

Quoted 22 lines
Quoted 10 lines
Quoted 9 lines

Review

Quoted 3 lines
(quoted diff) +3 -0
+   /* Compute ineligible ratio directly: 10000 - eligible_bp */
+   return 10000 - mult_frac(node_eligible, 10000, total_eligible);
+}

Does this return value match the documented metric? The formula computes the percentage of the system's eligible memory located on other NUMA nodes, rather than the amount of actual ineligible (filtered out) memory residing on the target node. Could this semantic mismatch cause confusion when configuring quota policies?

Quoted 8 lines

Nice catch. The name and the documentation are confusing. We actually confused a few times in previous revisions, and I'm again confused now. IIUC, the current implementation is the intended and right one for the given use case, though. If my understanding is correct, how about renaming DAMOS_QUOTA_NODE_INELIGIBLE_MEM_BP to DAMOS_QUOTA_NODE_ELIGIBLE_MEM_BP_COMPLEMENT, and updating the documentation together? Ravi, what do you think?

Agreed, the current name is confusing. How about
DAMOS_QUOTA_NODE_ELIGIBLE_MEM_BP_OFFNODE?

Quoted 6 lines

The rationale is that this metric measures "eligible memory that is off this node" (i.e., on other nodes).

I think "offnode" conveys the physical meaning more directly than "complement".
That said, I'm happy to go with "complement" if you prefer.
both are clearer than "ineligible".

Thank you for the nice suggestion. I like "offnode" term. But I think having "node" twice on the name is not really efficient for people who print code on papers. What about DAMOS_QUOTA_OFFNODE_ELIGIBLE_MEM_BP?

But... Maybe more importantly... Now I realize this means that offnode_eligible_mem_bp with target nid 0 is just same to node_eligible_mem_bp with target nid 1, on your test setup. Maybe we don't really need offnode_eligible_mem_bp? That is, your test setup could be like below.

'''
For maintaining hot memory on DRAM (node 0) and CXL (node 1) in a 7:3 ratio:

    PUSH scheme: migrate_hot from node 0 -> node 1
      goal: node_eligible_mem_bp, nid=1, target=3000
      "Move hot pages from DRAM to CXL if less thatn 30% of hot data is
       in CXL"

    PULL scheme: migrate_hot from node 1 -> node 0
      goal: node_eligible_mem_bp, nid=0, target=7000
      "Move hot pages from CXL to DRAM if less than 70% of hot data is
       in DRAM"

'''

And the schemes are more easy to read and understand for me. This seems even straightforward to scale for >2 nodes. For example, if we want hot memory distribution of 5:3:2 to nodes 0:1:2,

Two schemes for migrating hot pages out of node 0
- migrate_hot from node 0 -> node 1
- goal: node_eligible_mem_bp, nid=1, target=3000
- migrate_hot from node 0 -> node 2
- goal: node_eligible_mem_bp, nid=2, target=2000

Two schemes for migrating hot pages out of node 1
- migrate_hot from node 1 -> node 0
- goal: node_eligible_mem_bp, nid=0, target=5000
- migrate_hot from node 1 -> node 2
- goal: node_eligible_mem_bp, nid=2, target=2000

Quoted 8 lines

Two schemes for migrating hot pages out of node 2
- migrate_hot from node 2 -> node 0
- goal: node_eligible_mem_bp, nid=0, target=5000
- migrate_hot from node 2 -> node 1
- goal: node_eligible_mem_bp, nid=1, target=3000

Do you think this makes sense? If it makes sense and works for your use case, what about dropping the offnode goal type?

Now I recall I suggested the offnode metric because I suggested to run a kdamond per node. That is, having one kdamond that monitors only node 0 and migrate hot memory to node 1, and another kdamond that monitors only node 1 and migrate hot memory to node 0. And I suggested to do so because I knew it is suboptimal to run DAMOS schemes with node filter.

We made a change [1] for making that more optimum, though. The change is now in mm-stable, so hopefully it will be available from 7.1-rc1. So I believe the single quota goal metric should work now. Ravi, could you share what you think?

[1] commit e1ace69c33ec ("mm/damon/core: set quota-score histogram with core filters")

Thanks,
SJ

[...]

#6 lore raw

Re: (sashiko review) [PATCH v6 1/1] mm/damon: add node_eligible_mem_bp and node_ineligible_mem_bp goal metrics

Quoted 172 lines · SeongJae Park

Adding another thought at the end of the mail without cutting the previous unrelated questions, so that Ravi can answer all my questions at once.

Quoted 151 lines · On Mon, 6 Apr 2026 17:13:08 -0700 SeongJae Park <[email protected]> wrote:
Quoted 7 lines · On Mon, 6 Apr 2026 12:47:56 -0700 Ravi Jonnalagadda <[email protected]>…
Quoted 6 lines · SeongJae Park

Ravi, thank you for reposting this patch after the rebase. This time sashiko was able to review this, and found good points including things that deserve another revision of this patch.

Quoted 6 lines
Quoted 4 lines

Forwarding full sashiko review in a reply format with my inline comments below, for sharing details of my view and doing followup discussions via mails. Ravi, could you please reply?

Thanks SJ, providing your comments on top of sashiko's review is very helpful.

I'm glad to hear that it is working for you :)

[...]

Quoted 3 lines
Quoted 3 lines
Quoted 3 lines

+static unsigned long damos_calc_eligible_bytes(struct damon_ctx *c,

Quoted 2 lines
(quoted diff) +2 -0
+           struct damos *s, int nid, unsigned long *total)
+{

[...]

Quoted 17 lines
Quoted 11 lines
Quoted 10 lines

Review

Quoted 4 lines
(quoted diff) +4 -0
+                           struct folio *folio;
+                           unsigned long folio_sz, counted;
+
+                           folio = damon_get_folio(PHYS_PFN(addr));

What happens if this metric is assigned to a DAMON context configured for virtual address space monitoring? If the context uses DAMON_OPS_VADDR, passing a user-space virtual address to PHYS_PFN() might cause invalid memory accesses or out-of-bounds page struct reads. Should this code explicitly verify the operations type first?

Quoted 5 lines

Good finding. We intend to support only paddr ops. But there is no guard for using this on vaddr ops configuration. Ravi, could we add underlying ops check? I think damon_commit_ctx() is a good place to add that. The check could be something like below?

Quoted 8 lines

I plan to add the ops type check directly in the metric functions (damos_get_node_eligible_mem_bp and its counterpart) rather than in damon_commit_ctx(). The functions will return 0 early
if c->ops.id != DAMON_OPS_PADDR.

That said, if you prefer the damon_commit_ctx() validation approach to reject the configuration outright, I can implement it that way instead. Please let me know your preference.

I'd prefer damon_commit_ctx() validation approach since it would give users more clear message of the failure.

Quoted 29 lines
Quoted 29 lines

'''

mm/damon/core.c +13 -0
@@ -1515,10 +1515,23 @@ static int damon_commit_sample_control(
 int damon_commit_ctx(struct damon_ctx *dst, struct damon_ctx *src)
 {
        int err;
+       struct damos *scheme;
+       struct damos_quota_goal *goal;
 
        dst->maybe_corrupted = true;
        if (!is_power_of_2(src->min_region_sz))
                return -EINVAL;
+       if (src->ops.id != DAMON_OPS_PADDR) {
+               damon_for_each_scheme(scheme, src) {
+                       damos_for_each_quota_goal(goal, &scheme->quota) {
+                               switch (goal->metric) {
+                               case DAMOS_QUOTA_NODE_ELIGIBLE_MEM_BP:
+                               case DAMOS_QUOTA_NODE_INELIGIBLE_MEMPBP:
+                                       return -EINVAL;
+                               }
+                       }
+               }
+       }
 
        err = damon_commit_schemes(dst, src);
        if (err)

'''

[...]

Quoted 22 lines
Quoted 10 lines
Quoted 9 lines

Review

Quoted 3 lines
(quoted diff) +3 -0
+   /* Compute ineligible ratio directly: 10000 - eligible_bp */
+   return 10000 - mult_frac(node_eligible, 10000, total_eligible);
+}

Does this return value match the documented metric? The formula computes the percentage of the system's eligible memory located on other NUMA nodes, rather than the amount of actual ineligible (filtered out) memory residing on the target node. Could this semantic mismatch cause confusion when configuring quota policies?

Quoted 8 lines

Nice catch. The name and the documentation are confusing. We actually confused a few times in previous revisions, and I'm again confused now. IIUC, the current implementation is the intended and right one for the given use case, though. If my understanding is correct, how about renaming DAMOS_QUOTA_NODE_INELIGIBLE_MEM_BP to DAMOS_QUOTA_NODE_ELIGIBLE_MEM_BP_COMPLEMENT, and updating the documentation together? Ravi, what do you think?

Agreed, the current name is confusing. How about
DAMOS_QUOTA_NODE_ELIGIBLE_MEM_BP_OFFNODE?

Quoted 6 lines

The rationale is that this metric measures "eligible memory that is off this node" (i.e., on other nodes).

I think "offnode" conveys the physical meaning more directly than "complement".
That said, I'm happy to go with "complement" if you prefer.
both are clearer than "ineligible".

Thank you for the nice suggestion. I like "offnode" term. But I think having "node" twice on the name is not really efficient for people who print code on papers. What about DAMOS_QUOTA_OFFNODE_ELIGIBLE_MEM_BP?

But... Maybe more importantly... Now I realize this means that offnode_eligible_mem_bp with target nid 0 is just same to node_eligible_mem_bp with target nid 1, on your test setup. Maybe we don't really need offnode_eligible_mem_bp? That is, your test setup could be like below.

'''
For maintaining hot memory on DRAM (node 0) and CXL (node 1) in a 7:3 ratio:

    PUSH scheme: migrate_hot from node 0 -> node 1
      goal: node_eligible_mem_bp, nid=1, target=3000
      "Move hot pages from DRAM to CXL if less thatn 30% of hot data is
       in CXL"

    PULL scheme: migrate_hot from node 1 -> node 0
      goal: node_eligible_mem_bp, nid=0, target=7000
      "Move hot pages from CXL to DRAM if less than 70% of hot data is
       in DRAM"

'''

And the schemes are more easy to read and understand for me. This seems even straightforward to scale for >2 nodes. For example, if we want hot memory distribution of 5:3:2 to nodes 0:1:2,

Two schemes for migrating hot pages out of node 0
- migrate_hot from node 0 -> node 1
- goal: node_eligible_mem_bp, nid=1, target=3000
- migrate_hot from node 0 -> node 2
- goal: node_eligible_mem_bp, nid=2, target=2000

Two schemes for migrating hot pages out of node 1
- migrate_hot from node 1 -> node 0
- goal: node_eligible_mem_bp, nid=0, target=5000
- migrate_hot from node 1 -> node 2
- goal: node_eligible_mem_bp, nid=2, target=2000

Quoted 8 lines

Two schemes for migrating hot pages out of node 2
- migrate_hot from node 2 -> node 0
- goal: node_eligible_mem_bp, nid=0, target=5000
- migrate_hot from node 2 -> node 1
- goal: node_eligible_mem_bp, nid=1, target=3000

Do you think this makes sense? If it makes sense and works for your use case, what about dropping the offnode goal type?

Now I recall I suggested the offnode metric because I suggested to run a kdamond per node. That is, having one kdamond that monitors only node 0 and migrate hot memory to node 1, and another kdamond that monitors only node 1 and migrate hot memory to node 0. And I suggested to do so because I knew it is suboptimal to run DAMOS schemes with node filter.

Quoted 5 lines

We made a change [1] for making that more optimum, though. The change is now in mm-stable, so hopefully it will be available from 7.1-rc1. So I believe the single quota goal metric should work now. Ravi, could you share what you think?

Yes SJ. I think we can make it work with single goal now that the below commit is part of mainline. will give it a try and post an update.

Quoted 7 lines

[1] commit e1ace69c33ec ("mm/damon/core: set quota-score histogram with core filters")

Thanks,
SJ

[...]

maintainer
#7 lore raw

Re: (sashiko review) [PATCH v6 1/1] mm/damon: add node_eligible_mem_bp and node_ineligible_mem_bp goal metrics

Quoted 1 line · On Tue, 7 Apr 2026 19:33:43 -0700 Ravi Jonnalagadda <[email protected]>…

On Tue, Apr 7, 2026 at 9:05 AM SeongJae Park <[email protected]> wrote:

[...]

Quoted 3 lines

Yes SJ. I think we can make it work with single goal now that the below commit is part of mainline. will give it a try and post an update.

Sounds good, please don't hesitate asking any questions.

Thanks,
SJ

[...]

maintainer
#8 lore raw
In reply to Ravi Jonnalagadda

Hello Ravi,

Quoted 5 lines · On Sun, 5 Apr 2026 11:42:46 -0700 Ravi Jonnalagadda <[email protected]>…

Changes since v5:
=================
https://lore.kernel.org/linux-mm/[email protected]/

- Rebased onto mm-new instead of damon/next for sashiko review

Thank you for doing this. sashiko was able to review and find some good points. I shared the full review with my opinions as a reply to the patch 1. Please check and reply.

Quoted 27 lines

- Removed Reported-by/Closes tags per maintainer feedback (not needed for bugs found before merge)

Changes since v4:
=================
https://lore.kernel.org/linux-mm/[email protected]/

- Fixed commit message description for DAMOS_QUOTA_NODE_INELIGIBLE_MEM_BP per review feedback
- Added clarifying comment for ops-common.h include (for damon_get_folio())
- Fixed build error when CONFIG_DAMON_PADDR is disabled by adding #ifdef CONFIG_DAMON_PADDR guards around functions using damon_get_folio()
- Dropped RFC tag per maintainer feedback

This patch is based on top of mm-new.

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

In heterogeneous memory systems, controlling memory distribution across NUMA nodes is essential for performance optimization. This patch enables system-wide page distribution with target-state goals such as "maintain 30% of scheme-eligible memory on CXL" using PA-mode DAMON schemes.

What These Metrics Measure
==========================

Quoted 7 lines

node_eligible_mem_bp:
scheme_eligible_bytes_on_node / total_scheme_eligible_bytes * 10000

node_ineligible_mem_bp:

    (total - scheme_eligible_bytes_on_node) / total * 10000

These metrics are complementary: eligible_bp + ineligible_bp = 10000 bp.

As I mentioned on sashiko review, I now think node_ineligible_mem_bp is a confusing name, and thinking a new name, maybe node_eligible_mem_bp_complement is better. Let's further discuss on sashiko review reply thread.

Quoted 40 lines

Two-Scheme Setup for Hot Page Distribution
==========================================

For maintaining hot memory on DRAM (node 0) and CXL (node 1) in a 7:3 ratio:

    PUSH scheme: migrate_hot from node 0 -> node 1
      goal: node_ineligible_mem_bp, nid=0, target=3000
      "Move hot pages from DRAM to CXL if more than 70% of hot data is
       in DRAM"

    PULL scheme: migrate_hot from node 1 -> node 0
      goal: node_eligible_mem_bp, nid=0, target=7000
      "Move hot pages from CXL to DRAM if less than 70% of hot data is
       in DRAM"

The complementary goals create a feedback loop that converges to the target distribution.

Testing Results
===============

Functionally tested on a two-node heterogeneous memory system with DRAM (node 0) and CXL memory (node 1). A PUSH+PULL scheme configuration using migrate_hot actions was used to reach a target hot memory ratio between the two tiers. Testing used the TEMPORAL goal tuner available in damon/next and mm-unstable.

With the TEMPORAL tuner, the system converges quickly to the target distribution. The tuner drives esz to maximum when under goal and to zero once the goal is met, forming a simple on/off feedback loop that stabilizes at the desired ratio.

With the CONSIST tuner, the scheme still converges but more slowly, as it migrates and then throttles itself based on quota feedback. The time to reach the goal varies depending on workload intensity.

Note: These metrics work with both TEMPORAL and CONSIST goal tuners.

Quoted 8 lines

Ravi Jonnalagadda (1):
mm/damon: add node_eligible_mem_bp and node_ineligible_mem_bp goal
metrics

 include/linux/damon.h    |   6 ++
 mm/damon/core.c          | 188 ++++++++++++++++++++++++++++++++++++---
 mm/damon/sysfs-schemes.c |  12 +++
 3 files changed, 192 insertions(+), 14 deletions(-)

Btw, having a cover letter for single patch looks odd. Why don't you add this cover letter to the patch's commit message?

Thanks,
SJ

[...]

#9 lore raw

Hi SJ,

I will address your comments and the feedback from sashiko's review in the next version of the patch.

Regarding the cover letter, I originally included it to provide context on the general methodology. However, I agree that it is unnecessary for a single patch. I will move the relevant content to the commit message and drop the cover letter in the next iteration.

Thanks,
Ravi Jonnalagadda

Quoted 119 lines · SeongJae Park

Hello Ravi,

Quoted 3 lines · On Sun, 5 Apr 2026 11:42:46 -0700 Ravi Jonnalagadda <…

Changes since v5:
=================

https://lore.kernel.org/linux-mm/[email protected]/

Quoted 2 lines

- Rebased onto mm-new instead of damon/next for sashiko review

Thank you for doing this. sashiko was able to review and find some good points. I shared the full review with my opinions as a reply to the patch 1.
Please check and reply.

Quoted 6 lines

- Removed Reported-by/Closes tags per maintainer feedback (not needed for bugs found before merge)

Changes since v4:
=================

https://lore.kernel.org/linux-mm/[email protected]/

Quoted 4 lines

- Fixed commit message description for DAMOS_QUOTA_NODE_INELIGIBLE_MEM_BP per review feedback
- Added clarifying comment for ops-common.h include (for

damon_get_folio())

Quoted 2 lines

- Fixed build error when CONFIG_DAMON_PADDR is disabled by adding #ifdef CONFIG_DAMON_PADDR guards around functions using

damon_get_folio()

Quoted 15 lines

- Dropped RFC tag per maintainer feedback

This patch is based on top of mm-new.

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

In heterogeneous memory systems, controlling memory distribution across NUMA nodes is essential for performance optimization. This patch enables system-wide page distribution with target-state goals such as "maintain 30% of scheme-eligible memory on CXL" using PA-mode DAMON schemes.

What These Metrics Measure
==========================

Quoted 7 lines

node_eligible_mem_bp:
scheme_eligible_bytes_on_node / total_scheme_eligible_bytes * 10000

node_ineligible_mem_bp:

    (total - scheme_eligible_bytes_on_node) / total * 10000

These metrics are complementary: eligible_bp + ineligible_bp = 10000 bp.

As I mentioned on sashiko review, I now think node_ineligible_mem_bp is a confusing name, and thinking a new name, maybe node_eligible_mem_bp_complement
is better. Let's further discuss on sashiko review reply thread.

Quoted 40 lines

Two-Scheme Setup for Hot Page Distribution
==========================================

For maintaining hot memory on DRAM (node 0) and CXL (node 1) in a 7:3 ratio:

    PUSH scheme: migrate_hot from node 0 -> node 1
      goal: node_ineligible_mem_bp, nid=0, target=3000
      "Move hot pages from DRAM to CXL if more than 70% of hot data is
       in DRAM"

    PULL scheme: migrate_hot from node 1 -> node 0
      goal: node_eligible_mem_bp, nid=0, target=7000
      "Move hot pages from CXL to DRAM if less than 70% of hot data is
       in DRAM"

The complementary goals create a feedback loop that converges to the target distribution.

Testing Results
===============

Functionally tested on a two-node heterogeneous memory system with DRAM (node 0) and CXL memory (node 1). A PUSH+PULL scheme configuration using migrate_hot actions was used to reach a target hot memory ratio between the two tiers. Testing used the TEMPORAL goal tuner available in damon/next and mm-unstable.

With the TEMPORAL tuner, the system converges quickly to the target distribution. The tuner drives esz to maximum when under goal and to zero once the goal is met, forming a simple on/off feedback loop that stabilizes at the desired ratio.

With the CONSIST tuner, the scheme still converges but more slowly, as it migrates and then throttles itself based on quota feedback. The time to reach the goal varies depending on workload intensity.

Note: These metrics work with both TEMPORAL and CONSIST goal tuners.

Quoted 8 lines

Ravi Jonnalagadda (1):
mm/damon: add node_eligible_mem_bp and node_ineligible_mem_bp goal
metrics

 include/linux/damon.h    |   6 ++
 mm/damon/core.c          | 188 ++++++++++++++++++++++++++++++++++++---
 mm/damon/sysfs-schemes.c |  12 +++
 3 files changed, 192 insertions(+), 14 deletions(-)

Btw, having a cover letter for single patch looks odd. Why don't you add this
cover letter to the patch's commit message?

Thanks,
SJ

[...]