Path: blob/master/tools/testing/selftests/damon/_damon_sysfs.py
29268 views
# SPDX-License-Identifier: GPL-2.012import os34ksft_skip=456sysfs_root = None7with open('/proc/mounts', 'r') as f:8for line in f:9dev_name, mount_point, dev_fs = line.split()[:3]10if dev_fs == 'sysfs':11sysfs_root = '%s/kernel/mm/damon/admin' % mount_point12break13if sysfs_root is None:14print('Seems sysfs not mounted?')15exit(ksft_skip)1617if not os.path.exists(sysfs_root):18print('Seems DAMON disabled?')19exit(ksft_skip)2021def write_file(path, string):22"Returns error string if failed, or None otherwise"23string = '%s' % string24try:25with open(path, 'w') as f:26f.write(string)27except Exception as e:28return '%s' % e29return None3031def read_file(path):32'''Returns the read content and error string. The read content is None if33the reading failed'''34try:35with open(path, 'r') as f:36return f.read(), None37except Exception as e:38return None, '%s' % e3940class DamosAccessPattern:41size = None42nr_accesses = None43age = None44scheme = None4546def __init__(self, size=None, nr_accesses=None, age=None):47self.size = size48self.nr_accesses = nr_accesses49self.age = age5051if self.size is None:52self.size = [0, 2**64 - 1]53if self.nr_accesses is None:54self.nr_accesses = [0, 2**32 - 1]55if self.age is None:56self.age = [0, 2**32 - 1]5758def sysfs_dir(self):59return os.path.join(self.scheme.sysfs_dir(), 'access_pattern')6061def stage(self):62err = write_file(63os.path.join(self.sysfs_dir(), 'sz', 'min'), self.size[0])64if err is not None:65return err66err = write_file(67os.path.join(self.sysfs_dir(), 'sz', 'max'), self.size[1])68if err is not None:69return err70err = write_file(os.path.join(self.sysfs_dir(), 'nr_accesses', 'min'),71self.nr_accesses[0])72if err is not None:73return err74err = write_file(os.path.join(self.sysfs_dir(), 'nr_accesses', 'max'),75self.nr_accesses[1])76if err is not None:77return err78err = write_file(79os.path.join(self.sysfs_dir(), 'age', 'min'), self.age[0])80if err is not None:81return err82err = write_file(83os.path.join(self.sysfs_dir(), 'age', 'max'), self.age[1])84if err is not None:85return err8687qgoal_metric_user_input = 'user_input'88qgoal_metric_some_mem_psi_us = 'some_mem_psi_us'89qgoal_metrics = [qgoal_metric_user_input, qgoal_metric_some_mem_psi_us]9091class DamosQuotaGoal:92metric = None93target_value = None94current_value = None95nid = None96effective_bytes = None97quota = None # owner quota98idx = None99100def __init__(self, metric, target_value=10000, current_value=0, nid=0):101self.metric = metric102self.target_value = target_value103self.current_value = current_value104self.nid = nid105106def sysfs_dir(self):107return os.path.join(self.quota.sysfs_dir(), 'goals', '%d' % self.idx)108109def stage(self):110err = write_file(os.path.join(self.sysfs_dir(), 'target_metric'),111self.metric)112if err is not None:113return err114err = write_file(os.path.join(self.sysfs_dir(), 'target_value'),115self.target_value)116if err is not None:117return err118err = write_file(os.path.join(self.sysfs_dir(), 'current_value'),119self.current_value)120if err is not None:121return err122err = write_file(os.path.join(self.sysfs_dir(), 'nid'), self.nid)123if err is not None:124return err125126return None127128class DamosQuota:129sz = None # size quota, in bytes130ms = None # time quota131goals = None # quota goals132reset_interval_ms = None # quota reset interval133weight_sz_permil = None134weight_nr_accesses_permil = None135weight_age_permil = None136scheme = None # owner scheme137138def __init__(self, sz=0, ms=0, goals=None, reset_interval_ms=0,139weight_sz_permil=0, weight_nr_accesses_permil=0,140weight_age_permil=0):141self.sz = sz142self.ms = ms143self.reset_interval_ms = reset_interval_ms144self.weight_sz_permil = weight_sz_permil145self.weight_nr_accesses_permil = weight_nr_accesses_permil146self.weight_age_permil = weight_age_permil147self.goals = goals if goals is not None else []148for idx, goal in enumerate(self.goals):149goal.idx = idx150goal.quota = self151152def sysfs_dir(self):153return os.path.join(self.scheme.sysfs_dir(), 'quotas')154155def stage(self):156err = write_file(os.path.join(self.sysfs_dir(), 'bytes'), self.sz)157if err is not None:158return err159err = write_file(os.path.join(self.sysfs_dir(), 'ms'), self.ms)160if err is not None:161return err162err = write_file(os.path.join(self.sysfs_dir(), 'reset_interval_ms'),163self.reset_interval_ms)164if err is not None:165return err166167err = write_file(os.path.join(168self.sysfs_dir(), 'weights', 'sz_permil'), self.weight_sz_permil)169if err is not None:170return err171err = write_file(os.path.join(172self.sysfs_dir(), 'weights', 'nr_accesses_permil'),173self.weight_nr_accesses_permil)174if err is not None:175return err176err = write_file(os.path.join(177self.sysfs_dir(), 'weights', 'age_permil'), self.weight_age_permil)178if err is not None:179return err180181nr_goals_file = os.path.join(self.sysfs_dir(), 'goals', 'nr_goals')182content, err = read_file(nr_goals_file)183if err is not None:184return err185if int(content) != len(self.goals):186err = write_file(nr_goals_file, len(self.goals))187if err is not None:188return err189for goal in self.goals:190err = goal.stage()191if err is not None:192return err193return None194195class DamosWatermarks:196metric = None197interval = None198high = None199mid = None200low = None201scheme = None # owner scheme202203def __init__(self, metric='none', interval=0, high=0, mid=0, low=0):204self.metric = metric205self.interval = interval206self.high = high207self.mid = mid208self.low = low209210def sysfs_dir(self):211return os.path.join(self.scheme.sysfs_dir(), 'watermarks')212213def stage(self):214err = write_file(os.path.join(self.sysfs_dir(), 'metric'), self.metric)215if err is not None:216return err217err = write_file(os.path.join(self.sysfs_dir(), 'interval_us'),218self.interval)219if err is not None:220return err221err = write_file(os.path.join(self.sysfs_dir(), 'high'), self.high)222if err is not None:223return err224err = write_file(os.path.join(self.sysfs_dir(), 'mid'), self.mid)225if err is not None:226return err227err = write_file(os.path.join(self.sysfs_dir(), 'low'), self.low)228if err is not None:229return err230231class DamosFilter:232type_ = None233matching = None234allow = None235memcg_path = None236addr_start = None237addr_end = None238target_idx = None239min_ = None240max_ = None241idx = None242filters = None # owner filters243244def __init__(self, type_='anon', matching=False, allow=False,245memcg_path='', addr_start=0, addr_end=0, target_idx=0, min_=0,246max_=0):247self.type_ = type_248self.matching = matching249self.allow = allow250self.memcg_path = memcg_path,251self.addr_start = addr_start252self.addr_end = addr_end253self.target_idx = target_idx254self.min_ = min_255self.max_ = max_256257def sysfs_dir(self):258return os.path.join(self.filters.sysfs_dir(), '%d' % self.idx)259260def stage(self):261err = write_file(os.path.join(self.sysfs_dir(), 'type'), self.type_)262if err is not None:263return err264err = write_file(os.path.join(self.sysfs_dir(), 'matching'),265self.matching)266if err is not None:267return err268err = write_file(os.path.join(self.sysfs_dir(), 'allow'), self.allow)269if err is not None:270return err271err = write_file(os.path.join(self.sysfs_dir(), 'memcg_path'),272self.memcg_path)273if err is not None:274return err275err = write_file(os.path.join(self.sysfs_dir(), 'addr_start'),276self.addr_start)277if err is not None:278return err279err = write_file(os.path.join(self.sysfs_dir(), 'addr_end'),280self.addr_end)281if err is not None:282return err283err = write_file(os.path.join(self.sysfs_dir(), 'damon_target_idx'),284self.target_idx)285if err is not None:286return err287err = write_file(os.path.join(self.sysfs_dir(), 'min'), self.min_)288if err is not None:289return err290err = write_file(os.path.join(self.sysfs_dir(), 'max'), self.max_)291if err is not None:292return err293return None294295class DamosFilters:296name = None297filters = None298scheme = None # owner scheme299300def __init__(self, name, filters=[]):301self.name = name302self.filters = filters303for idx, filter_ in enumerate(self.filters):304filter_.idx = idx305filter_.filters = self306307def sysfs_dir(self):308return os.path.join(self.scheme.sysfs_dir(), self.name)309310def stage(self):311err = write_file(os.path.join(self.sysfs_dir(), 'nr_filters'),312len(self.filters))313if err is not None:314return err315for filter_ in self.filters:316err = filter_.stage()317if err is not None:318return err319return None320321class DamosDest:322id = None323weight = None324idx = None325dests = None # owner dests326327def __init__(self, id=0, weight=0):328self.id = id329self.weight = weight330331def sysfs_dir(self):332return os.path.join(self.dests.sysfs_dir(), '%d' % self.idx)333334def stage(self):335err = write_file(os.path.join(self.sysfs_dir(), 'id'), self.id)336if err is not None:337return err338err = write_file(os.path.join(self.sysfs_dir(), 'weight'), self.weight)339if err is not None:340return err341return None342343class DamosDests:344dests = None345scheme = None # owner scheme346347def __init__(self, dests=[]):348self.dests = dests349for idx, dest in enumerate(self.dests):350dest.idx = idx351dest.dests = self352353def sysfs_dir(self):354return os.path.join(self.scheme.sysfs_dir(), 'dests')355356def stage(self):357err = write_file(os.path.join(self.sysfs_dir(), 'nr_dests'),358len(self.dests))359if err is not None:360return err361for dest in self.dests:362err = dest.stage()363if err is not None:364return err365return None366367class DamosStats:368nr_tried = None369sz_tried = None370nr_applied = None371sz_applied = None372qt_exceeds = None373374def __init__(self, nr_tried, sz_tried, nr_applied, sz_applied, qt_exceeds):375self.nr_tried = nr_tried376self.sz_tried = sz_tried377self.nr_applied = nr_applied378self.sz_applied = sz_applied379self.qt_exceeds = qt_exceeds380381class DamosTriedRegion:382def __init__(self, start, end, nr_accesses, age):383self.start = start384self.end = end385self.nr_accesses = nr_accesses386self.age = age387388class Damos:389action = None390access_pattern = None391quota = None392watermarks = None393core_filters = None394ops_filters = None395filters = None396apply_interval_us = None397target_nid = None398dests = None399idx = None400context = None401tried_bytes = None402stats = None403tried_regions = None404405def __init__(self, action='stat', access_pattern=DamosAccessPattern(),406quota=DamosQuota(), watermarks=DamosWatermarks(),407core_filters=[], ops_filters=[], filters=[], target_nid=0,408dests=DamosDests(), apply_interval_us=0):409self.action = action410self.access_pattern = access_pattern411self.access_pattern.scheme = self412self.quota = quota413self.quota.scheme = self414self.watermarks = watermarks415self.watermarks.scheme = self416417self.core_filters = DamosFilters(name='core_filters',418filters=core_filters)419self.core_filters.scheme = self420self.ops_filters = DamosFilters(name='ops_filters',421filters=ops_filters)422self.ops_filters.scheme = self423self.filters = DamosFilters(name='filters', filters=filters)424self.filters.scheme = self425426self.target_nid = target_nid427self.dests = dests428self.dests.scheme = self429430self.apply_interval_us = apply_interval_us431432def sysfs_dir(self):433return os.path.join(434self.context.sysfs_dir(), 'schemes', '%d' % self.idx)435436def stage(self):437err = write_file(os.path.join(self.sysfs_dir(), 'action'), self.action)438if err is not None:439return err440err = self.access_pattern.stage()441if err is not None:442return err443err = write_file(os.path.join(self.sysfs_dir(), 'apply_interval_us'),444'%d' % self.apply_interval_us)445if err is not None:446return err447448err = self.quota.stage()449if err is not None:450return err451452err = self.watermarks.stage()453if err is not None:454return err455456err = self.core_filters.stage()457if err is not None:458return err459err = self.ops_filters.stage()460if err is not None:461return err462err = self.filters.stage()463if err is not None:464return err465466err = write_file(os.path.join(self.sysfs_dir(), 'target_nid'), '%d' %467self.target_nid)468if err is not None:469return err470471err = self.dests.stage()472if err is not None:473return err474475class DamonTarget:476pid = None477# todo: Support target regions if test is made478idx = None479context = None480481def __init__(self, pid):482self.pid = pid483484def sysfs_dir(self):485return os.path.join(486self.context.sysfs_dir(), 'targets', '%d' % self.idx)487488def stage(self):489err = write_file(490os.path.join(self.sysfs_dir(), 'regions', 'nr_regions'), '0')491if err is not None:492return err493return write_file(494os.path.join(self.sysfs_dir(), 'pid_target'), self.pid)495496class IntervalsGoal:497access_bp = None498aggrs = None499min_sample_us = None500max_sample_us = None501attrs = None # owner DamonAttrs502503def __init__(self, access_bp=0, aggrs=0, min_sample_us=0, max_sample_us=0):504self.access_bp = access_bp505self.aggrs = aggrs506self.min_sample_us = min_sample_us507self.max_sample_us = max_sample_us508509def sysfs_dir(self):510return os.path.join(self.attrs.interval_sysfs_dir(), 'intervals_goal')511512def stage(self):513err = write_file(514os.path.join(self.sysfs_dir(), 'access_bp'), self.access_bp)515if err is not None:516return err517err = write_file(os.path.join(self.sysfs_dir(), 'aggrs'), self.aggrs)518if err is not None:519return err520err = write_file(os.path.join(self.sysfs_dir(), 'min_sample_us'),521self.min_sample_us)522if err is not None:523return err524err = write_file(os.path.join(self.sysfs_dir(), 'max_sample_us'),525self.max_sample_us)526if err is not None:527return err528return None529530class DamonAttrs:531sample_us = None532aggr_us = None533intervals_goal = None534update_us = None535min_nr_regions = None536max_nr_regions = None537context = None538539def __init__(self, sample_us=5000, aggr_us=100000,540intervals_goal=IntervalsGoal(), update_us=1000000,541min_nr_regions=10, max_nr_regions=1000):542self.sample_us = sample_us543self.aggr_us = aggr_us544self.intervals_goal = intervals_goal545self.intervals_goal.attrs = self546self.update_us = update_us547self.min_nr_regions = min_nr_regions548self.max_nr_regions = max_nr_regions549550def interval_sysfs_dir(self):551return os.path.join(self.context.sysfs_dir(), 'monitoring_attrs',552'intervals')553554def nr_regions_range_sysfs_dir(self):555return os.path.join(self.context.sysfs_dir(), 'monitoring_attrs',556'nr_regions')557558def stage(self):559err = write_file(os.path.join(self.interval_sysfs_dir(), 'sample_us'),560self.sample_us)561if err is not None:562return err563err = write_file(os.path.join(self.interval_sysfs_dir(), 'aggr_us'),564self.aggr_us)565if err is not None:566return err567err = self.intervals_goal.stage()568if err is not None:569return err570err = write_file(os.path.join(self.interval_sysfs_dir(), 'update_us'),571self.update_us)572if err is not None:573return err574575err = write_file(576os.path.join(self.nr_regions_range_sysfs_dir(), 'min'),577self.min_nr_regions)578if err is not None:579return err580581err = write_file(582os.path.join(self.nr_regions_range_sysfs_dir(), 'max'),583self.max_nr_regions)584if err is not None:585return err586587class DamonCtx:588ops = None589monitoring_attrs = None590targets = None591schemes = None592kdamond = None593idx = None594595def __init__(self, ops='paddr', monitoring_attrs=DamonAttrs(), targets=[],596schemes=[]):597self.ops = ops598self.monitoring_attrs = monitoring_attrs599self.monitoring_attrs.context = self600601self.targets = targets602for idx, target in enumerate(self.targets):603target.idx = idx604target.context = self605606self.schemes = schemes607for idx, scheme in enumerate(self.schemes):608scheme.idx = idx609scheme.context = self610611def sysfs_dir(self):612return os.path.join(self.kdamond.sysfs_dir(), 'contexts',613'%d' % self.idx)614615def stage(self):616err = write_file(617os.path.join(self.sysfs_dir(), 'operations'), self.ops)618if err is not None:619return err620err = self.monitoring_attrs.stage()621if err is not None:622return err623624nr_targets_file = os.path.join(625self.sysfs_dir(), 'targets', 'nr_targets')626content, err = read_file(nr_targets_file)627if err is not None:628return err629if int(content) != len(self.targets):630err = write_file(nr_targets_file, '%d' % len(self.targets))631if err is not None:632return err633for target in self.targets:634err = target.stage()635if err is not None:636return err637638nr_schemes_file = os.path.join(639self.sysfs_dir(), 'schemes', 'nr_schemes')640content, err = read_file(nr_schemes_file)641if err is not None:642return err643if int(content) != len(self.schemes):644err = write_file(nr_schemes_file, '%d' % len(self.schemes))645if err is not None:646return err647for scheme in self.schemes:648err = scheme.stage()649if err is not None:650return err651return None652653class Kdamond:654state = None655pid = None656contexts = None657idx = None # index of this kdamond between siblings658kdamonds = None # parent659660def __init__(self, contexts=[]):661self.contexts = contexts662for idx, context in enumerate(self.contexts):663context.idx = idx664context.kdamond = self665666def sysfs_dir(self):667return os.path.join(self.kdamonds.sysfs_dir(), '%d' % self.idx)668669def start(self):670nr_contexts_file = os.path.join(self.sysfs_dir(),671'contexts', 'nr_contexts')672content, err = read_file(nr_contexts_file)673if err is not None:674return err675if int(content) != len(self.contexts):676err = write_file(nr_contexts_file, '%d' % len(self.contexts))677if err is not None:678return err679680for context in self.contexts:681err = context.stage()682if err is not None:683return err684err = write_file(os.path.join(self.sysfs_dir(), 'state'), 'on')685if err is not None:686return err687self.pid, err = read_file(os.path.join(self.sysfs_dir(), 'pid'))688return err689690def stop(self):691err = write_file(os.path.join(self.sysfs_dir(), 'state'), 'off')692return err693694def update_schemes_tried_regions(self):695err = write_file(os.path.join(self.sysfs_dir(), 'state'),696'update_schemes_tried_regions')697if err is not None:698return err699for context in self.contexts:700for scheme in context.schemes:701tried_regions = []702tried_regions_dir = os.path.join(703scheme.sysfs_dir(), 'tried_regions')704region_indices = []705for filename in os.listdir(706os.path.join(scheme.sysfs_dir(), 'tried_regions')):707tried_region_dir = os.path.join(tried_regions_dir, filename)708if not os.path.isdir(tried_region_dir):709continue710region_indices.append(int(filename))711for region_idx in sorted(region_indices):712tried_region_dir = os.path.join(tried_regions_dir,713'%d' % region_idx)714region_values = []715for f in ['start', 'end', 'nr_accesses', 'age']:716content, err = read_file(717os.path.join(tried_region_dir, f))718if err is not None:719return err720region_values.append(int(content))721tried_regions.append(DamosTriedRegion(*region_values))722scheme.tried_regions = tried_regions723724def update_schemes_tried_bytes(self):725err = write_file(os.path.join(self.sysfs_dir(), 'state'),726'update_schemes_tried_bytes')727if err is not None:728return err729for context in self.contexts:730for scheme in context.schemes:731content, err = read_file(os.path.join(scheme.sysfs_dir(),732'tried_regions', 'total_bytes'))733if err is not None:734return err735scheme.tried_bytes = int(content)736737def update_schemes_stats(self):738err = write_file(os.path.join(self.sysfs_dir(), 'state'),739'update_schemes_stats')740if err is not None:741return err742for context in self.contexts:743for scheme in context.schemes:744stat_values = []745for stat in ['nr_tried', 'sz_tried', 'nr_applied',746'sz_applied', 'qt_exceeds']:747content, err = read_file(748os.path.join(scheme.sysfs_dir(), 'stats', stat))749if err is not None:750return err751stat_values.append(int(content))752scheme.stats = DamosStats(*stat_values)753754def update_schemes_effective_quotas(self):755err = write_file(os.path.join(self.sysfs_dir(), 'state'),756'update_schemes_effective_quotas')757if err is not None:758return err759for context in self.contexts:760for scheme in context.schemes:761for goal in scheme.quota.goals:762content, err = read_file(763os.path.join(scheme.quota.sysfs_dir(),764'effective_bytes'))765if err is not None:766return err767goal.effective_bytes = int(content)768return None769770def commit(self):771nr_contexts_file = os.path.join(self.sysfs_dir(),772'contexts', 'nr_contexts')773content, err = read_file(nr_contexts_file)774if err is not None:775return err776if int(content) != len(self.contexts):777err = write_file(nr_contexts_file, '%d' % len(self.contexts))778if err is not None:779return err780781for context in self.contexts:782err = context.stage()783if err is not None:784return err785err = write_file(os.path.join(self.sysfs_dir(), 'state'), 'commit')786return err787788789def commit_schemes_quota_goals(self):790for context in self.contexts:791for scheme in context.schemes:792for goal in scheme.quota.goals:793err = goal.stage()794if err is not None:795print('commit_schemes_quota_goals failed stagign: %s'%796err)797exit(1)798return write_file(os.path.join(self.sysfs_dir(), 'state'),799'commit_schemes_quota_goals')800801class Kdamonds:802kdamonds = []803804def __init__(self, kdamonds=[]):805self.kdamonds = kdamonds806for idx, kdamond in enumerate(self.kdamonds):807kdamond.idx = idx808kdamond.kdamonds = self809810def sysfs_dir(self):811return os.path.join(sysfs_root, 'kdamonds')812813def start(self):814err = write_file(os.path.join(self.sysfs_dir(), 'nr_kdamonds'),815'%s' % len(self.kdamonds))816if err is not None:817return err818for kdamond in self.kdamonds:819err = kdamond.start()820if err is not None:821return err822return None823824def stop(self):825for kdamond in self.kdamonds:826err = kdamond.stop()827if err is not None:828return err829return None830831832