* [RFC PATCH 1/6] mm/damon/tests/core-kunit: test damon_commit_filter()
2026-07-29 14:47 [RFC PATCH 0/6] mm/damon: add kunit and selftests for probes and probe weights SJ Park
@ 2026-07-29 14:47 ` SJ Park
2026-07-29 14:47 ` [RFC PATCH 2/6] mm/damon/tests/core-kunit: add damon_commit_probes() test SJ Park
` (4 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: SJ Park @ 2026-07-29 14:47 UTC (permalink / raw)
Cc: SJ Park, Andrew Morton, Brendan Higgins, David Gow, damon,
kunit-dev, linux-kernel, linux-kselftest, linux-mm
Add kunit test to ensure damon_commit_filter() updates destination
filter as expected for valid inputs.
Signed-off-by: SJ Park <sj@kernel.org>
---
mm/damon/tests/core-kunit.h | 40 +++++++++++++++++++++++++++++++++++++
1 file changed, 40 insertions(+)
diff --git a/mm/damon/tests/core-kunit.h b/mm/damon/tests/core-kunit.h
index c7bda8aadef3f..ff864402e9c75 100644
--- a/mm/damon/tests/core-kunit.h
+++ b/mm/damon/tests/core-kunit.h
@@ -1354,6 +1354,45 @@ static void damon_test_commit_target_regions(struct kunit *test)
(unsigned long[][2]) {{3, 8}, {8, 10}}, 2);
}
+static void damon_test_commit_filter_for(struct kunit *test,
+ struct damon_filter *dst, struct damon_filter *src)
+{
+ damon_commit_filter(dst, src);
+ KUNIT_EXPECT_EQ(test, dst->type, src->type);
+ KUNIT_EXPECT_EQ(test, dst->matching, src->matching);
+ KUNIT_EXPECT_EQ(test, dst->allow, src->allow);
+ switch (src->type) {
+ case DAMOS_FILTER_TYPE_MEMCG:
+ KUNIT_EXPECT_EQ(test, dst->memcg_id, src->memcg_id);
+ break;
+ default:
+ break;
+ }
+}
+
+static void damon_test_commit_filter(struct kunit *test)
+{
+ struct damon_filter dst = {
+ .type = DAMON_FILTER_TYPE_ANON,
+ .matching = false,
+ .allow = false,
+ };
+
+ damon_test_commit_filter_for(test, &dst,
+ &(struct damon_filter){
+ .type = DAMON_FILTER_TYPE_ANON,
+ .matching = true,
+ .allow = true,
+ });
+ damon_test_commit_filter_for(test, &dst,
+ &(struct damon_filter){
+ .type = DAMON_FILTER_TYPE_MEMCG,
+ .matching = false,
+ .allow = false,
+ .memcg_id = 123,
+ });
+}
+
static void damon_test_commit_ctx(struct kunit *test)
{
struct damon_ctx *src, *dst;
@@ -1705,6 +1744,7 @@ static struct kunit_case damon_test_cases[] = {
KUNIT_CASE(damos_test_commit_pageout),
KUNIT_CASE(damos_test_commit_migrate_hot),
KUNIT_CASE(damon_test_commit_target_regions),
+ KUNIT_CASE(damon_test_commit_filter),
KUNIT_CASE(damon_test_commit_ctx),
KUNIT_CASE(damos_test_filter_out),
KUNIT_CASE(damon_test_feed_loop_next_input),
--
2.47.3
^ permalink raw reply related [flat|nested] 7+ messages in thread* [RFC PATCH 2/6] mm/damon/tests/core-kunit: add damon_commit_probes() test
2026-07-29 14:47 [RFC PATCH 0/6] mm/damon: add kunit and selftests for probes and probe weights SJ Park
2026-07-29 14:47 ` [RFC PATCH 1/6] mm/damon/tests/core-kunit: test damon_commit_filter() SJ Park
@ 2026-07-29 14:47 ` SJ Park
2026-07-29 14:47 ` [RFC PATCH 3/6] selftests/damon/_damon_sysfs: implement DamonProbes SJ Park
` (3 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: SJ Park @ 2026-07-29 14:47 UTC (permalink / raw)
Cc: SJ Park, Andrew Morton, Brendan Higgins, David Gow, damon,
kunit-dev, linux-kernel, linux-kselftest, linux-mm
Add kunit test to ensure damon_commit_probes() updates destination DAMON
context with source probes as expected.
Signed-off-by: SJ Park <sj@kernel.org>
---
mm/damon/tests/core-kunit.h | 81 +++++++++++++++++++++++++++++++++++++
1 file changed, 81 insertions(+)
diff --git a/mm/damon/tests/core-kunit.h b/mm/damon/tests/core-kunit.h
index ff864402e9c75..d1e8c612fea03 100644
--- a/mm/damon/tests/core-kunit.h
+++ b/mm/damon/tests/core-kunit.h
@@ -1393,6 +1393,86 @@ static void damon_test_commit_filter(struct kunit *test)
});
}
+static struct damon_ctx *damon_test_help_setup_probes(unsigned int weights[],
+ int nr_weights)
+{
+ struct damon_ctx *ctx;
+ struct damon_probe *probe;
+ int i;
+
+ ctx = damon_new_ctx();
+ if (!ctx)
+ return NULL;
+ for (i = 0; i < nr_weights; i++) {
+ probe = damon_new_probe();
+ if (!probe) {
+ damon_destroy_ctx(ctx);
+ return NULL;
+ }
+ probe->weight = weights[i];
+ damon_add_probe(ctx, probe);
+ }
+ return ctx;
+}
+
+static void damon_test_commit_probes_for(struct kunit *test,
+ unsigned int dst_weights[], int nr_dst_probes,
+ unsigned int src_weights[], int nr_src_probes)
+{
+ struct damon_ctx *dst, *src;
+ int err;
+ struct damon_probe *dst_probe, *src_probe;
+
+ dst = damon_test_help_setup_probes(dst_weights, nr_dst_probes);
+ if (!dst)
+ kunit_skip(test, "dst alloc fail");
+ src = damon_test_help_setup_probes(src_weights, nr_src_probes);
+ if (!src) {
+ damon_destroy_ctx(dst);
+ kunit_skip(test, "src alloc fail");
+ }
+
+ err = damon_commit_probes(dst, src);
+ KUNIT_EXPECT_EQ(test, err, 0);
+ if (err)
+ return;
+ nr_dst_probes = 0;
+ damon_for_each_probe(dst_probe, dst)
+ nr_dst_probes++;
+ nr_src_probes = 0;
+ damon_for_each_probe(src_probe, src)
+ nr_src_probes++;
+ KUNIT_EXPECT_EQ(test, nr_dst_probes, nr_src_probes);
+ nr_dst_probes = 0;
+ damon_for_each_probe(dst_probe, dst) {
+ src_probe = damon_nth_probe(nr_dst_probes, src);
+ KUNIT_EXPECT_EQ(test, src_probe->weight, dst_probe->weight);
+ nr_dst_probes++;
+ }
+ damon_destroy_ctx(dst);
+ damon_destroy_ctx(src);
+}
+
+static void damon_test_commit_probes(struct kunit *test)
+{
+ damon_test_commit_probes_for(test,
+ (unsigned int[]){}, 0, (unsigned int[]){}, 0);
+ damon_test_commit_probes_for(test,
+ (unsigned int[]){}, 0, (unsigned int[]){1}, 1);
+ damon_test_commit_probes_for(test,
+ (unsigned int[]){}, 0, (unsigned int[]){1, 2}, 2);
+ damon_test_commit_probes_for(test,
+ (unsigned int[]){1}, 1, (unsigned int[]){2}, 1);
+ damon_test_commit_probes_for(test,
+ (unsigned int[]){1}, 1, (unsigned int[]){2, 3}, 2);
+ damon_test_commit_probes_for(test,
+ (unsigned int[]){2, 3}, 2, (unsigned int[]){1}, 1);
+ damon_test_commit_probes_for(test,
+ (unsigned int[]){2, 3}, 2, (unsigned int[]){}, 0);
+ damon_test_commit_probes_for(test,
+ (unsigned int[]){2}, 1, (unsigned int[]){}, 0);
+}
+
static void damon_test_commit_ctx(struct kunit *test)
{
struct damon_ctx *src, *dst;
@@ -1745,6 +1825,7 @@ static struct kunit_case damon_test_cases[] = {
KUNIT_CASE(damos_test_commit_migrate_hot),
KUNIT_CASE(damon_test_commit_target_regions),
KUNIT_CASE(damon_test_commit_filter),
+ KUNIT_CASE(damon_test_commit_probes),
KUNIT_CASE(damon_test_commit_ctx),
KUNIT_CASE(damos_test_filter_out),
KUNIT_CASE(damon_test_feed_loop_next_input),
--
2.47.3
^ permalink raw reply related [flat|nested] 7+ messages in thread* [RFC PATCH 3/6] selftests/damon/_damon_sysfs: implement DamonProbes
2026-07-29 14:47 [RFC PATCH 0/6] mm/damon: add kunit and selftests for probes and probe weights SJ Park
2026-07-29 14:47 ` [RFC PATCH 1/6] mm/damon/tests/core-kunit: test damon_commit_filter() SJ Park
2026-07-29 14:47 ` [RFC PATCH 2/6] mm/damon/tests/core-kunit: add damon_commit_probes() test SJ Park
@ 2026-07-29 14:47 ` SJ Park
2026-07-29 14:47 ` [RFC PATCH 4/6] selftests/damon/drgn_dump_damon_status: dump probes SJ Park
` (2 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: SJ Park @ 2026-07-29 14:47 UTC (permalink / raw)
Cc: SJ Park, Shuah Khan, damon, linux-kernel, linux-kselftest,
linux-mm
Extend _damon_sysfs.py to support staging and committing DAMON probes.
It will be used for setting DAMON probes via sysfs changes for testing
purposes.
Signed-off-by: SJ Park <sj@kernel.org>
---
tools/testing/selftests/damon/_damon_sysfs.py | 123 +++++++++++++++++-
1 file changed, 122 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/damon/_damon_sysfs.py b/tools/testing/selftests/damon/_damon_sysfs.py
index e6a2265d721e8..d6afb5b2f0a4a 100644
--- a/tools/testing/selftests/damon/_damon_sysfs.py
+++ b/tools/testing/selftests/damon/_damon_sysfs.py
@@ -558,6 +558,115 @@ class IntervalsGoal:
return err
return None
+class DamonFilter:
+ type_ = None
+ matching = None
+ allow = None
+ filters = None
+ path = None
+ idx = None
+
+ def __init__(self, type_='anon', matching=False, allow=False, path=None):
+ self.type_ = type_
+ self.matching = matching
+ self.allow = allow
+ self.path = path
+
+ def sysfs_dir(self):
+ return os.path.join(self.filters.sysfs_dir(), '%d' % self.idx)
+
+ def stage(self):
+ err = write_file(os.path.join(self.sysfs_dir(), 'type'), self.type_)
+ if err is not None:
+ return err
+ err = write_file(os.path.join(self.sysfs_dir(), 'matching'),
+ 'Y' if self.matching else 'N')
+ if err is not None:
+ return err
+ err = write_file(os.path.join(self.sysfs_dir(), 'allow'),
+ 'Y' if self.allow else 'N')
+ if err is not None:
+ return err
+ if self.type_ == 'memcg':
+ err = write_file(os.path.join(self.sysfs_dir(), 'path'), self.path)
+ if err is not None:
+ return err
+ return None
+
+class DamonFilters:
+ filters = None
+ probe = None
+
+ def __init__(self, filters=None):
+ if filters is None:
+ filters = []
+ self.filters = filters
+ for idx, filter in enumerate(self.filters):
+ filter.filters = self
+ filter.idx = idx
+
+ def sysfs_dir(self):
+ return os.path.join(self.probe.sysfs_dir(), 'filters')
+
+ def stage(self):
+ err = write_file(
+ os.path.join(self.sysfs_dir(), 'nr_filters'),
+ len(self.filters))
+ if err is not None:
+ return err
+ for filter in self.filters:
+ err = filter.stage()
+ if err is not None:
+ return err
+ return None
+
+class DamonProbe:
+ weight = None
+ filters = None
+ probes = None
+ idx = None
+
+ def __init__(self, weight=0, filters=None):
+ self.weight = weight
+ if filters is None:
+ filters = DamonFilters()
+ self.filters = filters
+ self.filters.probe = self
+
+ def sysfs_dir(self):
+ return os.path.join(self.probes.sysfs_dir(), '%d' % self.idx)
+
+ def stage(self):
+ err = write_file(
+ os.path.join(self.sysfs_dir(), 'weight'), '%d' % self.weight)
+ return self.filters.stage()
+
+class DamonProbes:
+ probes = None
+ attrs = None
+
+ def __init__(self, probes=None):
+ if probes is None:
+ probes = []
+ self.probes = probes
+ for idx, probe in enumerate(self.probes):
+ probe.probes = self
+ probe.idx = idx
+
+ def sysfs_dir(self):
+ return os.path.join(self.attrs.sysfs_dir(), 'probes')
+
+ def stage(self):
+ err = write_file(os.path.join(self.sysfs_dir(), 'nr_probes'),
+ len(self.probes))
+ if err is not None:
+ return err
+ for probe in self.probes:
+ err = probe.stage()
+ if err is not None:
+ return err
+ return None
+
class DamonAttrs:
sample_us = None
aggr_us = None
@@ -565,11 +674,12 @@ class DamonAttrs:
update_us = None
min_nr_regions = None
max_nr_regions = None
+ probes = None
context = None
def __init__(self, sample_us=5000, aggr_us=100000,
intervals_goal=IntervalsGoal(), update_us=1000000,
- min_nr_regions=10, max_nr_regions=1000):
+ min_nr_regions=10, max_nr_regions=1000, probes=None):
self.sample_us = sample_us
self.aggr_us = aggr_us
self.intervals_goal = intervals_goal
@@ -577,6 +687,10 @@ class DamonAttrs:
self.update_us = update_us
self.min_nr_regions = min_nr_regions
self.max_nr_regions = max_nr_regions
+ if probes is None:
+ probes = DamonProbes()
+ self.probes = probes
+ self.probes.attrs = self
def interval_sysfs_dir(self):
return os.path.join(self.context.sysfs_dir(), 'monitoring_attrs',
@@ -586,6 +700,9 @@ class DamonAttrs:
return os.path.join(self.context.sysfs_dir(), 'monitoring_attrs',
'nr_regions')
+ def sysfs_dir(self):
+ return os.path.join(self.context.sysfs_dir(), 'monitoring_attrs')
+
def stage(self):
err = write_file(os.path.join(self.interval_sysfs_dir(), 'sample_us'),
self.sample_us)
@@ -615,6 +732,10 @@ class DamonAttrs:
if err is not None:
return err
+ err = self.probes.stage()
+ if err is not None:
+ return err
+
class DamonCtx:
ops = None
monitoring_attrs = None
--
2.47.3
^ permalink raw reply related [flat|nested] 7+ messages in thread* [RFC PATCH 4/6] selftests/damon/drgn_dump_damon_status: dump probes
2026-07-29 14:47 [RFC PATCH 0/6] mm/damon: add kunit and selftests for probes and probe weights SJ Park
` (2 preceding siblings ...)
2026-07-29 14:47 ` [RFC PATCH 3/6] selftests/damon/_damon_sysfs: implement DamonProbes SJ Park
@ 2026-07-29 14:47 ` SJ Park
2026-07-29 14:47 ` [RFC PATCH 5/6] selftests/damon/sysfs.py: extend commit assertion function for probes SJ Park
2026-07-29 14:47 ` [RFC PATCH 6/6] selftests/damon/sysfs.py: test damon probes SJ Park
5 siblings, 0 replies; 7+ messages in thread
From: SJ Park @ 2026-07-29 14:47 UTC (permalink / raw)
Cc: SJ Park, Shuah Khan, damon, linux-kernel, linux-kselftest,
linux-mm
Extend drgn_dump_damon_status.py to dump damon_ctx->probes. It will be
used to see if in-kernel DAMON status are changed as the user sets the
probes via sysfs.
Signed-off-by: SJ Park <sj@kernel.org>
---
.../selftests/damon/drgn_dump_damon_status.py | 32 +++++++++++++++++++
1 file changed, 32 insertions(+)
diff --git a/tools/testing/selftests/damon/drgn_dump_damon_status.py b/tools/testing/selftests/damon/drgn_dump_damon_status.py
index 09552e91bc782..4622046fd0118 100755
--- a/tools/testing/selftests/damon/drgn_dump_damon_status.py
+++ b/tools/testing/selftests/damon/drgn_dump_damon_status.py
@@ -48,6 +48,37 @@ def attrs_to_dict(attrs):
['max_nr_regions', int],
])
+def filter_to_dict(damon_filter):
+ filter_type_keyword = {
+ 0: 'anon',
+ 1: 'memcg',
+ }
+ dict_ = {
+ 'type': filter_type_keyword[int(damon_filter.type)],
+ 'matching': bool(damon_filter.matching),
+ 'allow': bool(damon_filter.allow),
+ }
+ type_ = dict_['type']
+ if type_ == 'memcg':
+ dict_['memcg_id'] = int(damon_filter.memcg_id)
+ return dict_
+
+def filters_to_list(filters):
+ return [filter_to_dict(f)
+ for f in list_for_each_entry(
+ 'struct damon_filter', filters.address_of_(), 'list')]
+
+def probe_to_dict(probe):
+ return to_dict(probe, [
+ ['weight', int],
+ ['filters', filters_to_list],
+ ])
+
+def probes_to_list(probes):
+ return [probe_to_dict(p)
+ for p in list_for_each_entry(
+ 'struct damon_probe', probes.address_of_(), 'list')]
+
def addr_range_to_dict(addr_range):
return to_dict(addr_range, [
['start', int],
@@ -199,6 +230,7 @@ def damon_ctx_to_dict(ctx):
return to_dict(ctx, [
['ops', ops_to_dict],
['attrs', attrs_to_dict],
+ ['probes', probes_to_list],
['adaptive_targets', targets_to_list],
['schemes', schemes_to_list],
['pause', bool],
--
2.47.3
^ permalink raw reply related [flat|nested] 7+ messages in thread* [RFC PATCH 5/6] selftests/damon/sysfs.py: extend commit assertion function for probes
2026-07-29 14:47 [RFC PATCH 0/6] mm/damon: add kunit and selftests for probes and probe weights SJ Park
` (3 preceding siblings ...)
2026-07-29 14:47 ` [RFC PATCH 4/6] selftests/damon/drgn_dump_damon_status: dump probes SJ Park
@ 2026-07-29 14:47 ` SJ Park
2026-07-29 14:47 ` [RFC PATCH 6/6] selftests/damon/sysfs.py: test damon probes SJ Park
5 siblings, 0 replies; 7+ messages in thread
From: SJ Park @ 2026-07-29 14:47 UTC (permalink / raw)
Cc: SJ Park, Shuah Khan, damon, linux-kernel, linux-kselftest,
linux-mm
Extend DAMON sysfs testing commit assertion helper function to check
probes too.
Signed-off-by: SJ Park <sj@kernel.org>
---
tools/testing/selftests/damon/sysfs.py | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/tools/testing/selftests/damon/sysfs.py b/tools/testing/selftests/damon/sysfs.py
index 88a26422ff44c..159cbeac067f8 100755
--- a/tools/testing/selftests/damon/sysfs.py
+++ b/tools/testing/selftests/damon/sysfs.py
@@ -178,6 +178,24 @@ def assert_monitoring_attrs_committed(attrs, dump):
assert_true(dump['max_nr_regions'] == attrs.max_nr_regions,
'max_nr_regions', dump)
+def assert_damon_filters_committed(filters, dump):
+ assert_true(len(dump) == len(filters.filters), 'probe filters', dump)
+ for idx, damon_filter in enumerate(filters.filters):
+ filter_dump = dump[idx]
+ assert_true(filter_dump['type'] == damon_filter.type_, 'type',
+ filter_dump)
+ assert_true(filter_dump['matching'] == damon_filter.matching,
+ 'matching', filter_dump)
+ assert_true(filter_dump['allow'] == damon_filter.allow, 'allow',
+ filter_dump)
+
+def assert_probes_committed(probes, dump):
+ assert_true(len(dump) == len(probes.probes), 'probes length', dump)
+ for idx, probe in enumerate(probes.probes):
+ probe_dump = dump[idx]
+ assert_true(probe.weight == probe_dump['weight'], 'weight', probe_dump)
+ assert_damon_filters_committed(probe.filters, probe_dump['filters'])
+
def assert_monitoring_target_committed(target, dump):
# target.pid is the pid "number", while dump['pid'] is 'struct pid'
# pointer, and hence cannot be compared.
@@ -196,6 +214,7 @@ def assert_ctx_committed(ctx, dump):
}
assert_true(dump['ops']['id'] == ops_val[ctx.ops], 'ops_id', dump)
assert_monitoring_attrs_committed(ctx.monitoring_attrs, dump['attrs'])
+ assert_probes_committed(ctx.monitoring_attrs.probes, dump['probes'])
assert_monitoring_targets_committed(ctx.targets, dump['adaptive_targets'])
assert_schemes_committed(ctx.schemes, dump['schemes'])
assert_true(dump['pause'] == ctx.pause, 'pause', dump)
--
2.47.3
^ permalink raw reply related [flat|nested] 7+ messages in thread* [RFC PATCH 6/6] selftests/damon/sysfs.py: test damon probes
2026-07-29 14:47 [RFC PATCH 0/6] mm/damon: add kunit and selftests for probes and probe weights SJ Park
` (4 preceding siblings ...)
2026-07-29 14:47 ` [RFC PATCH 5/6] selftests/damon/sysfs.py: extend commit assertion function for probes SJ Park
@ 2026-07-29 14:47 ` SJ Park
5 siblings, 0 replies; 7+ messages in thread
From: SJ Park @ 2026-07-29 14:47 UTC (permalink / raw)
Cc: SJ Park, Shuah Khan, damon, linux-kernel, linux-kselftest,
linux-mm
Extend sysfs.py to commit DAMON probes via sysfs, and see if it changed
in-kernel DAMON status as expected using drgn.
Signed-off-by: SJ Park <sj@kernel.org>
---
tools/testing/selftests/damon/sysfs.py | 16 +++++++++++++++-
1 file changed, 15 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/damon/sysfs.py b/tools/testing/selftests/damon/sysfs.py
index 159cbeac067f8..66c826189320d 100755
--- a/tools/testing/selftests/damon/sysfs.py
+++ b/tools/testing/selftests/damon/sysfs.py
@@ -319,7 +319,21 @@ def main():
intervals_goal=_damon_sysfs.IntervalsGoal(
access_bp=400, aggrs=3, min_sample_us=5000,
max_sample_us=10000000),
- update_us=2000000),
+ update_us=2000000,
+ probes=_damon_sysfs.DamonProbes(
+ probes=[_damon_sysfs.DamonProbe(
+ weight=42,
+ filters=_damon_sysfs.DamonFilters(
+ filters=[
+ _damon_sysfs.DamonFilter(
+ type_='anon',
+ matching=True,
+ allow=True,
+ ),
+ ]),
+ ),
+ ]),
+ ),
schemes=[_damon_sysfs.Damos(
action='pageout',
access_pattern=_damon_sysfs.DamosAccessPattern(
--
2.47.3
^ permalink raw reply related [flat|nested] 7+ messages in thread