[RFC PATCH v4 8/9] mm/damon/core: cap the region merge threshold per target

From: Ravi Jonnalagadda

Date: Mon Oct 05 2026 - 04:52:21 EST


kdamond_merge_regions() applies one context-wide threshold, the highest
merge score across all targets divided by 10, to every target. When a
high-traffic target and a low-traffic target share a context, the
high-traffic target sets a threshold permissive enough to erase the
hot/cold boundary in the low-traffic one: a region with nr_accesses=1
next to one with nr_accesses=0 merges because 1 <= threshold, even though
that difference is the only signal the cold scheme has to admit it.

On the regular merge pass, cap the threshold for each target by a tenth of
that target's own maximum merge score. The score is the one
damon_merge_regions_of() compares, so a probe-weighted context is capped
by its weighted hit sums.

The passes that follow the regular one exist only to bring the region
count under max_nr_regions by escalating the threshold. They keep using
the escalated threshold uncapped, so that bound still holds.

In a probe-weighted context the merge score is a weighted hit sum, which
can exceed max_thres, the samples-per-aggregation ceiling the escalating
passes stop at, when the probe weights sum above 1. Raise max_thres to
ten times the initial threshold there, so those passes still run.

Signed-off-by: Ravi Jonnalagadda <ravis.opensrc@xxxxxxxxx>
---
mm/damon/core.c | 30 +++++++++++++++++++++++++++++-
1 file changed, 29 insertions(+), 1 deletion(-)

diff --git a/mm/damon/core.c b/mm/damon/core.c
index e3c29d7cff23..7c0df07f0a90 100644
--- a/mm/damon/core.c
+++ b/mm/damon/core.c
@@ -4167,12 +4167,40 @@ static noinline_for_stack void kdamond_merge_regions(struct damon_ctx *c,
unsigned int nr_regions;
unsigned int max_thres;
bool count_age = true;
+ bool use_probe_hits = damon_has_probe_weights(c);

max_thres = damon_nr_samples_per_aggr(&c->attrs);
+ /* weighted scores can exceed max_thres; threshold is max score / 10 */
+ if (use_probe_hits)
+ max_thres = max(threshold * 10, max_thres);
while (true) {
nr_regions = 0;
damon_for_each_target(t, c) {
- damon_merge_regions_of(t, threshold, sz_limit, c,
+ struct damon_region *r;
+ unsigned int t_max = 0, t_thres = threshold;
+
+ /*
+ * On the regular pass, cap the threshold at a tenth of
+ * this target's maximum merge score. A high-traffic
+ * target in the same context must not set a threshold
+ * so permissive that a low-traffic target's hot/cold
+ * boundary merges away before the cold scheme can act
+ * on it. The score is the one damon_merge_regions_of()
+ * compares, so probe-weighted contexts are capped by
+ * their weighted hit sums.
+ *
+ * The passes that follow exist only to bring the region
+ * count under max_nr_regions, so they use the escalated
+ * threshold as is.
+ */
+ if (count_age) {
+ damon_for_each_region(r, t)
+ t_max = max(t_max,
+ damon_merge_score(r, false, c,
+ use_probe_hits));
+ t_thres = min(threshold, t_max / 10);
+ }
+ damon_merge_regions_of(t, t_thres, sz_limit, c,
count_age);
nr_regions += damon_nr_regions(t);
}

--
Git-157)