Path: blob/master/tools/testing/selftests/damon/_damon_sysfs.py
50004 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 = None477obsolete = None478# todo: Support target regions if test is made479idx = None480context = None481482def __init__(self, pid, obsolete=False):483self.pid = pid484self.obsolete = obsolete485486def sysfs_dir(self):487return os.path.join(488self.context.sysfs_dir(), 'targets', '%d' % self.idx)489490def stage(self):491err = write_file(492os.path.join(self.sysfs_dir(), 'regions', 'nr_regions'), '0')493if err is not None:494return err495err = write_file(496os.path.join(self.sysfs_dir(), 'pid_target'), self.pid)497if err is not None:498return err499return write_file(500os.path.join(self.sysfs_dir(), 'obsolete_target'),501'Y' if self.obsolete else 'N')502503class IntervalsGoal:504access_bp = None505aggrs = None506min_sample_us = None507max_sample_us = None508attrs = None # owner DamonAttrs509510def __init__(self, access_bp=0, aggrs=0, min_sample_us=0, max_sample_us=0):511self.access_bp = access_bp512self.aggrs = aggrs513self.min_sample_us = min_sample_us514self.max_sample_us = max_sample_us515516def sysfs_dir(self):517return os.path.join(self.attrs.interval_sysfs_dir(), 'intervals_goal')518519def stage(self):520err = write_file(521os.path.join(self.sysfs_dir(), 'access_bp'), self.access_bp)522if err is not None:523return err524err = write_file(os.path.join(self.sysfs_dir(), 'aggrs'), self.aggrs)525if err is not None:526return err527err = write_file(os.path.join(self.sysfs_dir(), 'min_sample_us'),528self.min_sample_us)529if err is not None:530return err531err = write_file(os.path.join(self.sysfs_dir(), 'max_sample_us'),532self.max_sample_us)533if err is not None:534return err535return None536537class DamonAttrs:538sample_us = None539aggr_us = None540intervals_goal = None541update_us = None542min_nr_regions = None543max_nr_regions = None544context = None545546def __init__(self, sample_us=5000, aggr_us=100000,547intervals_goal=IntervalsGoal(), update_us=1000000,548min_nr_regions=10, max_nr_regions=1000):549self.sample_us = sample_us550self.aggr_us = aggr_us551self.intervals_goal = intervals_goal552self.intervals_goal.attrs = self553self.update_us = update_us554self.min_nr_regions = min_nr_regions555self.max_nr_regions = max_nr_regions556557def interval_sysfs_dir(self):558return os.path.join(self.context.sysfs_dir(), 'monitoring_attrs',559'intervals')560561def nr_regions_range_sysfs_dir(self):562return os.path.join(self.context.sysfs_dir(), 'monitoring_attrs',563'nr_regions')564565def stage(self):566err = write_file(os.path.join(self.interval_sysfs_dir(), 'sample_us'),567self.sample_us)568if err is not None:569return err570err = write_file(os.path.join(self.interval_sysfs_dir(), 'aggr_us'),571self.aggr_us)572if err is not None:573return err574err = self.intervals_goal.stage()575if err is not None:576return err577err = write_file(os.path.join(self.interval_sysfs_dir(), 'update_us'),578self.update_us)579if err is not None:580return err581582err = write_file(583os.path.join(self.nr_regions_range_sysfs_dir(), 'min'),584self.min_nr_regions)585if err is not None:586return err587588err = write_file(589os.path.join(self.nr_regions_range_sysfs_dir(), 'max'),590self.max_nr_regions)591if err is not None:592return err593594class DamonCtx:595ops = None596monitoring_attrs = None597targets = None598schemes = None599kdamond = None600idx = None601602def __init__(self, ops='paddr', monitoring_attrs=DamonAttrs(), targets=[],603schemes=[]):604self.ops = ops605self.monitoring_attrs = monitoring_attrs606self.monitoring_attrs.context = self607608self.targets = targets609for idx, target in enumerate(self.targets):610target.idx = idx611target.context = self612613self.schemes = schemes614for idx, scheme in enumerate(self.schemes):615scheme.idx = idx616scheme.context = self617618def sysfs_dir(self):619return os.path.join(self.kdamond.sysfs_dir(), 'contexts',620'%d' % self.idx)621622def stage(self):623err = write_file(624os.path.join(self.sysfs_dir(), 'operations'), self.ops)625if err is not None:626return err627err = self.monitoring_attrs.stage()628if err is not None:629return err630631nr_targets_file = os.path.join(632self.sysfs_dir(), 'targets', 'nr_targets')633content, err = read_file(nr_targets_file)634if err is not None:635return err636if int(content) != len(self.targets):637err = write_file(nr_targets_file, '%d' % len(self.targets))638if err is not None:639return err640for target in self.targets:641err = target.stage()642if err is not None:643return err644645nr_schemes_file = os.path.join(646self.sysfs_dir(), 'schemes', 'nr_schemes')647content, err = read_file(nr_schemes_file)648if err is not None:649return err650if int(content) != len(self.schemes):651err = write_file(nr_schemes_file, '%d' % len(self.schemes))652if err is not None:653return err654for scheme in self.schemes:655err = scheme.stage()656if err is not None:657return err658return None659660class Kdamond:661state = None662pid = None663contexts = None664idx = None # index of this kdamond between siblings665kdamonds = None # parent666667def __init__(self, contexts=[]):668self.contexts = contexts669for idx, context in enumerate(self.contexts):670context.idx = idx671context.kdamond = self672673def sysfs_dir(self):674return os.path.join(self.kdamonds.sysfs_dir(), '%d' % self.idx)675676def start(self):677nr_contexts_file = os.path.join(self.sysfs_dir(),678'contexts', 'nr_contexts')679content, err = read_file(nr_contexts_file)680if err is not None:681return err682if int(content) != len(self.contexts):683err = write_file(nr_contexts_file, '%d' % len(self.contexts))684if err is not None:685return err686687for context in self.contexts:688err = context.stage()689if err is not None:690return err691err = write_file(os.path.join(self.sysfs_dir(), 'state'), 'on')692if err is not None:693return err694self.pid, err = read_file(os.path.join(self.sysfs_dir(), 'pid'))695return err696697def stop(self):698err = write_file(os.path.join(self.sysfs_dir(), 'state'), 'off')699return err700701def update_schemes_tried_regions(self):702err = write_file(os.path.join(self.sysfs_dir(), 'state'),703'update_schemes_tried_regions')704if err is not None:705return err706for context in self.contexts:707for scheme in context.schemes:708tried_regions = []709tried_regions_dir = os.path.join(710scheme.sysfs_dir(), 'tried_regions')711region_indices = []712for filename in os.listdir(713os.path.join(scheme.sysfs_dir(), 'tried_regions')):714tried_region_dir = os.path.join(tried_regions_dir, filename)715if not os.path.isdir(tried_region_dir):716continue717region_indices.append(int(filename))718for region_idx in sorted(region_indices):719tried_region_dir = os.path.join(tried_regions_dir,720'%d' % region_idx)721region_values = []722for f in ['start', 'end', 'nr_accesses', 'age']:723content, err = read_file(724os.path.join(tried_region_dir, f))725if err is not None:726return err727region_values.append(int(content))728tried_regions.append(DamosTriedRegion(*region_values))729scheme.tried_regions = tried_regions730731def update_schemes_tried_bytes(self):732err = write_file(os.path.join(self.sysfs_dir(), 'state'),733'update_schemes_tried_bytes')734if err is not None:735return err736for context in self.contexts:737for scheme in context.schemes:738content, err = read_file(os.path.join(scheme.sysfs_dir(),739'tried_regions', 'total_bytes'))740if err is not None:741return err742scheme.tried_bytes = int(content)743744def update_schemes_stats(self):745err = write_file(os.path.join(self.sysfs_dir(), 'state'),746'update_schemes_stats')747if err is not None:748return err749for context in self.contexts:750for scheme in context.schemes:751stat_values = []752for stat in ['nr_tried', 'sz_tried', 'nr_applied',753'sz_applied', 'qt_exceeds']:754content, err = read_file(755os.path.join(scheme.sysfs_dir(), 'stats', stat))756if err is not None:757return err758stat_values.append(int(content))759scheme.stats = DamosStats(*stat_values)760761def update_schemes_effective_quotas(self):762err = write_file(os.path.join(self.sysfs_dir(), 'state'),763'update_schemes_effective_quotas')764if err is not None:765return err766for context in self.contexts:767for scheme in context.schemes:768for goal in scheme.quota.goals:769content, err = read_file(770os.path.join(scheme.quota.sysfs_dir(),771'effective_bytes'))772if err is not None:773return err774goal.effective_bytes = int(content)775return None776777def commit(self):778nr_contexts_file = os.path.join(self.sysfs_dir(),779'contexts', 'nr_contexts')780content, err = read_file(nr_contexts_file)781if err is not None:782return err783if int(content) != len(self.contexts):784err = write_file(nr_contexts_file, '%d' % len(self.contexts))785if err is not None:786return err787788for context in self.contexts:789err = context.stage()790if err is not None:791return err792err = write_file(os.path.join(self.sysfs_dir(), 'state'), 'commit')793return err794795796def commit_schemes_quota_goals(self):797for context in self.contexts:798for scheme in context.schemes:799for goal in scheme.quota.goals:800err = goal.stage()801if err is not None:802print('commit_schemes_quota_goals failed stagign: %s'%803err)804exit(1)805return write_file(os.path.join(self.sysfs_dir(), 'state'),806'commit_schemes_quota_goals')807808class Kdamonds:809kdamonds = []810811def __init__(self, kdamonds=[]):812self.kdamonds = kdamonds813for idx, kdamond in enumerate(self.kdamonds):814kdamond.idx = idx815kdamond.kdamonds = self816817def sysfs_dir(self):818return os.path.join(sysfs_root, 'kdamonds')819820def start(self):821err = write_file(os.path.join(self.sysfs_dir(), 'nr_kdamonds'),822'%s' % len(self.kdamonds))823if err is not None:824return err825for kdamond in self.kdamonds:826err = kdamond.start()827if err is not None:828return err829return None830831def stop(self):832for kdamond in self.kdamonds:833err = kdamond.stop()834if err is not None:835return err836return None837838839