[PATCH v3 2/2] mm/damon/core: Copy only installed probe in damon_split_region_at()

From: Suhaas Joshi

Date: Mon Oct 12 2026 - 11:16:07 EST


In damon_split_region_at(), we copy the entire probe_hits[] and
last_probe_hits[] arrays of DAMON_MAX_PROBES size. However it is possible
that the number of probes installed is less than DAMON_MAX_PROBES, in which
case one or more iterations is/are redundant. To remedy this, iterate over
only as many times as the number of installed probes. To do so, accept a
struct damon_ctx pointer in damon_split_region_at(), and update all
invocations of the function to pass the new argument.

Also update damon_test_split_at() and damos_test_filter_out() in
core-kunit.h to supply a context to function paths that call
damon_split_region_at().

Signed-off-by: Suhaas Joshi <suhaas@xxxxxxxxxx>
---
mm/damon/core.c | 42 +++++++++++++++------------
mm/damon/tests/core-kunit.h | 58 ++++++++++++++++++++++++++++++-------
2 files changed, 72 insertions(+), 28 deletions(-)

diff --git a/mm/damon/core.c b/mm/damon/core.c
index bebd2889c2c8..2677fabc9c29 100644
--- a/mm/damon/core.c
+++ b/mm/damon/core.c
@@ -2051,8 +2051,8 @@ static unsigned long damon_region_sz_limit(struct damon_ctx *ctx)
return sz;
}

-static int damon_split_region_at(struct damon_target *t,
- struct damon_region *r, unsigned long sz_r);
+static int damon_split_region_at(struct damon_ctx *ctx, struct damon_target *t,
+ struct damon_region *r, unsigned long sz_r);

