diff options
Diffstat (limited to 'tools/testing/selftests/damon')
| -rw-r--r-- | tools/testing/selftests/damon/Makefile | 1 | ||||
| -rw-r--r-- | tools/testing/selftests/damon/_damon_sysfs.py | 56 | ||||
| -rw-r--r-- | tools/testing/selftests/damon/config | 1 | ||||
| -rwxr-xr-x | tools/testing/selftests/damon/damos_apply_interval.py | 2 | ||||
| -rwxr-xr-x | tools/testing/selftests/damon/damos_quota_goal.py | 2 | ||||
| -rwxr-xr-x | tools/testing/selftests/damon/damos_tried_regions.py | 4 | ||||
| -rwxr-xr-x | tools/testing/selftests/damon/drgn_dump_damon_status.py | 7 | ||||
| -rwxr-xr-x | tools/testing/selftests/damon/sysfs.py | 102 | ||||
| -rwxr-xr-x | tools/testing/selftests/damon/sysfs.sh | 141 | ||||
| -rwxr-xr-x | tools/testing/selftests/damon/sysfs_refresh.py | 75 | ||||
| -rwxr-xr-x | tools/testing/selftests/damon/sysfs_update_schemes_tried_regions_wss_estimation.py | 2 |
11 files changed, 365 insertions, 28 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 748778b563cd..e6a2265d721e 100644 --- a/tools/testing/selftests/damon/_damon_sysfs.py +++ b/tools/testing/selftests/damon/_damon_sysfs.py @@ -130,13 +130,17 @@ class DamosQuota: sz = None # size quota, in bytes ms = None # time quota goals = None # quota goals + goal_tuner = None # quota goal tuner reset_interval_ms = None # quota reset interval + fail_charge_num = None + fail_charge_denom = None weight_sz_permil = None weight_nr_accesses_permil = None weight_age_permil = None scheme = None # owner scheme - def __init__(self, sz=0, ms=0, goals=None, reset_interval_ms=0, + def __init__(self, sz=0, ms=0, goals=None, goal_tuner='consist', + reset_interval_ms=0, fail_charge_num=0, fail_charge_denom=0, weight_sz_permil=0, weight_nr_accesses_permil=0, weight_age_permil=0): self.sz = sz @@ -146,9 +150,12 @@ class DamosQuota: self.weight_nr_accesses_permil = weight_nr_accesses_permil self.weight_age_permil = weight_age_permil self.goals = goals if goals is not None else [] + self.goal_tuner = goal_tuner for idx, goal in enumerate(self.goals): goal.idx = idx goal.quota = self + self.fail_charge_num = fail_charge_num + self.fail_charge_denom = fail_charge_denom def sysfs_dir(self): return os.path.join(self.scheme.sysfs_dir(), 'quotas') @@ -191,6 +198,22 @@ class DamosQuota: err = goal.stage() if err is not None: return err + err = write_file( + os.path.join(self.sysfs_dir(), 'goal_tuner'), self.goal_tuner) + if err is not None: + return err + + err = write_file( + os.path.join(self.sysfs_dir(), 'fail_charge_num'), + self.fail_charge_num) + if err is not None: + return err + err = write_file( + os.path.join(self.sysfs_dir(), 'fail_charge_denom'), + self.fail_charge_denom) + if err is not None: + return err + return None class DamosWatermarks: @@ -248,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 @@ -598,24 +621,33 @@ class DamonCtx: targets = None schemes = None kdamond = None + pause = None idx = None - def __init__(self, ops='paddr', monitoring_attrs=DamonAttrs(), targets=[], - schemes=[]): + 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 scheme.context = self + self.pause=pause + def sysfs_dir(self): return os.path.join(self.kdamond.sysfs_dir(), 'contexts', '%d' % self.idx) @@ -656,17 +688,24 @@ class DamonCtx: err = scheme.stage() if err is not None: return err + + err = write_file(os.path.join(self.sysfs_dir(), 'pause'), self.pause) + if err is not None: + return err + return None 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 @@ -689,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 @@ -800,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/config b/tools/testing/selftests/damon/config index a68a9fead5dc..6304adacb741 100644 --- a/tools/testing/selftests/damon/config +++ b/tools/testing/selftests/damon/config @@ -4,3 +4,4 @@ CONFIG_DAMON_PADDR=y CONFIG_DAMON_VADDR=y CONFIG_DAMON_RECLAIM=y CONFIG_DAMON_LRU_SORT=y +CONFIG_DAMON_DEBUG_SANITY=y 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 5374d18d1fa8..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], ]) @@ -110,7 +109,10 @@ def damos_quota_to_dict(quota): ['reset_interval', int], ['ms', int], ['sz', int], ['goals', damos_quota_goals_to_list], + ['goal_tuner', int], ['esz', int], + ['fail_charge_num', int], + ['fail_charge_denom', int], ['weight_sz', int], ['weight_nr_accesses', int], ['weight_age', int], @@ -160,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_ @@ -199,6 +201,7 @@ def damon_ctx_to_dict(ctx): ['attrs', attrs_to_dict], ['adaptive_targets', targets_to_list], ['schemes', schemes_to_list], + ['pause', bool], ]) def main(): diff --git a/tools/testing/selftests/damon/sysfs.py b/tools/testing/selftests/damon/sysfs.py index 9cca71eb0325..3ffa054b6386 100755 --- a/tools/testing/selftests/damon/sysfs.py +++ b/tools/testing/selftests/damon/sysfs.py @@ -24,9 +24,12 @@ def dump_damon_status_dict(pid): except Exception as e: return None, 'json.load fail (%s)' % e +kdamonds = None def fail(expectation, status): print('unexpected %s' % expectation) print(json.dumps(status, indent=4)) + if kdamonds is not None: + kdamonds.stop() exit(1) def assert_true(condition, expectation, status): @@ -67,6 +70,16 @@ def assert_quota_committed(quota, dump): assert_true(dump['sz'] == quota.sz, 'sz', dump) for idx, qgoal in enumerate(quota.goals): assert_quota_goal_committed(qgoal, dump['goals'][idx]) + tuner_val = { + 'consist': 0, + 'temporal': 1, + } + assert_true(dump['goal_tuner'] == tuner_val[quota.goal_tuner], + 'goal_tuner', dump) + assert_true(dump['fail_charge_num'] == quota.fail_charge_num, + 'fail_charge_num', dump) + assert_true(dump['fail_charge_denom'] == quota.fail_charge_denom, + 'fail_charge_denom', dump) assert_true(dump['weight_sz'] == quota.weight_sz_permil, 'weight_sz', dump) assert_true(dump['weight_nr_accesses'] == quota.weight_nr_accesses_permil, 'weight_nr_accesses', dump) @@ -106,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): @@ -116,12 +129,13 @@ def assert_scheme_committed(scheme, dump): 'cold': 1, 'pageout': 2, 'hugepage': 3, - 'nohugeapge': 4, - 'lru_prio': 5, - 'lru_deprio': 6, - 'migrate_hot': 7, - 'migrate_cold': 8, - 'stat': 9, + 'nohugepage': 4, + 'collapse': 5, + 'lru_prio': 6, + 'lru_deprio': 7, + 'migrate_hot': 8, + 'migrate_cold': 9, + 'stat': 10, } assert_true(dump['action'] == action_val[scheme.action], 'action', dump) assert_true(dump['apply_interval_us'] == scheme. apply_interval_us, @@ -184,21 +198,89 @@ def assert_ctx_committed(ctx, dump): assert_monitoring_attrs_committed(ctx.monitoring_attrs, dump['attrs']) assert_monitoring_targets_committed(ctx.targets, dump['adaptive_targets']) assert_schemes_committed(ctx.schemes, dump['schemes']) + assert_true(dump['pause'] == ctx.pause, 'pause', dump) def assert_ctxs_committed(kdamonds): + ctxs_paused_for_dump = [] + kdamonds_paused_for_dump = [] + # pause for safe state dumping + for kd in kdamonds.kdamonds: + for ctx in kd.contexts: + if ctx.pause is False: + ctx.pause = True + ctxs_paused_for_dump.append(ctx) + if not kd in kdamonds_paused_for_dump: + kdamonds_paused_for_dump.append(kd) + if kd in kdamonds_paused_for_dump: + err = kd.commit() + if err is not None: + print('pause fail (%s)' % err) + kdamonds.stop() + exit(1) + status, err = dump_damon_status_dict(kdamonds.kdamonds[0].pid) if err is not None: print(err) kdamonds.stop() exit(1) + # resume contexts paused for safe state dumping + for ctx in ctxs_paused_for_dump: + ctx.pause = False + for kd in kdamonds_paused_for_dump: + err = kd.commit() + if err is not None: + print('resume fail (%s)' % err) + kdamonds.stop() + exit(1) + + # restore for comparison + for ctx in ctxs_paused_for_dump: + ctx.pause = True + ctxs = kdamonds.kdamonds[0].contexts dump = status['contexts'] assert_true(len(ctxs) == len(dump), 'ctxs length', dump) for idx, ctx in enumerate(ctxs): assert_ctx_committed(ctx, dump[idx]) + # restore for the caller + for kd in kdamonds.kdamonds: + for ctx in kd.contexts: + 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( [_damon_sysfs.Kdamond( contexts=[_damon_sysfs.DamonCtx( @@ -231,7 +313,10 @@ def main(): metric='node_mem_used_bp', target_value=9950, nid=1)], + goal_tuner='temporal', reset_interval_ms=1500, + fail_charge_num=1, + fail_charge_denom=4096, weight_sz_permil=20, weight_nr_accesses_permil=200, weight_age_permil=1000), @@ -294,10 +379,13 @@ def main(): print('kdamond start failed: %s' % err) exit(1) kdamonds.kdamonds[0].contexts[0].targets[1].obsolete = True + kdamonds.kdamonds[0].contexts[0].pause = True kdamonds.kdamonds[0].commit() del kdamonds.kdamonds[0].contexts[0].targets[1] 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 83e3b7f63d81..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" } @@ -282,6 +340,17 @@ test_targets() ensure_dir "$targets_dir/1" "not_exist" } + +test_intervals_goal() +{ + goal_dir=$1 + ensure_dir "$goal_dir" "exist" + ensure_file "$goal_dir/access_bp" "exist" "600" + ensure_file "$goal_dir/aggrs" "exist" "600" + ensure_file "$goal_dir/min_sample_us" "exist" "600" + ensure_file "$goal_dir/max_sample_us" "exist" "600" +} + test_intervals() { intervals_dir=$1 @@ -289,6 +358,59 @@ test_intervals() ensure_file "$intervals_dir/aggr_us" "exist" "600" ensure_file "$intervals_dir/sample_us" "exist" "600" ensure_file "$intervals_dir/update_us" "exist" "600" + test_intervals_goal "$intervals_dir/intervals_goal" +} + +test_damon_filter() +{ + damon_filter_dir=$1 + ensure_file "$damon_filter_dir/type" "exist" "600" + ensure_write_succ "$damon_filter_dir/type" "anon" "valid input" + ensure_write_fail "$damon_filter_dir/type" "foo" "invalid input" + ensure_file "$damon_filter_dir/matching" "exist" "600" + ensure_file "$damon_filter_dir/allow" "exist" "600" +} + +test_damon_filters() +{ + filters_dir=$1 + ensure_dir "$filters_dir" "exist" + ensure_file "$filters_dir/nr_filters" "exist" "600" + ensure_write_succ "$filters_dir/nr_filters" "1" "valid input" + test_damon_filter "$filters_dir/0" + + ensure_write_succ "$filters_dir/nr_filters" "2" "valid input" + test_damon_filter "$filters_dir/0" + test_damon_filter "$filters_dir/1" + + ensure_write_succ "$filters_dir/nr_filters" "0" "valid input" + ensure_dir "$filters_dir/0" "not_exist" + ensure_dir "$filters_dir/1" "not_exist" +} + +test_probe() +{ + probe_dir=$1 + ensure_dir "$probe_dir" "exist" + test_damon_filters "$probe_dir/filters" +} + +test_probes() +{ + probes_dir=$1 + ensure_dir "$probes_dir" "exist" + ensure_file "$probes_dir/nr_probes" "exist" "600" + + 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() @@ -296,6 +418,7 @@ test_monitoring_attrs() monitoring_attrs_dir=$1 ensure_dir "$monitoring_attrs_dir" "exist" test_intervals "$monitoring_attrs_dir/intervals" + test_probes "$monitoring_attrs_dir/probes" test_range "$monitoring_attrs_dir/nr_regions" } @@ -303,8 +426,10 @@ 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 test_monitoring_attrs "$context_dir/monitoring_attrs" test_targets "$context_dir/targets" test_schemes "$context_dir/schemes" 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( |
