summaryrefslogtreecommitdiff
path: root/tools/testing/selftests/damon
diff options
context:
space:
mode:
Diffstat (limited to 'tools/testing/selftests/damon')
-rw-r--r--tools/testing/selftests/damon/Makefile1
-rw-r--r--tools/testing/selftests/damon/_damon_sysfs.py23
-rwxr-xr-xtools/testing/selftests/damon/damos_apply_interval.py2
-rwxr-xr-xtools/testing/selftests/damon/damos_quota_goal.py2
-rwxr-xr-xtools/testing/selftests/damon/damos_tried_regions.py4
-rwxr-xr-xtools/testing/selftests/damon/drgn_dump_damon_status.py3
-rwxr-xr-xtools/testing/selftests/damon/sysfs.py35
-rwxr-xr-xtools/testing/selftests/damon/sysfs.sh79
-rwxr-xr-xtools/testing/selftests/damon/sysfs_refresh.py75
-rwxr-xr-xtools/testing/selftests/damon/sysfs_update_schemes_tried_regions_wss_estimation.py2
10 files changed, 204 insertions, 22 deletions
diff --git a/tools/testing/selftests/damon/Makefile b/tools/testing/selftests/damon/Makefile
index 2180c328a825..ece244e5c5b9 100644
--- a/tools/testing/selftests/damon/Makefile
+++ b/tools/testing/selftests/damon/Makefile
@@ -13,6 +13,7 @@ TEST_PROGS += sysfs.py
TEST_PROGS += sysfs_update_schemes_tried_regions_wss_estimation.py
TEST_PROGS += damos_quota.py damos_quota_goal.py damos_apply_interval.py
TEST_PROGS += damos_tried_regions.py damon_nr_regions.py
+TEST_PROGS += sysfs_refresh.py
TEST_PROGS += reclaim.sh lru_sort.sh
# regression tests (reproducers of previously found bugs)
diff --git a/tools/testing/selftests/damon/_damon_sysfs.py b/tools/testing/selftests/damon/_damon_sysfs.py
index 8b12cc048440..e6a2265d721e 100644
--- a/tools/testing/selftests/damon/_damon_sysfs.py
+++ b/tools/testing/selftests/damon/_damon_sysfs.py
@@ -271,7 +271,7 @@ class DamosFilter:
self.type_ = type_
self.matching = matching
self.allow = allow
- self.memcg_path = memcg_path,
+ self.memcg_path = memcg_path
self.addr_start = addr_start
self.addr_end = addr_end
self.target_idx = target_idx
@@ -624,17 +624,23 @@ class DamonCtx:
pause = None
idx = None
- def __init__(self, ops='paddr', monitoring_attrs=DamonAttrs(), targets=[],
- schemes=[], pause=False):
+ def __init__(self, ops='paddr', monitoring_attrs=None, targets=None,
+ schemes=None, pause=False):
self.ops = ops
+ if monitoring_attrs is None:
+ monitoring_attrs = DamonAttrs()
self.monitoring_attrs = monitoring_attrs
self.monitoring_attrs.context = self
+ if targets is None:
+ targets = []
self.targets = targets
for idx, target in enumerate(self.targets):
target.idx = idx
target.context = self
+ if schemes is None:
+ schemes = []
self.schemes = schemes
for idx, scheme in enumerate(self.schemes):
scheme.idx = idx
@@ -692,12 +698,14 @@ class DamonCtx:
class Kdamond:
state = None
pid = None
+ refresh_ms = None
contexts = None
idx = None # index of this kdamond between siblings
kdamonds = None # parent
- def __init__(self, contexts=[]):
+ def __init__(self, contexts=[], refresh_ms=None):
self.contexts = contexts
+ self.refresh_ms = refresh_ms
for idx, context in enumerate(self.contexts):
context.idx = idx
context.kdamond = self
@@ -720,6 +728,11 @@ class Kdamond:
err = context.stage()
if err is not None:
return err
+ if self.refresh_ms is not None:
+ err = write_file(os.path.join(self.sysfs_dir(), 'refresh_ms'),
+ '%d' % self.refresh_ms)
+ if err is not None:
+ return err
err = write_file(os.path.join(self.sysfs_dir(), 'state'), 'on')
if err is not None:
return err
@@ -831,7 +844,7 @@ class Kdamond:
for goal in scheme.quota.goals:
err = goal.stage()
if err is not None:
- print('commit_schemes_quota_goals failed stagign: %s'%
+ print('commit_schemes_quota_goals failed staging: %s'%
err)
exit(1)
return write_file(os.path.join(self.sysfs_dir(), 'state'),
diff --git a/tools/testing/selftests/damon/damos_apply_interval.py b/tools/testing/selftests/damon/damos_apply_interval.py
index f04d43702481..0f2f36584e48 100755
--- a/tools/testing/selftests/damon/damos_apply_interval.py
+++ b/tools/testing/selftests/damon/damos_apply_interval.py
@@ -56,7 +56,7 @@ def main():
# Because the second scheme was having the apply interval that is ten times
# lower than that of the first scheme, the second scheme should be tried
# about ten times more frequently than the first scheme. For possible
- # timing errors, check if it was at least nine times more freuqnetly tried.
+ # timing errors, check if it was at least nine times more frequently tried.
ratio = nr_tried_stats[1] / nr_tried_stats[0]
if ratio < 9:
print('%d / %d = %f (< 9)' %
diff --git a/tools/testing/selftests/damon/damos_quota_goal.py b/tools/testing/selftests/damon/damos_quota_goal.py
index f76e0412b564..661e4ba4765a 100755
--- a/tools/testing/selftests/damon/damos_quota_goal.py
+++ b/tools/testing/selftests/damon/damos_quota_goal.py
@@ -66,7 +66,7 @@ def main():
# effective quota was already minimum that cannot be more reduced
if expect_increase is False and last_effective_bytes == 1:
continue
- print('efective bytes not changed: %d' % goal.effective_bytes)
+ print('effective bytes not changed: %d' % goal.effective_bytes)
exit(1)
increased = last_effective_bytes < goal.effective_bytes
diff --git a/tools/testing/selftests/damon/damos_tried_regions.py b/tools/testing/selftests/damon/damos_tried_regions.py
index 3b347eb28bd2..d6472e6a6e08 100755
--- a/tools/testing/selftests/damon/damos_tried_regions.py
+++ b/tools/testing/selftests/damon/damos_tried_regions.py
@@ -55,10 +55,10 @@ def main():
collected_nr_regions.sort()
sample = collected_nr_regions[4]
print('50-th percentile nr_regions: %d' % sample)
- print('expectation (>= 14) is %s' % 'met' if sample >= 14 else 'not met')
+ print('expectation (>= 14) is %s' % ('met' if sample >= 14 else 'not met'))
if collected_nr_regions[4] < 14:
print('full nr_regions:')
- print('\n'.join(collected_nr_regions))
+ print('\n'.join(['%d' % x for x in collected_nr_regions]))
exit(1)
if __name__ == '__main__':
diff --git a/tools/testing/selftests/damon/drgn_dump_damon_status.py b/tools/testing/selftests/damon/drgn_dump_damon_status.py
index 972948e6215f..09552e91bc78 100755
--- a/tools/testing/selftests/damon/drgn_dump_damon_status.py
+++ b/tools/testing/selftests/damon/drgn_dump_damon_status.py
@@ -59,7 +59,6 @@ def region_to_dict(region):
['ar', addr_range_to_dict],
['sampling_addr', int],
['nr_accesses', int],
- ['nr_accesses_bp', int],
['age', int],
])
@@ -163,7 +162,7 @@ def damos_filter_to_dict(damos_filter):
int(damos_filter.addr_range.end)]
elif type_ == 'target':
dict_['target_idx'] = int(damos_filter.target_idx)
- elif type_ == 'hugeapge_size':
+ elif type_ == 'hugepage_size':
dict_['sz_range'] = [int(damos_filter.sz_range.min),
int(damos_filter.sz_range.max)]
return dict_
diff --git a/tools/testing/selftests/damon/sysfs.py b/tools/testing/selftests/damon/sysfs.py
index aa03a1187489..3ffa054b6386 100755
--- a/tools/testing/selftests/damon/sysfs.py
+++ b/tools/testing/selftests/damon/sysfs.py
@@ -119,7 +119,7 @@ def assert_access_pattern_committed(pattern, dump):
'max_nr_accesses', dump)
assert_true(dump['min_age_region'] == pattern.age[0], 'min_age_region',
dump)
- assert_true(dump['max_age_region'] == pattern.age[1], 'miaxage_region',
+ assert_true(dump['max_age_region'] == pattern.age[1], 'max_age_region',
dump)
def assert_scheme_committed(scheme, dump):
@@ -129,7 +129,7 @@ def assert_scheme_committed(scheme, dump):
'cold': 1,
'pageout': 2,
'hugepage': 3,
- 'nohugeapge': 4,
+ 'nohugepage': 4,
'collapse': 5,
'lru_prio': 6,
'lru_deprio': 7,
@@ -250,6 +250,35 @@ def assert_ctxs_committed(kdamonds):
if ctx in ctxs_paused_for_dump:
ctx.pause = False
+def test_memcg_filter_memcg_path_staging():
+ global kdamonds
+ memcg_filter = _damon_sysfs.DamosFilter(
+ type_='memcg', matching=True, allow=True, memcg_path='/')
+ kdamonds = _damon_sysfs.Kdamonds(
+ [_damon_sysfs.Kdamond(
+ contexts=[_damon_sysfs.DamonCtx(
+ targets=[_damon_sysfs.DamonTarget(pid=-1)],
+ schemes=[_damon_sysfs.Damos(
+ ops_filters=[memcg_filter])],
+ )])])
+ kdamonds.start()
+
+ shown, rd_err = _damon_sysfs.read_file(
+ os.path.join(memcg_filter.sysfs_dir(), 'memcg_path'))
+ if rd_err is not None:
+ print('memcg_path staging: sysfs read (%s)' % rd_err)
+ kdamonds.stop()
+ exit(1)
+ if shown.rstrip('\n') != memcg_filter.memcg_path:
+ print('memcg_path staging: memcg_path readback '
+ '(shown=%s, expected=%s)' %
+ (shown.rstrip('\n'), memcg_filter.memcg_path))
+ kdamonds.stop()
+ exit(1)
+
+ kdamonds.stop()
+ kdamonds = None
+
def main():
global kdamonds
kdamonds = _damon_sysfs.Kdamonds(
@@ -356,5 +385,7 @@ def main():
assert_ctxs_committed(kdamonds)
kdamonds.stop()
+ test_memcg_filter_memcg_path_staging()
+
if __name__ == '__main__':
main()
diff --git a/tools/testing/selftests/damon/sysfs.sh b/tools/testing/selftests/damon/sysfs.sh
index 78f4badb5beb..f7fb94b84e71 100755
--- a/tools/testing/selftests/damon/sysfs.sh
+++ b/tools/testing/selftests/damon/sysfs.sh
@@ -3,7 +3,7 @@
source _common.sh
-# Kselftest frmework requirement - SKIP code is 4.
+# Kselftest framework requirement - SKIP code is 4.
ksft_skip=4
ensure_write_succ()
@@ -28,7 +28,7 @@ ensure_write_fail()
if (echo "$content" > "$file") 2> /dev/null
then
- echo "writing $content to $file succeed ($fail_reason)"
+ echo "writing $content to $file succeeded ($reason)"
echo "expected failure because $reason"
exit 1
fi
@@ -67,7 +67,7 @@ ensure_file()
echo "$file permission: expected $permission but $perm"
exit 1
fi
- elif [ "$to_ensure" = "not_exist" ] && [ -f "$dir" ]
+ elif [ "$to_ensure" = "not_exist" ] && [ -f "$file" ]
then
echo "$file is not expected but found"
exit 1
@@ -99,14 +99,55 @@ test_stats()
done
}
+test_dest()
+{
+ dest_dir=$1
+ ensure_file "$dest_dir/id" "exist" "600"
+ ensure_file "$dest_dir/weight" "exist" "600"
+}
+
+test_dests()
+{
+ dests_dir=$1
+ ensure_file "$dests_dir/nr_dests" "exist" "600"
+ ensure_write_succ "$dests_dir/nr_dests" "1" "valid input"
+ test_dest "$dests_dir/0"
+
+ ensure_write_succ "$dests_dir/nr_dests" "2" "valid input"
+ test_dest "$dests_dir/0"
+ test_dest "$dests_dir/1"
+
+ ensure_write_succ "$dests_dir/nr_dests" "0" "valid input"
+ ensure_dir "$dests_dir/0" "not_exist"
+ ensure_dir "$dests_dir/1" "not_exist"
+}
+
test_filter()
{
filter_dir=$1
ensure_file "$filter_dir/type" "exist" "600"
- ensure_write_succ "$filter_dir/type" "anon" "valid input"
- ensure_write_succ "$filter_dir/type" "memcg" "valid input"
- ensure_write_succ "$filter_dir/type" "addr" "valid input"
- ensure_write_succ "$filter_dir/type" "target" "valid input"
+
+ local dir_name=$(basename "$(dirname "$filter_dir")")
+ if [ "$dir_name" = "filters" ] || [ "$dir_name" = "ops_filters" ]
+ then
+ ensure_write_succ "$filter_dir/type" "anon" "valid input"
+ ensure_write_succ "$filter_dir/type" "memcg" "valid input"
+ fi
+ if [ "$dir_name" = "filters" ] || [ "$dir_name" = "core_filters" ]
+ then
+ ensure_write_succ "$filter_dir/type" "addr" "valid input"
+ ensure_write_succ "$filter_dir/type" "target" "valid input"
+ fi
+ if [ "$dir_name" = "core_filters" ]
+ then
+ ensure_write_fail "$filter_dir/type" "anon" "ops type"
+ ensure_write_fail "$filter_dir/type" "memcg" "ops type"
+ fi
+ if [ "$dir_name" = "ops_filters" ]
+ then
+ ensure_write_fail "$filter_dir/type" "addr" "core type"
+ ensure_write_fail "$filter_dir/type" "target" "core type"
+ fi
ensure_write_fail "$filter_dir/type" "foo" "invalid input"
ensure_file "$filter_dir/matching" "exist" "600"
ensure_file "$filter_dir/memcg_path" "exist" "600"
@@ -158,6 +199,20 @@ test_goal()
ensure_dir "$goal_dir" "exist"
ensure_file "$goal_dir/target_value" "exist" "600"
ensure_file "$goal_dir/current_value" "exist" "600"
+ ensure_file "$goal_dir/target_metric" "exist" "600"
+ local fpath="$goal_dir/target_metric"
+ ensure_write_succ "$fpath" "user_input" "valid input"
+ ensure_write_succ "$fpath" "some_mem_psi_us" "valid input"
+ ensure_write_succ "$fpath" "node_mem_used_bp" "valid input"
+ ensure_write_succ "$fpath" "node_mem_free_bp" "valid input"
+ ensure_write_succ "$fpath" "node_memcg_used_bp" "valid input"
+ ensure_write_succ "$fpath" "node_memcg_free_bp" "valid input"
+ ensure_write_succ "$fpath" "active_mem_bp" "valid input"
+ ensure_write_succ "$fpath" "inactive_mem_bp" "valid input"
+ ensure_write_succ "$fpath" "node_eligible_mem_bp" "valid input"
+ ensure_write_fail "$fpath" "foo" "invalid input"
+ ensure_file "$goal_dir/nid" "exist" "600"
+ ensure_file "$goal_dir/path" "exist" "600"
}
test_goals()
@@ -207,7 +262,10 @@ test_scheme()
ensure_file "$scheme_dir/apply_interval_us" "exist" "600"
test_quotas "$scheme_dir/quotas"
test_watermarks "$scheme_dir/watermarks"
+ test_dests "$scheme_dir/dests"
test_filters "$scheme_dir/filters"
+ test_filters "$scheme_dir/core_filters"
+ test_filters "$scheme_dir/ops_filters"
test_stats "$scheme_dir/stats"
test_tried_regions "$scheme_dir/tried_regions"
}
@@ -346,8 +404,13 @@ test_probes()
ensure_write_succ "$probes_dir/nr_probes" "1" "valid input"
test_probe "$probes_dir/0"
+ ensure_write_succ "$probes_dir/nr_probes" "2" "valid input"
+ test_probe "$probes_dir/0"
+ test_probe "$probes_dir/1"
+
ensure_write_succ "$probes_dir/nr_probes" "0" "valid input"
ensure_dir "$probes_dir/0" "not_exist"
+ ensure_dir "$probes_dir/1" "not_exist"
}
test_monitoring_attrs()
@@ -363,7 +426,7 @@ test_context()
{
context_dir=$1
ensure_dir "$context_dir" "exist"
- ensure_file "$context_dir/avail_operations" "exit" 400
+ ensure_file "$context_dir/avail_operations" "exist" 400
ensure_file "$context_dir/operations" "exist" 600
ensure_file "$context_dir/addr_unit" "exist" 600
ensure_file "$context_dir/pause" "exist" 600
diff --git a/tools/testing/selftests/damon/sysfs_refresh.py b/tools/testing/selftests/damon/sysfs_refresh.py
new file mode 100755
index 000000000000..012b7e8f509f
--- /dev/null
+++ b/tools/testing/selftests/damon/sysfs_refresh.py
@@ -0,0 +1,75 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+
+import os
+import subprocess
+import time
+
+import _damon_sysfs
+
+def main():
+ # Continuously access a memory region for far longer than the test needs,
+ # so the kdamond always has a live target to monitor while we poll.
+ sz_region = 10 * 1024 * 1024
+ proc = subprocess.Popen(
+ ['./access_memory', '1', '%d' % sz_region, '60000', 'repeat'])
+
+ # A 'stat' scheme with the default (maximally wide) access pattern matches
+ # every monitored region, so its 'nr_tried' stat increases as the kdamond
+ # runs. refresh_ms should make DAMON update the schemes' stats files under
+ # sysfs on its own, without a manual 'update_schemes_stats' request.
+ kdamond = _damon_sysfs.Kdamond(
+ refresh_ms=100,
+ contexts=[_damon_sysfs.DamonCtx(
+ ops='vaddr',
+ targets=[_damon_sysfs.DamonTarget(pid=proc.pid)],
+ schemes=[_damon_sysfs.Damos(action='stat')],
+ )])
+ kdamonds = _damon_sysfs.Kdamonds([kdamond])
+
+ err = kdamonds.start()
+ if err is not None:
+ # Kernels older than the refresh_ms feature have no such file; treat
+ # that as unsupported rather than a failure.
+ if not os.path.exists(os.path.join(kdamond.sysfs_dir(), 'refresh_ms')):
+ proc.terminate()
+ proc.wait()
+ print('kdamond has no refresh_ms file; skipping')
+ exit(_damon_sysfs.ksft_skip)
+ proc.terminate()
+ proc.wait()
+ print('kdamond start failed: %s' % err)
+ exit(1)
+
+ scheme = kdamond.contexts[0].schemes[0]
+ nr_tried_path = os.path.join(scheme.sysfs_dir(), 'stats', 'nr_tried')
+
+ try:
+ # Poll the stat file directly. We never request an update (e.g.
+ # 'update_schemes_stats'), so 'nr_tried' can become non-zero only
+ # through the periodic refresh that refresh_ms enables.
+ nr_tried = 0
+ deadline = time.monotonic() + 10
+ while time.monotonic() < deadline:
+ if proc.poll() is not None:
+ print('the access_memory target exited unexpectedly')
+ exit(1)
+ content, err = _damon_sysfs.read_file(nr_tried_path)
+ if err is not None:
+ print('reading %s failed: %s' % (nr_tried_path, err))
+ exit(1)
+ nr_tried = int(content)
+ if nr_tried > 0:
+ break
+ time.sleep(0.1)
+ finally:
+ kdamonds.stop()
+ proc.terminate()
+ proc.wait()
+
+ if nr_tried == 0:
+ print('refresh_ms did not auto-update the schemes stats')
+ exit(1)
+
+if __name__ == '__main__':
+ main()
diff --git a/tools/testing/selftests/damon/sysfs_update_schemes_tried_regions_wss_estimation.py b/tools/testing/selftests/damon/sysfs_update_schemes_tried_regions_wss_estimation.py
index 35c724a63f6c..16fdc6e7fc56 100755
--- a/tools/testing/selftests/damon/sysfs_update_schemes_tried_regions_wss_estimation.py
+++ b/tools/testing/selftests/damon/sysfs_update_schemes_tried_regions_wss_estimation.py
@@ -7,7 +7,7 @@ import time
import _damon_sysfs
def pass_wss_estimation(sz_region):
- # access two regions of given size, 2 seocnds per each region
+ # access two regions of given size, 2 seconds per each region
proc = subprocess.Popen(
['./access_memory', '2', '%d' % sz_region, '2000', 'repeat'])
kdamonds = _damon_sysfs.Kdamonds([_damon_sysfs.Kdamond(