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 --- 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