/*
* damon_apply_min_nr_regions() - Make effect of min_nr_regions parameter.
@@ -2077,7 +2077,7 @@ static unsigned long damon_apply_min_nr_regions(struct damon_ctx *ctx)
damon_for_each_target(t, ctx) {
damon_for_each_region_safe(r, next, t) {
while (damon_sz_region(r) > max_region_sz) {
- if (damon_split_region_at(t, r, max_region_sz))
+ if (damon_split_region_at(ctx, t, r, max_region_sz))
goto out;
r = damon_next_region(r);
}
@@ -2474,6 +2474,7 @@ static bool damos_valid_target(struct damon_ctx *c, struct damon_region *r,
/*
* damos_skip_charged_region() - Check if the given region or starting part of
* it is already charged for the DAMOS quota.
+ * @ctx: The pointer to the context.
* @t: The target of the region.
* @rp: The pointer to the region.
* @s: The scheme to be applied.
@@ -2496,8 +2497,8 @@ static bool damos_valid_target(struct damon_ctx *c, struct damon_region *r,
*
* Return: true if the region should be skipped, false otherwise.
*/
-static bool damos_skip_charged_region(struct damon_target *t,
- struct damon_region *r, struct damos *s,
+static bool damos_skip_charged_region(struct damon_ctx *ctx,
+ struct damon_target *t, struct damon_region *r, struct damos *s,
unsigned long min_region_sz)
{
struct damos_quota *quota = &s->quota;
@@ -2525,7 +2526,7 @@ static bool damos_skip_charged_region(struct damon_target *t,
}
sz_to_skip = min_region_sz;
}
- damon_split_region_at(t, r, sz_to_skip);
+ damon_split_region_at(ctx, t, r, sz_to_skip);
skip = true;
}
}
@@ -2583,12 +2584,12 @@ static bool damos_filter_match(struct damon_ctx *ctx, struct damon_target *t,
}
/* start before the range and overlap */
if (r->ar.start < start) {
- damon_split_region_at(t, r, start - r->ar.start);
+ damon_split_region_at(ctx, t, r, start - r->ar.start);
matched = false;
break;
}
/* start inside the range */
- damon_split_region_at(t, r, end - r->ar.start);
+ damon_split_region_at(ctx, t, r, end - r->ar.start);
matched = true;
break;
case DAMOS_FILTER_TYPE_PROBE_HITS_WSUM:
@@ -2781,7 +2782,7 @@ static void damos_apply_scheme(struct damon_ctx *c, struct damon_target *t,
c->min_region_sz);
if (!sz)
goto update_stat;
- if (damon_split_region_at(t, r, sz))
+ if (damon_split_region_at(c, t, r, sz))
goto update_stat;
}
if (damos_core_filter_out(c, t, r, s))
@@ -2828,7 +2829,7 @@ static void damon_do_apply_schemes(struct damon_ctx *c,
if (damos_quota_is_full(quota, c->min_region_sz))
continue;

- if (damos_skip_charged_region(t, r, s, c->min_region_sz))
+ if (damos_skip_charged_region(c, t, r, s, c->min_region_sz))
continue;

if (s->max_nr_snapshots &&
@@ -3665,15 +3666,18 @@ static void damon_verify_split_region_at(struct damon_region *r,
/*
* Split a region in two
*
+ * ctx pointer to the region's context
* r the region to be split
* sz_r size of the first sub-region that will be made
*
* Return: 0 on success, negative error code otherwise.
*/
-static int damon_split_region_at(struct damon_target *t,
- struct damon_region *r, unsigned long sz_r)
+static int damon_split_region_at(struct damon_ctx *ctx, struct damon_target *t,
+ struct damon_region *r, unsigned long sz_r)
{
struct damon_region *new;
+ struct damon_probe *p;
+ int i;

damon_verify_split_region_at(r, sz_r);
new = damon_new_region(r->ar.start + sz_r, r->ar.end);
@@ -3685,11 +3689,13 @@ static int damon_split_region_at(struct damon_target *t,
new->age = r->age;
new->last_nr_accesses = r->last_nr_accesses;
new->nr_accesses = r->nr_accesses;
- /* todo: do this for only installed probes */
- memcpy(new->probe_hits, r->probe_hits, sizeof(r->probe_hits));
- memcpy(new->last_probe_hits, r->last_probe_hits,
- sizeof(r->last_probe_hits));

+ i = 0;
+ damon_for_each_probe(p, ctx) {
+ new->probe_hits[i] = r->probe_hits[i];
+ new->last_probe_hits[i] = r->last_probe_hits[i];
+ ++i;
+ }
damon_insert_region(new, r, damon_next_region(r), t);
return 0;
}
@@ -3718,7 +3724,7 @@ static void damon_split_regions_of(struct damon_ctx *ctx,
if (sz_sub == 0 || sz_sub >= sz_region)
continue;

- damon_split_region_at(t, r, sz_sub);
+ damon_split_region_at(ctx, t, r, sz_sub);
sz_region = sz_sub;
}
}
@@ -3750,7 +3756,7 @@ static void damon_split_some_regions(struct damon_ctx *ctx,
if (sz_sub == 0 || sz_sub >= sz_region)
continue;

- damon_split_region_at(t, r, sz_sub);
+ damon_split_region_at(ctx, t, r, sz_sub);
}
}
}
diff --git a/mm/damon/tests/core-kunit.h b/mm/damon/tests/core-kunit.h
index 385d32901650..61ce085549a6 100644
--- a/mm/damon/tests/core-kunit.h
+++ b/mm/damon/tests/core-kunit.h
@@ -141,6 +141,8 @@ static void damon_test_split_at(struct kunit *test)
{
struct damon_target *t;
struct damon_region *r, *r_new;
+ struct damon_ctx *ctx;
+ struct damon_probe *p;

t = damon_new_target();
if (!t)
@@ -150,13 +152,25 @@ static void damon_test_split_at(struct kunit *test)
damon_free_target(t);
kunit_skip(test, "region alloc fail");
}
+ p = damon_new_probe();
+ if (!p)
+ kunit_skip(test, "probe alloc fail");
+ ctx = damon_new_ctx();
+ if (!ctx) {
+ damon_destroy_probe(p);
+ damon_free_target(t);
+ kunit_skip(test, "context alloc fail");
+ }
+ damon_add_probe(ctx, p);
+ damon_add_target(ctx, t);
+
r->nr_accesses = 42;
r->last_nr_accesses = 15;
r->probe_hits[0] = 7;
r->last_probe_hits[0] = 3;
r->age = 10;
damon_add_region(r, t);
- damon_split_region_at(t, r, 25);
+ damon_split_region_at(ctx, t, r, 25);
KUNIT_EXPECT_EQ(test, damon_nr_regions(t), 2);
if (damon_nr_regions(t) != 2)
goto out;
@@ -175,7 +189,7 @@ static void damon_test_split_at(struct kunit *test)
KUNIT_EXPECT_EQ(test, r->age, r_new->age);

out:
- damon_free_target(t);
+ damon_destroy_ctx(ctx);
}

static void damon_test_merge_two(struct kunit *test)
@@ -1799,6 +1813,8 @@ static void damos_test_filter_out(struct kunit *test)
struct damon_target *t;
struct damon_region *r, *r2;
struct damos_filter *f;
+ struct damon_ctx *ctx;
+ struct damon_probe *p;

