Re: [PATCH v8] mm/damon: add node_eligible_mem_bp goal metric

From: Ravi Jonnalagadda

Date: Sun Apr 26 2026 - 17:34:37 EST


On Sun, Apr 26, 2026 at 10:19 AM SeongJae Park <sj@xxxxxxxxxx> wrote:
>
> On Sat, 25 Apr 2026 17:32:45 -0700 Ravi Jonnalagadda <ravis.opensrc@xxxxxxxxx> wrote:
>
> > 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
> > 60% of scheme-eligible memory on DRAM" using PA-mode DAMON schemes.
> >
> > Rather than using absolute thresholds, this metric tracks the ratio of
> > memory that matches each scheme's access pattern filters on a target
> > node, enabling the quota system to automatically adjust migration
> > aggressiveness to maintain the desired distribution.
> >
> > What This Metric Measures
> > =========================
> >
> > node_eligible_mem_bp:
> > scheme_eligible_bytes_on_node / total_scheme_eligible_bytes * 10000
> >
> > Two-Scheme Setup for Hot Page Distribution
> > ==========================================
> >
> > For maintaining 60% of hot memory on DRAM (node 0) and 40% on CXL
> > (node 1):
> >
> > PULL scheme: migrate_hot to node 0
> > goal: node_eligible_mem_bp, nid=0, target=6000
> > addr filter: node 1 address range (only migrate FROM CXL)
> > "Move hot pages to DRAM if less than 60% of hot data is in DRAM"
> >
> > PUSH scheme: migrate_hot to node 1
> > goal: node_eligible_mem_bp, nid=1, target=4000
> > addr filter: node 0 address range (only migrate FROM DRAM)
> > "Move hot pages to CXL if less than 40% of hot data is in CXL"
> >
> > Each scheme independently measures its own eligible memory and adjusts
> > its quota to achieve its target ratio. The schemes work in concert
> > through DAMON's unified monitoring context, with the quota autotuner
> > balancing their relative aggressiveness.
> >
> > Implementation Details
> > ======================
> >
> > The implementation adds a new quota goal metric type
> > DAMOS_QUOTA_NODE_ELIGIBLE_MEM_BP to the existing DAMOS quota goal
> > framework. When this metric is configured for a scheme:
> >
> > 1. During each quota adjustment cycle, damos_get_node_eligible_mem_bp()
> > is called to calculate the current memory distribution.
> >
> > 2. The function iterates through all regions that match the scheme's
> > access pattern (via __damos_valid_target()) and calculates:
> > - Total eligible bytes across all nodes
> > - Eligible bytes specifically on the target node (goal->nid)
> >
> > 3. For each eligible region, damos_calc_eligible_bytes() walks through
> > the physical address range, using damon_get_folio() to look up
> > each folio and determine its NUMA node via folio_nid().
> >
> > 4. Large folios are handled by calculating the exact overlap between
> > the region boundaries and folio boundaries, ensuring accurate
> > byte counts even when regions partially span folios.
> >
> > 5. The ratio (node_eligible / total_eligible * 10000) is returned
> > as basis points, which the quota autotuner uses to adjust the
> > scheme's effective quota size (esz).
> >
> > The implementation requires CONFIG_DAMON_PADDR since damon_get_folio()
> > is only available for physical address space monitoring.
> >
> > 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.
> >
> > 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: This metric works with both TEMPORAL and CONSIST goal tuners.
> >
> > Suggested-by: SeongJae Park <sj@xxxxxxxxxx>
> > Signed-off-by: Ravi Jonnalagadda <ravis.opensrc@xxxxxxxxx>
>
> Assuming below two minor things are addressed,
>
> Reviewed-by: SeongJae Park <sj@xxxxxxxxxx>

Thank you SJ. Will send v9 addressing these two changes.

>
> [...]
> > +static unsigned long damos_get_node_eligible_mem_bp(struct damon_ctx *c,
> > + struct damos *s, int nid)
> > +{
> > + phys_addr_t total_eligible = 0;
> > + phys_addr_t node_eligible;
> > +
> > + if (c->ops.id != DAMON_OPS_PADDR)
> > + return 0;
> > +
> > + if (nid < 0 || nid >= MAX_NUMNODES || !node_online(nid))
> > + return 0;
> > +
> > + node_eligible = damos_calc_eligible_bytes(c, s, nid, &total_eligible);
> > +
> > + if (!total_eligible)
> > + return 0;
> > +
> > + return mult_frac((unsigned long)node_eligible, 10000,
> > + (unsigned long)total_eligible);
>
> Sashiko found [1] total_eligible after the casting could be zero on 32bit
> system, resulting in divide-by-zero. As I also replied to Sashiko review,
> could you please fix this? It seems we can simply remove the castings.
>
> [...]
> > @@ -2389,9 +2528,9 @@ static void damos_goal_tune_esz_bp_temporal(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,
> > - struct damon_ctx *ctx)
> > +static void damos_set_effective_quota(struct damon_ctx *c, struct damos *s)
>
> Sorry for finding this late. Could we keep the dmon_ctx parameter name?
> Otherwise, we introduce unnecessary change below.
>
> If the mult_frac() divide-by-zero is not a real issue, I wouldn't insist this
> change. But, if we will make a new version, let's do this together.
>
> > {
> > + struct damos_quota *quota = &s->quota;
> > unsigned long throughput;
> > unsigned long esz = ULONG_MAX;
> >
> > @@ -2402,9 +2541,9 @@ static void damos_set_effective_quota(struct damos_quota *quota,
> >
> > if (!list_empty(&quota->goals)) {
> > if (quota->goal_tuner == DAMOS_QUOTA_GOAL_TUNER_CONSIST)
> > - damos_goal_tune_esz_bp_consist(quota);
> > + damos_goal_tune_esz_bp_consist(c, s);
> > else if (quota->goal_tuner == DAMOS_QUOTA_GOAL_TUNER_TEMPORAL)
> > - damos_goal_tune_esz_bp_temporal(quota);
> > + damos_goal_tune_esz_bp_temporal(c, s);
> > esz = quota->esz_bp / 10000;
> > }
> >
> > @@ -2415,7 +2554,7 @@ static void damos_set_effective_quota(struct damos_quota *quota,
> > else
> > throughput = PAGE_SIZE * 1024;
> > esz = min(throughput * quota->ms, esz);
> > - esz = max(ctx->min_region_sz, esz);
> > + esz = max(c->min_region_sz, esz);
>
> Above change is unnecessarily introduced. Could we keep the old damon_ctx
> parameter name?
>
> [1] https://lore.kernel.org/20260426005341.B393EC2BCB0@xxxxxxxxxxxxxxx
>
>
> Thanks,
> SJ
>
> [...]