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 rather than by nr_accesses, which stays near zero when access checks are off. 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. Signed-off-by: Ravi Jonnalagadda --- mm/damon/core.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/mm/damon/core.c b/mm/damon/core.c index 1d25f820e3f9..c2c74d0daf95 100644 --- a/mm/damon/core.c +++ b/mm/damon/core.c @@ -4175,12 +4175,36 @@ 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); 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 by this + * target's own 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 rather than by nr_accesses. + * + * 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)