f = damos_new_filter(DAMOS_FILTER_TYPE_ADDR, true, false);
if (!f)
@@ -1818,28 +1834,42 @@ static void damos_test_filter_out(struct kunit *test)
}
damon_add_region(r, t);

+ p = damon_new_probe();
+ if (!p) {
+ kunit_skip(test, "probe alloc fail");
+ damon_free_target(t);
+ }
+ ctx = damon_new_ctx();
+ if (!ctx) {
+ damon_destroy_probe(p);
+ damon_free_target(t);
+ kunit_skip(test, "context alloc fail");
+ }
+ damon_add_probe(ctx, p);
+ damon_add_target(ctx, t);
+
/* region in the range */
- KUNIT_EXPECT_TRUE(test, damos_filter_match(NULL, t, r, f, 1));
+ KUNIT_EXPECT_TRUE(test, damos_filter_match(ctx, t, r, f, 1));
KUNIT_EXPECT_EQ(test, damon_nr_regions(t), 1);

/* region before the range */
r->ar.start = 1;
r->ar.end = 2;
KUNIT_EXPECT_FALSE(test,
- damos_filter_match(NULL, t, r, f, 1));
+ damos_filter_match(ctx, t, r, f, 1));
KUNIT_EXPECT_EQ(test, damon_nr_regions(t), 1);

/* region after the range */
r->ar.start = 6;
r->ar.end = 8;
KUNIT_EXPECT_FALSE(test,
- damos_filter_match(NULL, t, r, f, 1));
+ damos_filter_match(ctx, t, r, f, 1));
KUNIT_EXPECT_EQ(test, damon_nr_regions(t), 1);

/* region started before the range */
r->ar.start = 1;
r->ar.end = 4;
- KUNIT_EXPECT_FALSE(test, damos_filter_match(NULL, t, r, f, 1));
+ KUNIT_EXPECT_FALSE(test, damos_filter_match(ctx, t, r, f, 1));
/* filter should have split the region */
KUNIT_EXPECT_EQ(test, r->ar.start, 1);
KUNIT_EXPECT_EQ(test, r->ar.end, 2);
@@ -1855,7 +1885,7 @@ static void damos_test_filter_out(struct kunit *test)
r->ar.start = 2;
r->ar.end = 8;
KUNIT_EXPECT_TRUE(test,
- damos_filter_match(NULL, t, r, f, 1));
+ damos_filter_match(ctx, t, r, f, 1));
/* filter should have split the region */
KUNIT_EXPECT_EQ(test, r->ar.start, 2);
KUNIT_EXPECT_EQ(test, r->ar.end, 6);
@@ -1868,7 +1898,7 @@ static void damos_test_filter_out(struct kunit *test)
damon_destroy_region(r2, t);

out:
- damon_free_target(t);
+ damon_destroy_ctx(ctx);
damos_free_filter(f);
}

@@ -2458,6 +2488,7 @@ static void damos_test_esz_goal_temporal(struct kunit *test)
static void damon_test_last_region_quota_reset(struct kunit *test)
{
struct damos s = {0};
+ struct damon_ctx *ctx;
struct damon_target *t;
struct damon_region *r1, *r2;
unsigned long min_region_sz = 1;
@@ -2484,7 +2515,14 @@ static void damon_test_last_region_quota_reset(struct kunit *test)
s.quota.charge_target_from = t;
s.quota.charge_addr_from = r1->ar.end;

- skipped = damos_skip_charged_region(t, r2, &s, min_region_sz);
+ ctx = damon_new_ctx();
+ if (!ctx) {
+ kunit_skip(test, "ctx alloc failed");
+ damon_free_target(t);
+ }
+ damon_add_target(ctx, t);
+
+ skipped = damos_skip_charged_region(ctx, t, r2, &s, min_region_sz);

/* 'r2' is not processed, it should not skip */
KUNIT_EXPECT_EQ(test, skipped, false);
@@ -2493,7 +2531,7 @@ static void damon_test_last_region_quota_reset(struct kunit *test)
KUNIT_EXPECT_PTR_EQ(test, s.quota.charge_target_from, NULL);
KUNIT_EXPECT_EQ(test, s.quota.charge_addr_from, 0);

- damon_free_target(t);
+ damon_destroy_ctx(ctx);
}

static struct kunit_case damon_test_cases[] = {
--
2.55.0