From: SJ Park <sj@kernel.org>
To: Andrew Morton <akpm@linux-foundation.org>
Cc: Donggeun Yoo <donggeunyoo.kernel@gmail.com>,
Brendan Higgins <brendan.higgins@linux.dev>,
David Gow <davidgow@davidgow.net>, SJ Park <sj@kernel.org>,
damon@lists.linux.dev, kunit-dev@googlegroups.com,
linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org,
linux-mm@kvack.org
Subject: [PATCH v2 2/2] mm/damon/tests/core-kunit: test the size charged for a filter-trimmed region
Date: Mon, 21 Sep 2026 08:24:42 -0700 [thread overview]
Message-ID: <20260921152443.80132-3-sj@kernel.org> (raw)
In-Reply-To: <20260921152443.80132-1-sj@kernel.org>
From: Donggeun Yoo <donggeunyoo.kernel@gmail.com>
damos_test_filter_out() checks that an address range filter splits a
region at the filter boundary, and stops there. Nothing checks what
damos_apply_scheme() then charges to the quota and reports as tried.
damos_test_apply_scheme_filtered_sz() covers the two ways such a filter
trims a region: one that starts before the filter's range, and one that
starts inside it.
damos_test_apply_scheme_filter_sz_unchanged() and
damos_test_apply_scheme_quota_sz() cover the cases whose accounting must
not change: a region wholly inside a reject range, one wholly inside an
allow range, two filters trimming in a single call, a filter type that
never splits, no filter at all, a region the quota trims, and a quota
remainder too small for one region.
Signed-off-by: Donggeun Yoo <donggeunyoo.kernel@gmail.com>
Reviewed-by: SJ Park <sj@kernel.org>
Signed-off-by: SJ Park <sj@kernel.org>
---
mm/damon/tests/core-kunit.h | 316 ++++++++++++++++++++++++++++++++++++
1 file changed, 316 insertions(+)
diff --git a/mm/damon/tests/core-kunit.h b/mm/damon/tests/core-kunit.h
index 5ff0436c5844..df84d9cc7d20 100644
--- a/mm/damon/tests/core-kunit.h
+++ b/mm/damon/tests/core-kunit.h
@@ -1706,6 +1706,319 @@ static void damos_test_filter_out(struct kunit *test)
damos_free_filter(f);
}
+static unsigned long damos_test_apply_scheme_stub(struct damon_ctx *c,
+ struct damon_target *t, struct damon_region *r,
+ struct damos *s, unsigned long *sz_filter_passed)
+{
+ return 0;
+}
+
+static void damos_test_apply_scheme_filtered_sz(struct kunit *test)
+{
+ struct damos_access_pattern pattern = {
+ .min_sz_region = 0,
+ .max_sz_region = ULONG_MAX,
+ .min_nr_accesses = 0,
+ .max_nr_accesses = UINT_MAX,
+ .min_age_region = 0,
+ .max_age_region = UINT_MAX,
+ };
+ unsigned long min_sz = DAMON_MIN_REGION_SZ;
+ struct damos_watermarks wmarks = {};
+ struct damos_quota quota = {};
+ struct damon_ctx *c;
+ struct damon_target *t;
+ struct damon_region *r;
+ struct damos_filter *f;
+ struct damos *s;
+
+ c = damon_new_ctx();
+ if (!c)
+ kunit_skip(test, "ctx alloc fail");
+ c->ops.apply_scheme = damos_test_apply_scheme_stub;
+
+ s = damon_new_scheme(&pattern, DAMOS_STAT, 0, "a, &wmarks,
+ NUMA_NO_NODE);
+ if (!s) {
+ damon_destroy_ctx(c);
+ kunit_skip(test, "scheme alloc fail");
+ }
+ damon_add_scheme(c, s);
+
+ t = damon_new_target();
+ if (!t) {
+ damon_destroy_ctx(c);
+ kunit_skip(test, "target alloc fail");
+ }
+ damon_add_target(c, t);
+
+ f = damos_new_filter(DAMOS_FILTER_TYPE_ADDR, true, false);
+ if (!f) {
+ damon_destroy_ctx(c);
+ kunit_skip(test, "filter alloc fail");
+ }
+ f->addr_range = (struct damon_addr_range){
+ .start = 2 * min_sz,
+ .end = 8 * min_sz
+ };
+ damos_add_filter(s, f);
+ damos_set_filters_default_reject(s);
+
+ /* reject filter, region starting before the range */
+ r = damon_new_region(0, 4 * min_sz);
+ if (!r) {
+ damon_destroy_ctx(c);
+ kunit_skip(test, "region alloc fail");
+ }
+ damon_add_region(r, t);
+
+ damos_apply_scheme(c, t, r, s);
+ KUNIT_EXPECT_EQ(test, damon_nr_regions(t), 2);
+ KUNIT_EXPECT_EQ(test, r->ar.end, 2 * min_sz);
+ KUNIT_EXPECT_EQ(test, s->stat.sz_tried, 2 * min_sz);
+
+ if (damon_nr_regions(t) != 2)
+ goto out;
+ damon_destroy_region(damon_next_region(r), t);
+ damon_destroy_region(r, t);
+ s->stat = (struct damos_stat){};
+
+ /* allow filter, region starting inside the range */
+ f->allow = true;
+ damos_set_filters_default_reject(s);
+ r = damon_new_region(2 * min_sz, 10 * min_sz);
+ if (!r) {
+ damon_destroy_ctx(c);
+ kunit_skip(test, "region alloc fail");
+ }
+ damon_add_region(r, t);
+
+ damos_apply_scheme(c, t, r, s);
+ KUNIT_EXPECT_EQ(test, damon_nr_regions(t), 2);
+ KUNIT_EXPECT_EQ(test, r->ar.end, 8 * min_sz);
+ KUNIT_EXPECT_EQ(test, s->stat.sz_tried, 6 * min_sz);
+
+out:
+ damon_destroy_ctx(c);
+}
+
+static void damos_test_apply_scheme_filter_sz_unchanged(struct kunit *test)
+{
+ struct damos_access_pattern pattern = {
+ .min_sz_region = 0,
+ .max_sz_region = ULONG_MAX,
+ .min_nr_accesses = 0,
+ .max_nr_accesses = UINT_MAX,
+ .min_age_region = 0,
+ .max_age_region = UINT_MAX,
+ };
+ unsigned long min_sz = DAMON_MIN_REGION_SZ;
+ struct damos_watermarks wmarks = {};
+ struct damos_quota quota = {};
+ struct damos_filter *f, *f2;
+ struct damon_ctx *c;
+ struct damon_target *t;
+ struct damon_region *r;
+ struct damos *s;
+
+ c = damon_new_ctx();
+ if (!c)
+ kunit_skip(test, "ctx alloc fail");
+ c->ops.apply_scheme = damos_test_apply_scheme_stub;
+
+ s = damon_new_scheme(&pattern, DAMOS_STAT, 0, "a, &wmarks,
+ NUMA_NO_NODE);
+ if (!s) {
+ damon_destroy_ctx(c);
+ kunit_skip(test, "scheme alloc fail");
+ }
+ damon_add_scheme(c, s);
+
+ t = damon_new_target();
+ if (!t) {
+ damon_destroy_ctx(c);
+ kunit_skip(test, "target alloc fail");
+ }
+ damon_add_target(c, t);
+
+ f = damos_new_filter(DAMOS_FILTER_TYPE_ADDR, true, false);
+ if (!f) {
+ damon_destroy_ctx(c);
+ kunit_skip(test, "filter alloc fail");
+ }
+ f->addr_range = (struct damon_addr_range){
+ .start = 2 * min_sz, .end = 8 * min_sz};
+ damos_add_filter(s, f);
+ damos_set_filters_default_reject(s);
+
+ /* wholly inside a reject range: not counted at all */
+ r = damon_new_region(4 * min_sz, 6 * min_sz);
+ if (!r) {
+ damon_destroy_ctx(c);
+ kunit_skip(test, "region alloc fail");
+ }
+ damon_add_region(r, t);
+
+ damos_apply_scheme(c, t, r, s);
+ KUNIT_EXPECT_EQ(test, s->stat.nr_tried, 0);
+ KUNIT_EXPECT_EQ(test, s->stat.sz_tried, 0);
+ KUNIT_EXPECT_EQ(test, damon_nr_regions(t), 1);
+
+ /* wholly inside an allow range: counted whole, not split */
+ f->allow = true;
+ damos_set_filters_default_reject(s);
+
+ damos_apply_scheme(c, t, r, s);
+ KUNIT_EXPECT_EQ(test, s->stat.sz_tried, 2 * min_sz);
+ KUNIT_EXPECT_EQ(test, damon_nr_regions(t), 1);
+
+ /* two filters, each trimming: the size left after both is counted */
+ damon_destroy_region(r, t);
+ s->stat = (struct damos_stat){};
+ f->allow = false;
+ f->addr_range = (struct damon_addr_range){
+ .start = 4 * min_sz, .end = 12 * min_sz};
+ f2 = damos_new_filter(DAMOS_FILTER_TYPE_ADDR, true, false);
+ if (!f2) {
+ damon_destroy_ctx(c);
+ kunit_skip(test, "filter alloc fail");
+ }
+ f2->addr_range = (struct damon_addr_range){
+ .start = 2 * min_sz, .end = 3 * min_sz};
+ damos_add_filter(s, f2);
+ damos_set_filters_default_reject(s);
+
+ r = damon_new_region(0, 8 * min_sz);
+ if (!r) {
+ damon_destroy_ctx(c);
+ kunit_skip(test, "region alloc fail");
+ }
+ damon_add_region(r, t);
+
+ damos_apply_scheme(c, t, r, s);
+ KUNIT_EXPECT_EQ(test, damon_nr_regions(t), 3);
+ KUNIT_EXPECT_EQ(test, r->ar.end, 2 * min_sz);
+ KUNIT_EXPECT_EQ(test, s->stat.sz_tried, 2 * min_sz);
+
+ /* a core filter that never splits: counted whole */
+ damos_destroy_filter(f2);
+ damos_destroy_filter(f);
+ f = damos_new_filter(DAMOS_FILTER_TYPE_TARGET, true, true);
+ if (!f) {
+ damon_destroy_ctx(c);
+ kunit_skip(test, "filter alloc fail");
+ }
+ f->target_idx = 0;
+ damos_add_filter(s, f);
+ damos_set_filters_default_reject(s);
+ s->stat = (struct damos_stat){};
+
+ damos_apply_scheme(c, t, r, s);
+ KUNIT_EXPECT_EQ(test, damon_nr_regions(t), 3);
+ KUNIT_EXPECT_EQ(test, r->ar.end, 2 * min_sz);
+ KUNIT_EXPECT_EQ(test, s->stat.sz_tried, 2 * min_sz);
+
+ damon_destroy_ctx(c);
+}
+
+static void damos_test_apply_scheme_quota_sz(struct kunit *test)
+{
+ struct damos_access_pattern pattern = {
+ .min_sz_region = 0,
+ .max_sz_region = ULONG_MAX,
+ .min_nr_accesses = 0,
+ .max_nr_accesses = UINT_MAX,
+ .min_age_region = 0,
+ .max_age_region = UINT_MAX,
+ };
+ unsigned long min_sz = DAMON_MIN_REGION_SZ;
+ struct damos_watermarks wmarks = {};
+ struct damos_quota quota = {};
+ struct damon_region *r, *next;
+ struct damon_ctx *c;
+ struct damon_target *t;
+ struct damos *s;
+
+ c = damon_new_ctx();
+ if (!c)
+ kunit_skip(test, "ctx alloc fail");
+
+ s = damon_new_scheme(&pattern, DAMOS_STAT, 0, "a, &wmarks,
+ NUMA_NO_NODE);
+ if (!s) {
+ damon_destroy_ctx(c);
+ kunit_skip(test, "scheme alloc fail");
+ }
+ damon_add_scheme(c, s);
+ damos_set_filters_default_reject(s);
+
+ t = damon_new_target();
+ if (!t) {
+ damon_destroy_ctx(c);
+ kunit_skip(test, "target alloc fail");
+ }
+ damon_add_target(c, t);
+
+ /* no apply_scheme operation: the whole region is counted */
+ r = damon_new_region(0, 4 * min_sz);
+ if (!r) {
+ damon_destroy_ctx(c);
+ kunit_skip(test, "region alloc fail");
+ }
+ damon_add_region(r, t);
+
+ damos_apply_scheme(c, t, r, s);
+ KUNIT_EXPECT_EQ(test, s->stat.sz_tried, 4 * min_sz);
+ KUNIT_EXPECT_EQ(test, damon_nr_regions(t), 1);
+
+ /* no filter and no quota: the whole region is counted */
+ c->ops.apply_scheme = damos_test_apply_scheme_stub;
+ s->stat = (struct damos_stat){};
+
+ damos_apply_scheme(c, t, r, s);
+ KUNIT_EXPECT_EQ(test, s->stat.sz_tried, 4 * min_sz);
+ KUNIT_EXPECT_EQ(test, damon_nr_regions(t), 1);
+
+ /* the quota trims the region: the trimmed size is counted */
+ damon_for_each_region_safe(r, next, t)
+ damon_destroy_region(r, t);
+ s->stat = (struct damos_stat){};
+ s->quota.esz = 2 * min_sz;
+ s->quota.charged_sz = 0;
+
+ r = damon_new_region(0, 8 * min_sz);
+ if (!r) {
+ damon_destroy_ctx(c);
+ kunit_skip(test, "region alloc fail");
+ }
+ damon_add_region(r, t);
+
+ damos_apply_scheme(c, t, r, s);
+ KUNIT_EXPECT_EQ(test, r->ar.end, 2 * min_sz);
+ KUNIT_EXPECT_EQ(test, s->stat.sz_tried, 2 * min_sz);
+
+ /* quota remainder below one region: tried, but nothing counted */
+ damon_for_each_region_safe(r, next, t)
+ damon_destroy_region(r, t);
+ s->stat = (struct damos_stat){};
+ s->quota.esz = 1;
+ s->quota.charged_sz = 0;
+
+ r = damon_new_region(0, 4 * min_sz);
+ if (!r) {
+ damon_destroy_ctx(c);
+ kunit_skip(test, "region alloc fail");
+ }
+ damon_add_region(r, t);
+
+ damos_apply_scheme(c, t, r, s);
+ KUNIT_EXPECT_EQ(test, s->stat.nr_tried, 1);
+ KUNIT_EXPECT_EQ(test, s->stat.sz_tried, 0);
+ KUNIT_EXPECT_EQ(test, r->ar.end, 4 * min_sz);
+
+ damon_destroy_ctx(c);
+}
+
static void damon_test_feed_loop_next_input(struct kunit *test)
{
unsigned long last_input = 900000, current_score = 200;
@@ -1959,6 +2272,9 @@ static struct kunit_case damon_test_cases[] = {
KUNIT_CASE(damon_test_commit_ctx),
KUNIT_CASE(damon_test_valid_probe_params),
KUNIT_CASE(damos_test_filter_out),
+ KUNIT_CASE(damos_test_apply_scheme_filtered_sz),
+ KUNIT_CASE(damos_test_apply_scheme_filter_sz_unchanged),
+ KUNIT_CASE(damos_test_apply_scheme_quota_sz),
KUNIT_CASE(damon_test_feed_loop_next_input),
KUNIT_CASE(damon_test_set_filters_default_reject),
KUNIT_CASE(damon_test_apply_min_nr_regions),
--
2.47.3
next prev parent reply other threads:[~2026-09-21 15:24 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-21 15:24 [PATCH v2 0/2] mm/damon/core: fix the size charged for a filter-trimmed region SJ Park
2026-09-21 15:24 ` [PATCH v2 1/2] mm/damon/core: charge only the part of a region the filter left SJ Park
2026-09-21 15:32 ` sashiko-bot
2026-09-21 15:24 ` SJ Park [this message]
2026-09-21 15:31 ` [PATCH v2 2/2] mm/damon/tests/core-kunit: test the size charged for a filter-trimmed region sashiko-bot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260921152443.80132-3-sj@kernel.org \
--to=sj@kernel.org \
--cc=akpm@linux-foundation.org \
--cc=brendan.higgins@linux.dev \
--cc=damon@lists.linux.dev \
--cc=davidgow@davidgow.net \
--cc=donggeunyoo.kernel@gmail.com \
--cc=kunit-dev@googlegroups.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=linux-mm@kvack.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.