sysfs.py tests if DAMON sysfs interface is passing the user-requested parameters to DAMON as expected. But only the default (minimum) parameters are being tested. This is partially because _damon_sysfs.py, which is the library for making the parameter requests, is not supporting the entire parameters. The internal DAMON status dump script (drgn_dump_damon_status.py) is also not dumping entire parameters. Extend the test coverage by updating parameters input and status dumping scripts to support all parameters, and writing additional tests using those.
This increased test coverage actually found one real bug (https://lore.kernel.org/20250719181932.72944-1-sj@kernel.org).
First seven patches (1-7) extend _damon_sysfs.py for all parameters setup. The eight patch (8) fixes _damon_sysfs.py to use correct max nr_acceses and age values for their type. Following three patches (9-11) extend drgn_dump_damon_status.py to dump full DAMON parameters. Following nine patches (12-20) refactor sysfs.py for general testing code reuse, and extend it for full parameters check. Finally, two patches (21 and 22) add test cases in sysfs.py for full parameters testing.
SeongJae Park (22): selftests/damon/_damon_sysfs: support DAMOS watermarks setup selftests/damon/_damon_sysfs: support DAMOS filters setup selftests/damon/_damon_sysfs: support monitoring intervals goal setup selftests/damon/_damon_sysfs: support DAMOS quota weights setup selftests/damon/_damon_sysfs: support DAMOS quota goal nid setup selftests/damon/_damon_sysfs: support DAMOS action dests setup selftests/damon/_damon_sysfs: support DAMOS target_nid setup selftests/damon/_damon_sysfs: use 2**32 - 1 as max nr_accesses and age selftests/damon/drgn_dump_damon_status: dump damos->migrate_dests selftests/damon/drgn_dump_damon_status: dump ctx->ops.id selftests/damon/drgn_dump_damon_status: dump DAMOS filters selftests/damon/sysfs.py: generalize DAMOS Watermarks commit assertion selftests/damon/sysfs.py: generalize DamosQuota commit assertion selftests/damon/sysfs.py: test quota goal commitment selftests/damon/sysfs.py: test DAMOS destinations commitment selftests/damon/sysfs.py: generalize DAMOS schemes commit assertion selftests/damon/sysfs.py: test DAMOS filters commitment selftests/damon/sysfs.py: generalize DAMOS schemes commit assertion selftests/damon/sysfs.py: generalize monitoring attributes commit assertion selftests/damon/sysfs.py: generalize DAMON context commit assertion selftests/damon/sysfs.py: test non-default parameters runtime commit selftests/damon/sysfs.py: test runtime reduction of DAMON parameters
tools/testing/selftests/damon/_damon_sysfs.py | 301 +++++++++++++++++- .../selftests/damon/drgn_dump_damon_status.py | 63 +++- tools/testing/selftests/damon/sysfs.py | 284 +++++++++++++---- 3 files changed, 568 insertions(+), 80 deletions(-)
base-commit: fc8066077f44a4fd43f8fdb12bc238f8fbeaa3c5
_damon_sysfs.py contains code for test-purpose DAMON sysfs interface control. Add support of DAMOS watermarks setup for more tests.
Signed-off-by: SeongJae Park sj@kernel.org --- tools/testing/selftests/damon/_damon_sysfs.py | 46 +++++++++++++++++-- 1 file changed, 42 insertions(+), 4 deletions(-)
diff --git a/tools/testing/selftests/damon/_damon_sysfs.py b/tools/testing/selftests/damon/_damon_sysfs.py index f587e117472e..d81aa11e3d32 100644 --- a/tools/testing/selftests/damon/_damon_sysfs.py +++ b/tools/testing/selftests/damon/_damon_sysfs.py @@ -165,6 +165,42 @@ class DamosQuota: return err return None
+class DamosWatermarks: + metric = None + interval = None + high = None + mid = None + low = None + scheme = None # owner scheme + + def __init__(self, metric='none', interval=0, high=0, mid=0, low=0): + self.metric = metric + self.interval = interval + self.high = high + self.mid = mid + self.low = low + + def sysfs_dir(self): + return os.path.join(self.scheme.sysfs_dir(), 'watermarks') + + def stage(self): + err = write_file(os.path.join(self.sysfs_dir(), 'metric'), self.metric) + if err is not None: + return err + err = write_file(os.path.join(self.sysfs_dir(), 'interval_us'), + self.interval) + if err is not None: + return err + err = write_file(os.path.join(self.sysfs_dir(), 'high'), self.high) + if err is not None: + return err + err = write_file(os.path.join(self.sysfs_dir(), 'mid'), self.mid) + if err is not None: + return err + err = write_file(os.path.join(self.sysfs_dir(), 'low'), self.low) + if err is not None: + return err + class DamosStats: nr_tried = None sz_tried = None @@ -190,6 +226,7 @@ class Damos: action = None access_pattern = None quota = None + watermarks = None apply_interval_us = None # todo: Support watermarks, stats idx = None @@ -199,12 +236,15 @@ class Damos: tried_regions = None
def __init__(self, action='stat', access_pattern=DamosAccessPattern(), - quota=DamosQuota(), apply_interval_us=0): + quota=DamosQuota(), watermarks=DamosWatermarks(), + apply_interval_us=0): self.action = action self.access_pattern = access_pattern self.access_pattern.scheme = self self.quota = quota self.quota.scheme = self + self.watermarks = watermarks + self.watermarks.scheme = self self.apply_interval_us = apply_interval_us
def sysfs_dir(self): @@ -227,9 +267,7 @@ class Damos: if err is not None: return err
- # disable watermarks - err = write_file( - os.path.join(self.sysfs_dir(), 'watermarks', 'metric'), 'none') + err = self.watermarks.stage() if err is not None: return err
_damon_sysfs.py contains code for test-purpose DAMON sysfs interface control. Add support of DAMOS filters setup for more tests.
Signed-off-by: SeongJae Park sj@kernel.org --- tools/testing/selftests/damon/_damon_sysfs.py | 115 +++++++++++++++++- 1 file changed, 111 insertions(+), 4 deletions(-)
diff --git a/tools/testing/selftests/damon/_damon_sysfs.py b/tools/testing/selftests/damon/_damon_sysfs.py index d81aa11e3d32..f853af6ad926 100644 --- a/tools/testing/selftests/damon/_damon_sysfs.py +++ b/tools/testing/selftests/damon/_damon_sysfs.py @@ -201,6 +201,96 @@ class DamosWatermarks: if err is not None: return err
+class DamosFilter: + type_ = None + matching = None + allow = None + memcg_path = None + addr_start = None + addr_end = None + target_idx = None + min_ = None + max_ = None + idx = None + filters = None # owner filters + + def __init__(self, type_='anon', matching=False, allow=False, + memcg_path='', addr_start=0, addr_end=0, target_idx=0, min_=0, + max_=0): + self.type_ = type_ + self.matching = matching + self.allow = allow + self.memcg_path = memcg_path, + self.addr_start = addr_start + self.addr_end = addr_end + self.target_idx = target_idx + self.min_ = min_ + self.max_ = max_ + + 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'), + self.matching) + if err is not None: + return err + err = write_file(os.path.join(self.sysfs_dir(), 'allow'), self.allow) + if err is not None: + return err + err = write_file(os.path.join(self.sysfs_dir(), 'memcg_path'), + self.memcg_path) + if err is not None: + return err + err = write_file(os.path.join(self.sysfs_dir(), 'addr_start'), + self.addr_start) + if err is not None: + return err + err = write_file(os.path.join(self.sysfs_dir(), 'addr_end'), + self.addr_end) + if err is not None: + return err + err = write_file(os.path.join(self.sysfs_dir(), 'damon_target_idx'), + self.target_idx) + if err is not None: + return err + err = write_file(os.path.join(self.sysfs_dir(), 'min'), self.min_) + if err is not None: + return err + err = write_file(os.path.join(self.sysfs_dir(), 'max'), self.max_) + if err is not None: + return err + return None + +class DamosFilters: + name = None + filters = None + scheme = None # owner scheme + + def __init__(self, name, filters=[]): + self.name = name + self.filters = filters + for idx, filter_ in enumerate(self.filters): + filter_.idx = idx + filter_.filters = self + + def sysfs_dir(self): + return os.path.join(self.scheme.sysfs_dir(), self.name) + + 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 DamosStats: nr_tried = None sz_tried = None @@ -227,8 +317,10 @@ class Damos: access_pattern = None quota = None watermarks = None + core_filters = None + ops_filters = None + filters = None apply_interval_us = None - # todo: Support watermarks, stats idx = None context = None tried_bytes = None @@ -237,6 +329,7 @@ class Damos:
def __init__(self, action='stat', access_pattern=DamosAccessPattern(), quota=DamosQuota(), watermarks=DamosWatermarks(), + core_filters=[], ops_filters=[], filters=[], apply_interval_us=0): self.action = action self.access_pattern = access_pattern @@ -245,6 +338,16 @@ class Damos: self.quota.scheme = self self.watermarks = watermarks self.watermarks.scheme = self + + self.core_filters = DamosFilters(name='core_filters', + filters=core_filters) + self.core_filters.scheme = self + self.ops_filters = DamosFilters(name='ops_filters', + filters=ops_filters) + self.ops_filters.scheme = self + self.filters = DamosFilters(name='filters', filters=filters) + self.filters.scheme = self + self.apply_interval_us = apply_interval_us
def sysfs_dir(self): @@ -271,9 +374,13 @@ class Damos: if err is not None: return err
- # disable filters - err = write_file( - os.path.join(self.sysfs_dir(), 'filters', 'nr_filters'), '0') + err = self.core_filters.stage() + if err is not None: + return err + err = self.ops_filters.stage() + if err is not None: + return err + err = self.filters.stage() if err is not None: return err
_damon_sysfs.py contains code for test-purpose DAMON sysfs interface control. Add support of the monitoring intervals auto-tune goal setup for more tests.
Signed-off-by: SeongJae Park sj@kernel.org --- tools/testing/selftests/damon/_damon_sysfs.py | 43 ++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/damon/_damon_sysfs.py b/tools/testing/selftests/damon/_damon_sysfs.py index f853af6ad926..ec6230929d36 100644 --- a/tools/testing/selftests/damon/_damon_sysfs.py +++ b/tools/testing/selftests/damon/_damon_sysfs.py @@ -405,18 +405,56 @@ class DamonTarget: return write_file( os.path.join(self.sysfs_dir(), 'pid_target'), self.pid)
+class IntervalsGoal: + access_bp = None + aggrs = None + min_sample_us = None + max_sample_us = None + attrs = None # owner DamonAttrs + + def __init__(self, access_bp=0, aggrs=0, min_sample_us=0, max_sample_us=0): + self.access_bp = access_bp + self.aggrs = aggrs + self.min_sample_us = min_sample_us + self.max_sample_us = max_sample_us + + def sysfs_dir(self): + return os.path.join(self.attrs.interval_sysfs_dir(), 'intervals_goal') + + def stage(self): + err = write_file( + os.path.join(self.sysfs_dir(), 'access_bp'), self.access_bp) + if err is not None: + return err + err = write_file(os.path.join(self.sysfs_dir(), 'aggrs'), self.aggrs) + if err is not None: + return err + err = write_file(os.path.join(self.sysfs_dir(), 'min_sample_us'), + self.min_sample_us) + if err is not None: + return err + err = write_file(os.path.join(self.sysfs_dir(), 'max_sample_us'), + self.max_sample_us) + if err is not None: + return err + return None + class DamonAttrs: sample_us = None aggr_us = None + intervals_goal = None update_us = None min_nr_regions = None max_nr_regions = None context = None
- def __init__(self, sample_us=5000, aggr_us=100000, update_us=1000000, + def __init__(self, sample_us=5000, aggr_us=100000, + intervals_goal=IntervalsGoal(), update_us=1000000, min_nr_regions=10, max_nr_regions=1000): self.sample_us = sample_us self.aggr_us = aggr_us + self.intervals_goal = intervals_goal + self.intervals_goal.attrs = self self.update_us = update_us self.min_nr_regions = min_nr_regions self.max_nr_regions = max_nr_regions @@ -436,6 +474,9 @@ class DamonAttrs: return err err = write_file(os.path.join(self.interval_sysfs_dir(), 'aggr_us'), self.aggr_us) + if err is not None: + return err + err = self.intervals_goal.stage() if err is not None: return err err = write_file(os.path.join(self.interval_sysfs_dir(), 'update_us'),
_damon_sysfs.py contains code for test-purpose DAMON sysfs interface control. Add support of DAMOS quotas prioritization weights setup for more tests.
Signed-off-by: SeongJae Park sj@kernel.org --- tools/testing/selftests/damon/_damon_sysfs.py | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/damon/_damon_sysfs.py b/tools/testing/selftests/damon/_damon_sysfs.py index ec6230929d36..12d076260b2b 100644 --- a/tools/testing/selftests/damon/_damon_sysfs.py +++ b/tools/testing/selftests/damon/_damon_sysfs.py @@ -125,12 +125,20 @@ class DamosQuota: ms = None # time quota goals = None # quota goals reset_interval_ms = None # quota reset interval + 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, reset_interval_ms=0, + weight_sz_permil=0, weight_nr_accesses_permil=0, + weight_age_permil=0): self.sz = sz self.ms = ms self.reset_interval_ms = reset_interval_ms + self.weight_sz_permil = weight_sz_permil + 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 [] for idx, goal in enumerate(self.goals): goal.idx = idx @@ -151,6 +159,20 @@ class DamosQuota: if err is not None: return err
+ err = write_file(os.path.join( + self.sysfs_dir(), 'weights', 'sz_permil'), self.weight_sz_permil) + if err is not None: + return err + err = write_file(os.path.join( + self.sysfs_dir(), 'weights', 'nr_accesses_permil'), + self.weight_nr_accesses_permil) + if err is not None: + return err + err = write_file(os.path.join( + self.sysfs_dir(), 'weights', 'age_permil'), self.weight_age_permil) + if err is not None: + return err + nr_goals_file = os.path.join(self.sysfs_dir(), 'goals', 'nr_goals') content, err = read_file(nr_goals_file) if err is not None:
_damon_sysfs.py contains code for test-purpose DAMON sysfs interface control. Add support of DAMOS quota goal nid setup for more tests.
Signed-off-by: SeongJae Park sj@kernel.org --- tools/testing/selftests/damon/_damon_sysfs.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/damon/_damon_sysfs.py b/tools/testing/selftests/damon/_damon_sysfs.py index 12d076260b2b..23de9202b4e3 100644 --- a/tools/testing/selftests/damon/_damon_sysfs.py +++ b/tools/testing/selftests/damon/_damon_sysfs.py @@ -93,14 +93,16 @@ class DamosQuotaGoal: metric = None target_value = None current_value = None + nid = None effective_bytes = None quota = None # owner quota idx = None
- def __init__(self, metric, target_value=10000, current_value=0): + def __init__(self, metric, target_value=10000, current_value=0, nid=0): self.metric = metric self.target_value = target_value self.current_value = current_value + self.nid = nid
def sysfs_dir(self): return os.path.join(self.quota.sysfs_dir(), 'goals', '%d' % self.idx) @@ -118,6 +120,10 @@ class DamosQuotaGoal: self.current_value) if err is not None: return err + err = write_file(os.path.join(self.sysfs_dir(), 'nid'), self.nid) + if err is not None: + return err + return None
class DamosQuota:
_damon_sysfs.py contains code for test-purpose DAMON sysfs interface control. Add support of DAMOS action destinations setup for more tests.
Signed-off-by: SeongJae Park sj@kernel.org --- tools/testing/selftests/damon/_damon_sysfs.py | 56 ++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/damon/_damon_sysfs.py b/tools/testing/selftests/damon/_damon_sysfs.py index 23de9202b4e3..2d95ab564885 100644 --- a/tools/testing/selftests/damon/_damon_sysfs.py +++ b/tools/testing/selftests/damon/_damon_sysfs.py @@ -319,6 +319,52 @@ class DamosFilters: return err return None
+class DamosDest: + id = None + weight = None + idx = None + dests = None # owner dests + + def __init__(self, id=0, weight=0): + self.id = id + self.weight = weight + + def sysfs_dir(self): + return os.path.join(self.dests.sysfs_dir(), '%d' % self.idx) + + def stage(self): + err = write_file(os.path.join(self.sysfs_dir(), 'id'), self.id) + if err is not None: + return err + err = write_file(os.path.join(self.sysfs_dir(), 'weight'), self.weight) + if err is not None: + return err + return None + +class DamosDests: + dests = None + scheme = None # owner scheme + + def __init__(self, dests=[]): + self.dests = dests + for idx, dest in enumerate(self.dests): + dest.idx = idx + dest.dests = self + + def sysfs_dir(self): + return os.path.join(self.scheme.sysfs_dir(), 'dests') + + def stage(self): + err = write_file(os.path.join(self.sysfs_dir(), 'nr_dests'), + len(self.dests)) + if err is not None: + return err + for dest in self.dests: + err = dest.stage() + if err is not None: + return err + return None + class DamosStats: nr_tried = None sz_tried = None @@ -349,6 +395,7 @@ class Damos: ops_filters = None filters = None apply_interval_us = None + dests = None idx = None context = None tried_bytes = None @@ -358,7 +405,7 @@ class Damos: def __init__(self, action='stat', access_pattern=DamosAccessPattern(), quota=DamosQuota(), watermarks=DamosWatermarks(), core_filters=[], ops_filters=[], filters=[], - apply_interval_us=0): + dests=DamosDests(), apply_interval_us=0): self.action = action self.access_pattern = access_pattern self.access_pattern.scheme = self @@ -376,6 +423,9 @@ class Damos: self.filters = DamosFilters(name='filters', filters=filters) self.filters.scheme = self
+ self.dests = dests + self.dests.scheme = self + self.apply_interval_us = apply_interval_us
def sysfs_dir(self): @@ -412,6 +462,10 @@ class Damos: if err is not None: return err
+ err = self.dests.stage() + if err is not None: + return err + class DamonTarget: pid = None # todo: Support target regions if test is made
_damon_sysfs.py contains code for test-purpose DAMON sysfs interface control. Add support of DAMOS action destination target_nid setup for more tests.
Signed-off-by: SeongJae Park sj@kernel.org --- tools/testing/selftests/damon/_damon_sysfs.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/damon/_damon_sysfs.py b/tools/testing/selftests/damon/_damon_sysfs.py index 2d95ab564885..70860d925503 100644 --- a/tools/testing/selftests/damon/_damon_sysfs.py +++ b/tools/testing/selftests/damon/_damon_sysfs.py @@ -395,6 +395,7 @@ class Damos: ops_filters = None filters = None apply_interval_us = None + target_nid = None dests = None idx = None context = None @@ -404,7 +405,7 @@ class Damos:
def __init__(self, action='stat', access_pattern=DamosAccessPattern(), quota=DamosQuota(), watermarks=DamosWatermarks(), - core_filters=[], ops_filters=[], filters=[], + core_filters=[], ops_filters=[], filters=[], target_nid=0, dests=DamosDests(), apply_interval_us=0): self.action = action self.access_pattern = access_pattern @@ -423,6 +424,7 @@ class Damos: self.filters = DamosFilters(name='filters', filters=filters) self.filters.scheme = self
+ self.target_nid = target_nid self.dests = dests self.dests.scheme = self
@@ -462,6 +464,11 @@ class Damos: if err is not None: return err
+ err = write_file(os.path.join(self.sysfs_dir(), 'target_nid'), '%d' % + self.target_nid) + if err is not None: + return err + err = self.dests.stage() if err is not None: return err
nr_accesses and age are unsigned int. Use the proper max value.
Signed-off-by: SeongJae Park sj@kernel.org --- tools/testing/selftests/damon/_damon_sysfs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/tools/testing/selftests/damon/_damon_sysfs.py b/tools/testing/selftests/damon/_damon_sysfs.py index 70860d925503..a0e6290833fb 100644 --- a/tools/testing/selftests/damon/_damon_sysfs.py +++ b/tools/testing/selftests/damon/_damon_sysfs.py @@ -52,9 +52,9 @@ class DamosAccessPattern: if self.size is None: self.size = [0, 2**64 - 1] if self.nr_accesses is None: - self.nr_accesses = [0, 2**64 - 1] + self.nr_accesses = [0, 2**32 - 1] if self.age is None: - self.age = [0, 2**64 - 1] + self.age = [0, 2**32 - 1]
def sysfs_dir(self): return os.path.join(self.scheme.sysfs_dir(), 'access_pattern')
drgn_dump_damon_status.py is a script for dumping DAMON internal status in json format. It is being used for seeing if DAMON parameters that are set using _damon_sysfs.py are actually passed to DAMON in the kernel space. It is, however, not dumping full DAMON internal status, and it makes increasing test coverage difficult. Add damos->migrate_dests dumping for more tests.
Signed-off-by: SeongJae Park sj@kernel.org --- .../selftests/damon/drgn_dump_damon_status.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+)
diff --git a/tools/testing/selftests/damon/drgn_dump_damon_status.py b/tools/testing/selftests/damon/drgn_dump_damon_status.py index 333a0d0c4bff..8db081f965f5 100755 --- a/tools/testing/selftests/damon/drgn_dump_damon_status.py +++ b/tools/testing/selftests/damon/drgn_dump_damon_status.py @@ -117,6 +117,19 @@ def damos_watermarks_to_dict(watermarks): ['high', int], ['mid', int], ['low', int], ])
+def damos_migrate_dests_to_dict(dests): + nr_dests = int(dests.nr_dests) + node_id_arr = [] + weight_arr = [] + for i in range(nr_dests): + node_id_arr.append(int(dests.node_id_arr[i])) + weight_arr.append(int(dests.weight_arr[i])) + return { + 'node_id_arr': node_id_arr, + 'weight_arr': weight_arr, + 'nr_dests': nr_dests, + } + def scheme_to_dict(scheme): return to_dict(scheme, [ ['pattern', damos_access_pattern_to_dict], @@ -125,6 +138,7 @@ def scheme_to_dict(scheme): ['quota', damos_quota_to_dict], ['wmarks', damos_watermarks_to_dict], ['target_nid', int], + ['migrate_dests', damos_migrate_dests_to_dict], ])
def schemes_to_list(schemes):
drgn_dump_damon_status.py is a script for dumping DAMON internal status in json format. It is being used for seeing if DAMON parameters that are set using _damon_sysfs.py are actually passed to DAMON in the kernel space. It is, however, not dumping full DAMON internal status, and it makes increasing test coverage difficult. Add ctx->ops.id dumping for more tests.
Signed-off-by: SeongJae Park sj@kernel.org --- tools/testing/selftests/damon/drgn_dump_damon_status.py | 6 ++++++ 1 file changed, 6 insertions(+)
diff --git a/tools/testing/selftests/damon/drgn_dump_damon_status.py b/tools/testing/selftests/damon/drgn_dump_damon_status.py index 8db081f965f5..cf5d492670d8 100755 --- a/tools/testing/selftests/damon/drgn_dump_damon_status.py +++ b/tools/testing/selftests/damon/drgn_dump_damon_status.py @@ -25,6 +25,11 @@ def to_dict(object, attr_name_converter): d[attr_name] = converter(getattr(object, attr_name)) return d
+def ops_to_dict(ops): + return to_dict(ops, [ + ['id', int], + ]) + def intervals_goal_to_dict(goal): return to_dict(goal, [ ['access_bp', int], @@ -148,6 +153,7 @@ def schemes_to_list(schemes):
def damon_ctx_to_dict(ctx): return to_dict(ctx, [ + ['ops', ops_to_dict], ['attrs', attrs_to_dict], ['adaptive_targets', targets_to_list], ['schemes', schemes_to_list],
drgn_dump_damon_status.py is a script for dumping DAMON internal status in json format. It is being used for seeing if DAMON parameters that are set using _damon_sysfs.py are actually passed to DAMON in the kernel space. It is, however, not dumping full DAMON internal status, and it makes increasing test coverage difficult. Add damos filters dumping for more tests.
Signed-off-by: SeongJae Park sj@kernel.org --- .../selftests/damon/drgn_dump_damon_status.py | 43 ++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/damon/drgn_dump_damon_status.py b/tools/testing/selftests/damon/drgn_dump_damon_status.py index cf5d492670d8..7233369a3a44 100755 --- a/tools/testing/selftests/damon/drgn_dump_damon_status.py +++ b/tools/testing/selftests/damon/drgn_dump_damon_status.py @@ -135,8 +135,37 @@ def damos_migrate_dests_to_dict(dests): 'nr_dests': nr_dests, }
+def damos_filter_to_dict(damos_filter): + filter_type_keyword = { + 0: 'anon', + 1: 'active', + 2: 'memcg', + 3: 'young', + 4: 'hugepage_size', + 5: 'unmapped', + 6: 'addr', + 7: 'target' + } + dict_ = { + 'type': filter_type_keyword[int(damos_filter.type)], + 'matching': bool(damos_filter.matching), + 'allow': bool(damos_filter.allow), + } + type_ = dict_['type'] + if type_ == 'memcg': + dict_['memcg_id'] = int(damos_filter.memcg_id) + elif type_ == 'addr': + dict_['addr_range'] = [int(damos_filter.addr_range.start), + int(damos_filter.addr_range.end)] + elif type_ == 'target': + dict_['target_idx'] = int(damos_filter.target_idx) + elif type_ == 'hugeapge_size': + dict_['sz_range'] = [int(damos_filter.sz_range.min), + int(damos_filter.sz_range.max)] + return dict_ + def scheme_to_dict(scheme): - return to_dict(scheme, [ + dict_ = to_dict(scheme, [ ['pattern', damos_access_pattern_to_dict], ['action', int], ['apply_interval_us', int], @@ -145,6 +174,18 @@ def scheme_to_dict(scheme): ['target_nid', int], ['migrate_dests', damos_migrate_dests_to_dict], ]) + filters = [] + for f in list_for_each_entry( + 'struct damos_filter', scheme.filters.address_of_(), 'list'): + filters.append(damos_filter_to_dict(f)) + dict_['filters'] = filters + ops_filters = [] + for f in list_for_each_entry( + 'struct damos_filter', scheme.ops_filters.address_of_(), 'list'): + ops_filters.append(damos_filter_to_dict(f)) + dict_['ops_filters'] = ops_filters + + return dict_
def schemes_to_list(schemes): return [scheme_to_dict(s)
DamosWatermarks commitment assertion is hard-coded for a specific test case. Split it out into a general version that can be reused for different test cases.
Signed-off-by: SeongJae Park sj@kernel.org --- tools/testing/selftests/damon/sysfs.py | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-)
diff --git a/tools/testing/selftests/damon/sysfs.py b/tools/testing/selftests/damon/sysfs.py index e67008fd055d..91a28d97b015 100755 --- a/tools/testing/selftests/damon/sysfs.py +++ b/tools/testing/selftests/damon/sysfs.py @@ -25,6 +25,22 @@ def fail(expectation, status): print(json.dumps(status, indent=4)) exit(1)
+def assert_true(condition, expectation, status): + if condition is not True: + fail(expectation, status) + +def assert_watermarks_committed(watermarks, dump): + wmark_metric_val = { + 'none': 0, + 'free_mem_rate': 1, + } + assert_true(dump['metric'] == wmark_metric_val[watermarks.metric], + 'metric', dump) + assert_true(dump['interval'] == watermarks.interval, 'interval', dump) + assert_true(dump['high'] == watermarks.high, 'high', dump) + assert_true(dump['mid'] == watermarks.mid, 'mid', dump) + assert_true(dump['low'] == watermarks.low, 'low', dump) + def main(): kdamonds = _damon_sysfs.Kdamonds( [_damon_sysfs.Kdamond( @@ -100,14 +116,8 @@ def main(): }: fail('damos quota', status)
- if scheme['wmarks'] != { - 'metric': 0, - 'interval': 0, - 'high': 0, - 'mid': 0, - 'low': 0, - }: - fail('damos wmarks', status) + assert_watermarks_committed(_damon_sysfs.DamosWatermarks(), + scheme['wmarks'])
kdamonds.stop()
DamosQuota commitment assertion is hard-coded for a specific test case. Split it out into a general version that can be reused for different test cases.
Signed-off-by: SeongJae Park sj@kernel.org --- tools/testing/selftests/damon/sysfs.py | 31 +++++++++++++++++--------- 1 file changed, 20 insertions(+), 11 deletions(-)
diff --git a/tools/testing/selftests/damon/sysfs.py b/tools/testing/selftests/damon/sysfs.py index 91a28d97b015..dbe9f1a3b976 100755 --- a/tools/testing/selftests/damon/sysfs.py +++ b/tools/testing/selftests/damon/sysfs.py @@ -41,6 +41,18 @@ def assert_watermarks_committed(watermarks, dump): assert_true(dump['mid'] == watermarks.mid, 'mid', dump) assert_true(dump['low'] == watermarks.low, 'low', dump)
+def assert_quota_committed(quota, dump): + assert_true(dump['reset_interval'] == quota.reset_interval_ms, + 'reset_interval', dump) + assert_true(dump['ms'] == quota.ms, 'ms', dump) + assert_true(dump['sz'] == quota.sz, 'sz', dump) + # TODO: assert goals are committed + 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) + assert_true( + dump['weight_age'] == quota.weight_age_permil, 'weight_age', dump) + def main(): kdamonds = _damon_sysfs.Kdamonds( [_damon_sysfs.Kdamond( @@ -104,18 +116,15 @@ def main(): if scheme['target_nid'] != -1: fail('damos target nid', status)
- if scheme['quota'] != { - 'reset_interval': 0, - 'ms': 0, - 'sz': 0, - 'goals': [], - 'esz': 0, - 'weight_sz': 0, - 'weight_nr_accesses': 0, - 'weight_age': 0, - }: - fail('damos quota', status) + migrate_dests = scheme['migrate_dests'] + if migrate_dests['nr_dests'] != 0: + fail('nr_dests', status) + if migrate_dests['node_id_arr'] != []: + fail('node_id_arr', status) + if migrate_dests['weight_arr'] != []: + fail('weight_arr', status)
+ assert_quota_committed(_damon_sysfs.DamosQuota(), scheme['quota']) assert_watermarks_committed(_damon_sysfs.DamosWatermarks(), scheme['wmarks'])
Current DAMOS quota commitment assertion is not testing quota goal commitment. Add the test.
Signed-off-by: SeongJae Park sj@kernel.org --- tools/testing/selftests/damon/sysfs.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/damon/sysfs.py b/tools/testing/selftests/damon/sysfs.py index dbe9f1a3b976..93f20c5c6780 100755 --- a/tools/testing/selftests/damon/sysfs.py +++ b/tools/testing/selftests/damon/sysfs.py @@ -41,12 +41,28 @@ def assert_watermarks_committed(watermarks, dump): assert_true(dump['mid'] == watermarks.mid, 'mid', dump) assert_true(dump['low'] == watermarks.low, 'low', dump)
+def assert_quota_goal_committed(qgoal, dump): + metric_val = { + 'user_input': 0, + 'some_mem_psi_us': 1, + 'node_mem_used_bp': 2, + 'node_mem_free_bp': 3, + } + assert_true(dump['metric'] == metric_val[qgoal.metric], 'metric', dump) + assert_true(dump['target_value'] == qgoal.target_value, 'target_value', + dump) + if qgoal.metric == 'user_input': + assert_true(dump['current_value'] == qgoal.current_value, + 'current_value', dump) + assert_true(dump['nid'] == qgoal.nid, 'nid', dump) + def assert_quota_committed(quota, dump): assert_true(dump['reset_interval'] == quota.reset_interval_ms, 'reset_interval', dump) assert_true(dump['ms'] == quota.ms, 'ms', dump) assert_true(dump['sz'] == quota.sz, 'sz', dump) - # TODO: assert goals are committed + for idx, qgoal in enumerate(quota.goals): + assert_quota_goal_committed(qgoal, dump['goals'][idx]) 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)
Current DAMOS commitment assertion is not testing quota destinations commitment. Add the test.
Signed-off-by: SeongJae Park sj@kernel.org --- tools/testing/selftests/damon/sysfs.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-)
diff --git a/tools/testing/selftests/damon/sysfs.py b/tools/testing/selftests/damon/sysfs.py index 93f20c5c6780..803f38b61ab3 100755 --- a/tools/testing/selftests/damon/sysfs.py +++ b/tools/testing/selftests/damon/sysfs.py @@ -69,6 +69,13 @@ def assert_quota_committed(quota, dump): assert_true( dump['weight_age'] == quota.weight_age_permil, 'weight_age', dump)
+ +def assert_migrate_dests_committed(dests, dump): + assert_true(dump['nr_dests'] == len(dests.dests), 'nr_dests', dump) + for idx, dest in enumerate(dests.dests): + assert_true(dump['node_id_arr'][idx] == dest.id, 'node_id', dump) + assert_true(dump['weight_arr'][idx] == dest.weight, 'weight', dump) + def main(): kdamonds = _damon_sysfs.Kdamonds( [_damon_sysfs.Kdamond( @@ -132,14 +139,8 @@ def main(): if scheme['target_nid'] != -1: fail('damos target nid', status)
- migrate_dests = scheme['migrate_dests'] - if migrate_dests['nr_dests'] != 0: - fail('nr_dests', status) - if migrate_dests['node_id_arr'] != []: - fail('node_id_arr', status) - if migrate_dests['weight_arr'] != []: - fail('weight_arr', status) - + assert_migrate_dests_committed(_damon_sysfs.DamosDests(), + scheme['migrate_dests']) assert_quota_committed(_damon_sysfs.DamosQuota(), scheme['quota']) assert_watermarks_committed(_damon_sysfs.DamosWatermarks(), scheme['wmarks'])
DAMOS scheme commitment assertion is hard-coded for a specific test case. Split it out into a general version that can be reused for different test cases.
Signed-off-by: SeongJae Park sj@kernel.org --- tools/testing/selftests/damon/sysfs.py | 59 ++++++++++++++++---------- 1 file changed, 37 insertions(+), 22 deletions(-)
diff --git a/tools/testing/selftests/damon/sysfs.py b/tools/testing/selftests/damon/sysfs.py index 803f38b61ab3..584a71cbb891 100755 --- a/tools/testing/selftests/damon/sysfs.py +++ b/tools/testing/selftests/damon/sysfs.py @@ -76,6 +76,42 @@ def assert_migrate_dests_committed(dests, dump): assert_true(dump['node_id_arr'][idx] == dest.id, 'node_id', dump) assert_true(dump['weight_arr'][idx] == dest.weight, 'weight', dump)
+def assert_access_pattern_committed(pattern, dump): + assert_true(dump['min_sz_region'] == pattern.size[0], 'min_sz_region', + dump) + assert_true(dump['max_sz_region'] == pattern.size[1], 'max_sz_region', + dump) + assert_true(dump['min_nr_accesses'] == pattern.nr_accesses[0], + 'min_nr_accesses', dump) + assert_true(dump['max_nr_accesses'] == pattern.nr_accesses[1], + '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', + dump) + +def assert_scheme_committed(scheme, dump): + assert_access_pattern_committed(scheme.access_pattern, dump['pattern']) + action_val = { + 'willneed': 0, + 'cold': 1, + 'pageout': 2, + 'hugepage': 3, + 'nohugeapge': 4, + 'lru_prio': 5, + 'lru_deprio': 6, + 'migrate_hot': 7, + 'migrate_cold': 8, + 'stat': 9, + } + assert_true(dump['action'] == action_val[scheme.action], 'action', dump) + assert_true(dump['apply_interval_us'] == scheme. apply_interval_us, + 'apply_interval_us', dump) + assert_true(dump['target_nid'] == scheme.target_nid, 'target_nid', dump) + assert_migrate_dests_committed(scheme.dests, dump['migrate_dests']) + assert_quota_committed(scheme.quota, dump['quota']) + assert_watermarks_committed(scheme.watermarks, dump['wmarks']) + def main(): kdamonds = _damon_sysfs.Kdamonds( [_damon_sysfs.Kdamond( @@ -122,28 +158,7 @@ def main(): if len(ctx['schemes']) != 1: fail('number of schemes', status)
- scheme = ctx['schemes'][0] - if scheme['pattern'] != { - 'min_sz_region': 0, - 'max_sz_region': 2**64 - 1, - 'min_nr_accesses': 0, - 'max_nr_accesses': 2**32 - 1, - 'min_age_region': 0, - 'max_age_region': 2**32 - 1, - }: - fail('damos pattern', status) - if scheme['action'] != 9: # stat - fail('damos action', status) - if scheme['apply_interval_us'] != 0: - fail('damos apply interval', status) - if scheme['target_nid'] != -1: - fail('damos target nid', status) - - assert_migrate_dests_committed(_damon_sysfs.DamosDests(), - scheme['migrate_dests']) - assert_quota_committed(_damon_sysfs.DamosQuota(), scheme['quota']) - assert_watermarks_committed(_damon_sysfs.DamosWatermarks(), - scheme['wmarks']) + assert_scheme_committed(_damon_sysfs.Damos(), ctx['schemes'][0])
kdamonds.stop()
On Sun, 20 Jul 2025 10:16:46 -0700 SeongJae Park sj@kernel.org wrote:
DAMOS scheme commitment assertion is hard-coded for a specific test case. Split it out into a general version that can be reused for different test cases.
This patch has the same name as [18/22]. I renamed this patch to
"selftests/damon/sysfs.py: generalize DAMOS access pattern commit assertion"
and altered its changelog to
: DAMOS scheme access pattern assertion is hard-coded for a specific test : case. Split it out into a general version that can be reused for : different test cases.
--- a/tools/testing/selftests/damon/sysfs.py +++ b/tools/testing/selftests/damon/sysfs.py @@ -76,6 +76,42 @@ def assert_migrate_dests_committed(dests, dump): assert_true(dump['node_id_arr'][idx] == dest.id, 'node_id', dump) assert_true(dump['weight_arr'][idx] == dest.weight, 'weight', dump) +def assert_access_pattern_committed(pattern, dump):
- assert_true(dump['min_sz_region'] == pattern.size[0], 'min_sz_region',
dump)
- assert_true(dump['max_sz_region'] == pattern.size[1], 'max_sz_region',
dump)
- assert_true(dump['min_nr_accesses'] == pattern.nr_accesses[0],
'min_nr_accesses', dump)
- assert_true(dump['max_nr_accesses'] == pattern.nr_accesses[1],
'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',
dump)
+def assert_scheme_committed(scheme, dump):
- assert_access_pattern_committed(scheme.access_pattern, dump['pattern'])
- action_val = {
'willneed': 0,
'cold': 1,
'pageout': 2,
'hugepage': 3,
'nohugeapge': 4,
'lru_prio': 5,
'lru_deprio': 6,
'migrate_hot': 7,
'migrate_cold': 8,
'stat': 9,
}
- assert_true(dump['action'] == action_val[scheme.action], 'action', dump)
- assert_true(dump['apply_interval_us'] == scheme. apply_interval_us,
'apply_interval_us', dump)
- assert_true(dump['target_nid'] == scheme.target_nid, 'target_nid', dump)
- assert_migrate_dests_committed(scheme.dests, dump['migrate_dests'])
- assert_quota_committed(scheme.quota, dump['quota'])
- assert_watermarks_committed(scheme.watermarks, dump['wmarks'])
def main(): kdamonds = _damon_sysfs.Kdamonds( [_damon_sysfs.Kdamond( @@ -122,28 +158,7 @@ def main(): if len(ctx['schemes']) != 1: fail('number of schemes', status)
- scheme = ctx['schemes'][0]
- if scheme['pattern'] != {
'min_sz_region': 0,
'max_sz_region': 2**64 - 1,
'min_nr_accesses': 0,
'max_nr_accesses': 2**32 - 1,
'min_age_region': 0,
'max_age_region': 2**32 - 1,
}:
fail('damos pattern', status)
- if scheme['action'] != 9: # stat
fail('damos action', status)
- if scheme['apply_interval_us'] != 0:
fail('damos apply interval', status)
- if scheme['target_nid'] != -1:
fail('damos target nid', status)
- assert_migrate_dests_committed(_damon_sysfs.DamosDests(),
scheme['migrate_dests'])
- assert_quota_committed(_damon_sysfs.DamosQuota(), scheme['quota'])
- assert_watermarks_committed(_damon_sysfs.DamosWatermarks(),
scheme['wmarks'])
- assert_scheme_committed(_damon_sysfs.Damos(), ctx['schemes'][0])
kdamonds.stop() -- 2.39.5
On Mon, 21 Jul 2025 20:09:22 -0700 Andrew Morton akpm@linux-foundation.org wrote:
On Sun, 20 Jul 2025 10:16:46 -0700 SeongJae Park sj@kernel.org wrote:
DAMOS scheme commitment assertion is hard-coded for a specific test case. Split it out into a general version that can be reused for different test cases.
This patch has the same name as [18/22]. I renamed this patch to
"selftests/damon/sysfs.py: generalize DAMOS access pattern commit assertion"
and altered its changelog to
: DAMOS scheme access pattern assertion is hard-coded for a specific test : case. Split it out into a general version that can be reused for : different test cases.
Thank you for catching this and fixing on your side, Andrew! But, the actual change that this patch is adding is the assertion of individual scheme, while the 18th patch for the assertion of multiple schemes. That is, this patch has actually wrong subject, but the changelog is correct.
So, could you please make the changelog back to the original, but fix the subject by 's/schemes/scheme/', as below?
"selftests/damon/sysfs.py: generalize DAMOS scheme commit assertion"
Sorry for making things unnecessarily complicated. Please let me know if there is anything that I can help for this.
Thanks, SJ
[...]
Current DAMOS scheme commitment assertion is not testing DAMOS filters. Add the test.
Signed-off-by: SeongJae Park sj@kernel.org --- tools/testing/selftests/damon/sysfs.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+)
diff --git a/tools/testing/selftests/damon/sysfs.py b/tools/testing/selftests/damon/sysfs.py index 584a71cbb891..a32871b9b0f2 100755 --- a/tools/testing/selftests/damon/sysfs.py +++ b/tools/testing/selftests/damon/sysfs.py @@ -76,6 +76,21 @@ def assert_migrate_dests_committed(dests, dump): assert_true(dump['node_id_arr'][idx] == dest.id, 'node_id', dump) assert_true(dump['weight_arr'][idx] == dest.weight, 'weight', dump)
+def assert_filter_committed(filter_, dump): + assert_true(filter_.type_ == dump['type'], 'type', dump) + assert_true(filter_.matching == dump['matching'], 'matching', dump) + assert_true(filter_.allow == dump['allow'], 'allow', dump) + # TODO: check memcg_path and memcg_id if type is memcg + if filter_.type_ == 'addr': + assert_true([filter_.addr_start, filter_.addr_end] == + dump['addr_range'], 'addr_range', dump) + elif filter_.type_ == 'target': + assert_true(filter_.target_idx == dump['target_idx'], 'target_idx', + dump) + elif filter_.type_ == 'hugepage_size': + assert_true([filter_.min_, filter_.max_] == dump['sz_range'], + 'sz_range', dump) + def assert_access_pattern_committed(pattern, dump): assert_true(dump['min_sz_region'] == pattern.size[0], 'min_sz_region', dump) @@ -111,6 +126,11 @@ def assert_scheme_committed(scheme, dump): assert_migrate_dests_committed(scheme.dests, dump['migrate_dests']) assert_quota_committed(scheme.quota, dump['quota']) assert_watermarks_committed(scheme.watermarks, dump['wmarks']) + # TODO: test filters directory + for idx, f in enumerate(scheme.core_filters.filters): + assert_filter_committed(f, dump['filters'][idx]) + for idx, f in enumerate(scheme.ops_filters.filters): + assert_filter_committed(f, dump['ops_filters'][idx])
def main(): kdamonds = _damon_sysfs.Kdamonds(
DAMOS schemes commitment assertion is hard-coded for a specific test case. Split it out into a general version that can be reused for different test cases.
Signed-off-by: SeongJae Park sj@kernel.org --- tools/testing/selftests/damon/sysfs.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/tools/testing/selftests/damon/sysfs.py b/tools/testing/selftests/damon/sysfs.py index a32871b9b0f2..9e5c72804edf 100755 --- a/tools/testing/selftests/damon/sysfs.py +++ b/tools/testing/selftests/damon/sysfs.py @@ -132,6 +132,11 @@ def assert_scheme_committed(scheme, dump): for idx, f in enumerate(scheme.ops_filters.filters): assert_filter_committed(f, dump['ops_filters'][idx])
+def assert_schemes_committed(schemes, dump): + assert_true(len(schemes) == len(dump), 'len_schemes', dump) + for idx, scheme in enumerate(schemes): + assert_scheme_committed(scheme, dump[idx]) + def main(): kdamonds = _damon_sysfs.Kdamonds( [_damon_sysfs.Kdamond( @@ -175,10 +180,7 @@ def main(): { 'pid': 0, 'nr_regions': 0, 'regions_list': []}]: fail('adaptive targets', status)
- if len(ctx['schemes']) != 1: - fail('number of schemes', status) - - assert_scheme_committed(_damon_sysfs.Damos(), ctx['schemes'][0]) + assert_schemes_committed([_damon_sysfs.Damos()], ctx['schemes'])
kdamonds.stop()
DAMON monitoring attributes commitment assertion is hard-coded for a specific test case. Split it out into a general version that can be reused for different test cases.
Signed-off-by: SeongJae Park sj@kernel.org --- tools/testing/selftests/damon/sysfs.py | 42 +++++++++++++++----------- 1 file changed, 25 insertions(+), 17 deletions(-)
diff --git a/tools/testing/selftests/damon/sysfs.py b/tools/testing/selftests/damon/sysfs.py index 9e5c72804edf..ed4c3e9fc972 100755 --- a/tools/testing/selftests/damon/sysfs.py +++ b/tools/testing/selftests/damon/sysfs.py @@ -137,6 +137,29 @@ def assert_schemes_committed(schemes, dump): for idx, scheme in enumerate(schemes): assert_scheme_committed(scheme, dump[idx])
+def assert_monitoring_attrs_committed(attrs, dump): + assert_true(dump['sample_interval'] == attrs.sample_us, 'sample_interval', + dump) + assert_true(dump['aggr_interval'] == attrs.aggr_us, 'aggr_interval', dump) + assert_true(dump['intervals_goal']['access_bp'] == + attrs.intervals_goal.access_bp, 'access_bp', + dump['intervals_goal']) + assert_true(dump['intervals_goal']['aggrs'] == attrs.intervals_goal.aggrs, + 'aggrs', dump['intervals_goal']) + assert_true(dump['intervals_goal']['min_sample_us'] == + attrs.intervals_goal.min_sample_us, 'min_sample_us', + dump['intervals_goal']) + assert_true(dump['intervals_goal']['max_sample_us'] == + attrs.intervals_goal.max_sample_us, 'max_sample_us', + dump['intervals_goal']) + + assert_true(dump['ops_update_interval'] == attrs.update_us, + 'ops_update_interval', dump) + assert_true(dump['min_nr_regions'] == attrs.min_nr_regions, + 'min_nr_regions', dump) + assert_true(dump['max_nr_regions'] == attrs.max_nr_regions, + 'max_nr_regions', dump) + def main(): kdamonds = _damon_sysfs.Kdamonds( [_damon_sysfs.Kdamond( @@ -158,23 +181,8 @@ def main(): fail('number of contexts', status)
ctx = status['contexts'][0] - attrs = ctx['attrs'] - if attrs['sample_interval'] != 5000: - fail('sample interval', status) - if attrs['aggr_interval'] != 100000: - fail('aggr interval', status) - if attrs['ops_update_interval'] != 1000000: - fail('ops updte interval', status) - - if attrs['intervals_goal'] != { - 'access_bp': 0, 'aggrs': 0, - 'min_sample_us': 0, 'max_sample_us': 0}: - fail('intervals goal') - - if attrs['min_nr_regions'] != 10: - fail('min_nr_regions') - if attrs['max_nr_regions'] != 1000: - fail('max_nr_regions') + + assert_monitoring_attrs_committed(_damon_sysfs.DamonAttrs(), ctx['attrs'])
if ctx['adaptive_targets'] != [ { 'pid': 0, 'nr_regions': 0, 'regions_list': []}]:
DAMON context commitment assertion is hard-coded for a specific test case. Split it out into a general version that can be reused for different test cases.
Signed-off-by: SeongJae Park sj@kernel.org --- tools/testing/selftests/damon/sysfs.py | 28 +++++++++++++++----------- 1 file changed, 16 insertions(+), 12 deletions(-)
diff --git a/tools/testing/selftests/damon/sysfs.py b/tools/testing/selftests/damon/sysfs.py index ed4c3e9fc972..429865e5ac9c 100755 --- a/tools/testing/selftests/damon/sysfs.py +++ b/tools/testing/selftests/damon/sysfs.py @@ -160,6 +160,21 @@ def assert_monitoring_attrs_committed(attrs, dump): assert_true(dump['max_nr_regions'] == attrs.max_nr_regions, 'max_nr_regions', dump)
+def assert_ctx_committed(ctx, dump): + ops_val = { + 'vaddr': 0, + 'fvaddr': 1, + 'paddr': 2, + } + assert_true(dump['ops']['id'] == ops_val[ctx.ops], 'ops_id', dump) + assert_monitoring_attrs_committed(ctx.monitoring_attrs, dump['attrs']) + assert_schemes_committed(ctx.schemes, dump['schemes']) + +def assert_ctxs_committed(ctxs, dump): + assert_true(len(ctxs) == len(dump), 'ctxs length', dump) + for idx, ctx in enumerate(ctxs): + assert_ctx_committed(ctx, dump[idx]) + def main(): kdamonds = _damon_sysfs.Kdamonds( [_damon_sysfs.Kdamond( @@ -177,18 +192,7 @@ def main(): print(err) exit(1)
- if len(status['contexts']) != 1: - fail('number of contexts', status) - - ctx = status['contexts'][0] - - assert_monitoring_attrs_committed(_damon_sysfs.DamonAttrs(), ctx['attrs']) - - if ctx['adaptive_targets'] != [ - { 'pid': 0, 'nr_regions': 0, 'regions_list': []}]: - fail('adaptive targets', status) - - assert_schemes_committed([_damon_sysfs.Damos()], ctx['schemes']) + assert_ctxs_committed(kdamonds.kdamonds[0].contexts, status['contexts'])
kdamonds.stop()
sysfs.py is testing only the default and minimum DAMON parameters. Add another test case for more non-default additional DAMON parameters commitment on runtime.
Signed-off-by: SeongJae Park sj@kernel.org --- tools/testing/selftests/damon/sysfs.py | 53 ++++++++++++++++++++++++++ 1 file changed, 53 insertions(+)
diff --git a/tools/testing/selftests/damon/sysfs.py b/tools/testing/selftests/damon/sysfs.py index 429865e5ac9c..b2cb178dda15 100755 --- a/tools/testing/selftests/damon/sysfs.py +++ b/tools/testing/selftests/damon/sysfs.py @@ -194,6 +194,59 @@ def main():
assert_ctxs_committed(kdamonds.kdamonds[0].contexts, status['contexts'])
+ context = _damon_sysfs.DamonCtx( + monitoring_attrs=_damon_sysfs.DamonAttrs( + sample_us=100000, aggr_us=2000000, + intervals_goal=_damon_sysfs.IntervalsGoal( + access_bp=400, aggrs=3, min_sample_us=5000, + max_sample_us=10000000), + update_us=2000000), + schemes=[_damon_sysfs.Damos( + action='pageout', + access_pattern=_damon_sysfs.DamosAccessPattern( + size=[4096, 2**10], + nr_accesses=[3, 317], + age=[5,71]), + quota=_damon_sysfs.DamosQuota( + sz=100*1024*1024, ms=100, + goals=[_damon_sysfs.DamosQuotaGoal( + metric='node_mem_used_bp', + target_value=9950, + nid=1)], + reset_interval_ms=1500, + weight_sz_permil=20, + weight_nr_accesses_permil=200, + weight_age_permil=1000), + watermarks=_damon_sysfs.DamosWatermarks( + metric = 'free_mem_rate', interval = 500000, # 500 ms + high = 500, mid = 400, low = 50), + target_nid=1, + apply_interval_us=1000000, + dests=_damon_sysfs.DamosDests( + dests=[_damon_sysfs.DamosDest(id=1, weight=30), + _damon_sysfs.DamosDest(id=0, weight=70)]), + core_filters=[ + _damon_sysfs.DamosFilter(type_='addr', matching=True, + allow=False, addr_start=42, + addr_end=4242), + ], + ops_filters=[ + _damon_sysfs.DamosFilter(type_='anon', matching=True, + allow=True), + ], + )]) + context.idx = 0 + context.kdamond = kdamonds.kdamonds[0] + kdamonds.kdamonds[0].contexts = [context] + kdamonds.kdamonds[0].commit() + + status, err = dump_damon_status_dict(kdamonds.kdamonds[0].pid) + if err is not None: + print(err) + exit(1) + + assert_ctxs_committed(kdamonds.kdamonds[0].contexts, status['contexts']) + kdamonds.stop()
if __name__ == '__main__':
sysfs.py is testing if non-default additional parameters can be committed. Add a test case for further reducing the parameters to the default set.
Signed-off-by: SeongJae Park sj@kernel.org --- tools/testing/selftests/damon/sysfs.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+)
diff --git a/tools/testing/selftests/damon/sysfs.py b/tools/testing/selftests/damon/sysfs.py index b2cb178dda15..e0753ed92a98 100755 --- a/tools/testing/selftests/damon/sysfs.py +++ b/tools/testing/selftests/damon/sysfs.py @@ -247,6 +247,20 @@ def main():
assert_ctxs_committed(kdamonds.kdamonds[0].contexts, status['contexts'])
+ # test online commitment of minimum context. + context = _damon_sysfs.DamonCtx() + context.idx = 0 + context.kdamond = kdamonds.kdamonds[0] + kdamonds.kdamonds[0].contexts = [context] + kdamonds.kdamonds[0].commit() + + status, err = dump_damon_status_dict(kdamonds.kdamonds[0].pid) + if err is not None: + print(err) + exit(1) + + assert_ctxs_committed(kdamonds.kdamonds[0].contexts, status['contexts']) + kdamonds.stop()
if __name__ == '__main__':
linux-kselftest-mirror@lists.linaro.org