544 lines · python
1# SPDX-License-Identifier: GPL-2.02 3import os4 5ksft_skip=46 7sysfs_root = None8with open('/proc/mounts', 'r') as f:9 for line in f:10 dev_name, mount_point, dev_fs = line.split()[:3]11 if dev_fs == 'sysfs':12 sysfs_root = '%s/kernel/mm/damon/admin' % mount_point13 break14if sysfs_root is None:15 print('Seems sysfs not mounted?')16 exit(ksft_skip)17 18def write_file(path, string):19 "Returns error string if failed, or None otherwise"20 string = '%s' % string21 try:22 with open(path, 'w') as f:23 f.write(string)24 except Exception as e:25 return '%s' % e26 return None27 28def read_file(path):29 '''Returns the read content and error string. The read content is None if30 the reading failed'''31 try:32 with open(path, 'r') as f:33 return f.read(), None34 except Exception as e:35 return None, '%s' % e36 37class DamosAccessPattern:38 size = None39 nr_accesses = None40 age = None41 scheme = None42 43 def __init__(self, size=None, nr_accesses=None, age=None):44 self.size = size45 self.nr_accesses = nr_accesses46 self.age = age47 48 if self.size is None:49 self.size = [0, 2**64 - 1]50 if self.nr_accesses is None:51 self.nr_accesses = [0, 2**64 - 1]52 if self.age is None:53 self.age = [0, 2**64 - 1]54 55 def sysfs_dir(self):56 return os.path.join(self.scheme.sysfs_dir(), 'access_pattern')57 58 def stage(self):59 err = write_file(60 os.path.join(self.sysfs_dir(), 'sz', 'min'), self.size[0])61 if err is not None:62 return err63 err = write_file(64 os.path.join(self.sysfs_dir(), 'sz', 'max'), self.size[1])65 if err is not None:66 return err67 err = write_file(os.path.join(self.sysfs_dir(), 'nr_accesses', 'min'),68 self.nr_accesses[0])69 if err is not None:70 return err71 err = write_file(os.path.join(self.sysfs_dir(), 'nr_accesses', 'max'),72 self.nr_accesses[1])73 if err is not None:74 return err75 err = write_file(76 os.path.join(self.sysfs_dir(), 'age', 'min'), self.age[0])77 if err is not None:78 return err79 err = write_file(80 os.path.join(self.sysfs_dir(), 'age', 'max'), self.age[1])81 if err is not None:82 return err83 84qgoal_metric_user_input = 'user_input'85qgoal_metric_some_mem_psi_us = 'some_mem_psi_us'86qgoal_metrics = [qgoal_metric_user_input, qgoal_metric_some_mem_psi_us]87 88class DamosQuotaGoal:89 metric = None90 target_value = None91 current_value = None92 effective_bytes = None93 quota = None # owner quota94 idx = None95 96 def __init__(self, metric, target_value=10000, current_value=0):97 self.metric = metric98 self.target_value = target_value99 self.current_value = current_value100 101 def sysfs_dir(self):102 return os.path.join(self.quota.sysfs_dir(), 'goals', '%d' % self.idx)103 104 def stage(self):105 err = write_file(os.path.join(self.sysfs_dir(), 'target_metric'),106 self.metric)107 if err is not None:108 return err109 err = write_file(os.path.join(self.sysfs_dir(), 'target_value'),110 self.target_value)111 if err is not None:112 return err113 err = write_file(os.path.join(self.sysfs_dir(), 'current_value'),114 self.current_value)115 if err is not None:116 return err117 return None118 119class DamosQuota:120 sz = None # size quota, in bytes121 ms = None # time quota122 goals = None # quota goals123 reset_interval_ms = None # quota reset interval124 scheme = None # owner scheme125 126 def __init__(self, sz=0, ms=0, goals=None, reset_interval_ms=0):127 self.sz = sz128 self.ms = ms129 self.reset_interval_ms = reset_interval_ms130 self.goals = goals if goals is not None else []131 for idx, goal in enumerate(self.goals):132 goal.idx = idx133 goal.quota = self134 135 def sysfs_dir(self):136 return os.path.join(self.scheme.sysfs_dir(), 'quotas')137 138 def stage(self):139 err = write_file(os.path.join(self.sysfs_dir(), 'bytes'), self.sz)140 if err is not None:141 return err142 err = write_file(os.path.join(self.sysfs_dir(), 'ms'), self.ms)143 if err is not None:144 return err145 err = write_file(os.path.join(self.sysfs_dir(), 'reset_interval_ms'),146 self.reset_interval_ms)147 if err is not None:148 return err149 150 nr_goals_file = os.path.join(self.sysfs_dir(), 'goals', 'nr_goals')151 content, err = read_file(nr_goals_file)152 if err is not None:153 return err154 if int(content) != len(self.goals):155 err = write_file(nr_goals_file, len(self.goals))156 if err is not None:157 return err158 for goal in self.goals:159 err = goal.stage()160 if err is not None:161 return err162 return None163 164class DamosStats:165 nr_tried = None166 sz_tried = None167 nr_applied = None168 sz_applied = None169 qt_exceeds = None170 171 def __init__(self, nr_tried, sz_tried, nr_applied, sz_applied, qt_exceeds):172 self.nr_tried = nr_tried173 self.sz_tried = sz_tried174 self.nr_applied = nr_applied175 self.sz_applied = sz_applied176 self.qt_exceeds = qt_exceeds177 178class DamosTriedRegion:179 def __init__(self, start, end, nr_accesses, age):180 self.start = start181 self.end = end182 self.nr_accesses = nr_accesses183 self.age = age184 185class Damos:186 action = None187 access_pattern = None188 quota = None189 apply_interval_us = None190 # todo: Support watermarks, stats191 idx = None192 context = None193 tried_bytes = None194 stats = None195 tried_regions = None196 197 def __init__(self, action='stat', access_pattern=DamosAccessPattern(),198 quota=DamosQuota(), apply_interval_us=0):199 self.action = action200 self.access_pattern = access_pattern201 self.access_pattern.scheme = self202 self.quota = quota203 self.quota.scheme = self204 self.apply_interval_us = apply_interval_us205 206 def sysfs_dir(self):207 return os.path.join(208 self.context.sysfs_dir(), 'schemes', '%d' % self.idx)209 210 def stage(self):211 err = write_file(os.path.join(self.sysfs_dir(), 'action'), self.action)212 if err is not None:213 return err214 err = self.access_pattern.stage()215 if err is not None:216 return err217 err = write_file(os.path.join(self.sysfs_dir(), 'apply_interval_us'),218 '%d' % self.apply_interval_us)219 if err is not None:220 return err221 222 err = self.quota.stage()223 if err is not None:224 return err225 226 # disable watermarks227 err = write_file(228 os.path.join(self.sysfs_dir(), 'watermarks', 'metric'), 'none')229 if err is not None:230 return err231 232 # disable filters233 err = write_file(234 os.path.join(self.sysfs_dir(), 'filters', 'nr_filters'), '0')235 if err is not None:236 return err237 238class DamonTarget:239 pid = None240 # todo: Support target regions if test is made241 idx = None242 context = None243 244 def __init__(self, pid):245 self.pid = pid246 247 def sysfs_dir(self):248 return os.path.join(249 self.context.sysfs_dir(), 'targets', '%d' % self.idx)250 251 def stage(self):252 err = write_file(253 os.path.join(self.sysfs_dir(), 'regions', 'nr_regions'), '0')254 if err is not None:255 return err256 return write_file(257 os.path.join(self.sysfs_dir(), 'pid_target'), self.pid)258 259class DamonAttrs:260 sample_us = None261 aggr_us = None262 update_us = None263 min_nr_regions = None264 max_nr_regions = None265 context = None266 267 def __init__(self, sample_us=5000, aggr_us=100000, update_us=1000000,268 min_nr_regions=10, max_nr_regions=1000):269 self.sample_us = sample_us270 self.aggr_us = aggr_us271 self.update_us = update_us272 self.min_nr_regions = min_nr_regions273 self.max_nr_regions = max_nr_regions274 275 def interval_sysfs_dir(self):276 return os.path.join(self.context.sysfs_dir(), 'monitoring_attrs',277 'intervals')278 279 def nr_regions_range_sysfs_dir(self):280 return os.path.join(self.context.sysfs_dir(), 'monitoring_attrs',281 'nr_regions')282 283 def stage(self):284 err = write_file(os.path.join(self.interval_sysfs_dir(), 'sample_us'),285 self.sample_us)286 if err is not None:287 return err288 err = write_file(os.path.join(self.interval_sysfs_dir(), 'aggr_us'),289 self.aggr_us)290 if err is not None:291 return err292 err = write_file(os.path.join(self.interval_sysfs_dir(), 'update_us'),293 self.update_us)294 if err is not None:295 return err296 297 err = write_file(298 os.path.join(self.nr_regions_range_sysfs_dir(), 'min'),299 self.min_nr_regions)300 if err is not None:301 return err302 303 err = write_file(304 os.path.join(self.nr_regions_range_sysfs_dir(), 'max'),305 self.max_nr_regions)306 if err is not None:307 return err308 309class DamonCtx:310 ops = None311 monitoring_attrs = None312 targets = None313 schemes = None314 kdamond = None315 idx = None316 317 def __init__(self, ops='paddr', monitoring_attrs=DamonAttrs(), targets=[],318 schemes=[]):319 self.ops = ops320 self.monitoring_attrs = monitoring_attrs321 self.monitoring_attrs.context = self322 323 self.targets = targets324 for idx, target in enumerate(self.targets):325 target.idx = idx326 target.context = self327 328 self.schemes = schemes329 for idx, scheme in enumerate(self.schemes):330 scheme.idx = idx331 scheme.context = self332 333 def sysfs_dir(self):334 return os.path.join(self.kdamond.sysfs_dir(), 'contexts',335 '%d' % self.idx)336 337 def stage(self):338 err = write_file(339 os.path.join(self.sysfs_dir(), 'operations'), self.ops)340 if err is not None:341 return err342 err = self.monitoring_attrs.stage()343 if err is not None:344 return err345 346 nr_targets_file = os.path.join(347 self.sysfs_dir(), 'targets', 'nr_targets')348 content, err = read_file(nr_targets_file)349 if err is not None:350 return err351 if int(content) != len(self.targets):352 err = write_file(nr_targets_file, '%d' % len(self.targets))353 if err is not None:354 return err355 for target in self.targets:356 err = target.stage()357 if err is not None:358 return err359 360 nr_schemes_file = os.path.join(361 self.sysfs_dir(), 'schemes', 'nr_schemes')362 content, err = read_file(nr_schemes_file)363 if err is not None:364 return err365 if int(content) != len(self.schemes):366 err = write_file(nr_schemes_file, '%d' % len(self.schemes))367 if err is not None:368 return err369 for scheme in self.schemes:370 err = scheme.stage()371 if err is not None:372 return err373 return None374 375class Kdamond:376 state = None377 pid = None378 contexts = None379 idx = None # index of this kdamond between siblings380 kdamonds = None # parent381 382 def __init__(self, contexts=[]):383 self.contexts = contexts384 for idx, context in enumerate(self.contexts):385 context.idx = idx386 context.kdamond = self387 388 def sysfs_dir(self):389 return os.path.join(self.kdamonds.sysfs_dir(), '%d' % self.idx)390 391 def start(self):392 nr_contexts_file = os.path.join(self.sysfs_dir(),393 'contexts', 'nr_contexts')394 content, err = read_file(nr_contexts_file)395 if err is not None:396 return err397 if int(content) != len(self.contexts):398 err = write_file(nr_contexts_file, '%d' % len(self.contexts))399 if err is not None:400 return err401 402 for context in self.contexts:403 err = context.stage()404 if err is not None:405 return err406 err = write_file(os.path.join(self.sysfs_dir(), 'state'), 'on')407 return err408 409 def stop(self):410 err = write_file(os.path.join(self.sysfs_dir(), 'state'), 'off')411 return err412 413 def update_schemes_tried_regions(self):414 err = write_file(os.path.join(self.sysfs_dir(), 'state'),415 'update_schemes_tried_regions')416 if err is not None:417 return err418 for context in self.contexts:419 for scheme in context.schemes:420 tried_regions = []421 tried_regions_dir = os.path.join(422 scheme.sysfs_dir(), 'tried_regions')423 for filename in os.listdir(424 os.path.join(scheme.sysfs_dir(), 'tried_regions')):425 tried_region_dir = os.path.join(tried_regions_dir, filename)426 if not os.path.isdir(tried_region_dir):427 continue428 region_values = []429 for f in ['start', 'end', 'nr_accesses', 'age']:430 content, err = read_file(431 os.path.join(tried_region_dir, f))432 if err is not None:433 return err434 region_values.append(int(content))435 tried_regions.append(DamosTriedRegion(*region_values))436 scheme.tried_regions = tried_regions437 438 def update_schemes_tried_bytes(self):439 err = write_file(os.path.join(self.sysfs_dir(), 'state'),440 'update_schemes_tried_bytes')441 if err is not None:442 return err443 for context in self.contexts:444 for scheme in context.schemes:445 content, err = read_file(os.path.join(scheme.sysfs_dir(),446 'tried_regions', 'total_bytes'))447 if err is not None:448 return err449 scheme.tried_bytes = int(content)450 451 def update_schemes_stats(self):452 err = write_file(os.path.join(self.sysfs_dir(), 'state'),453 'update_schemes_stats')454 if err is not None:455 return err456 for context in self.contexts:457 for scheme in context.schemes:458 stat_values = []459 for stat in ['nr_tried', 'sz_tried', 'nr_applied',460 'sz_applied', 'qt_exceeds']:461 content, err = read_file(462 os.path.join(scheme.sysfs_dir(), 'stats', stat))463 if err is not None:464 return err465 stat_values.append(int(content))466 scheme.stats = DamosStats(*stat_values)467 468 def update_schemes_effective_quotas(self):469 err = write_file(os.path.join(self.sysfs_dir(), 'state'),470 'update_schemes_effective_quotas')471 if err is not None:472 return err473 for context in self.contexts:474 for scheme in context.schemes:475 for goal in scheme.quota.goals:476 content, err = read_file(477 os.path.join(scheme.quota.sysfs_dir(),478 'effective_bytes'))479 if err is not None:480 return err481 goal.effective_bytes = int(content)482 return None483 484 def commit(self):485 nr_contexts_file = os.path.join(self.sysfs_dir(),486 'contexts', 'nr_contexts')487 content, err = read_file(nr_contexts_file)488 if err is not None:489 return err490 if int(content) != len(self.contexts):491 err = write_file(nr_contexts_file, '%d' % len(self.contexts))492 if err is not None:493 return err494 495 for context in self.contexts:496 err = context.stage()497 if err is not None:498 return err499 err = write_file(os.path.join(self.sysfs_dir(), 'state'), 'commit')500 return err501 502 503 def commit_schemes_quota_goals(self):504 for context in self.contexts:505 for scheme in context.schemes:506 for goal in scheme.quota.goals:507 err = goal.stage()508 if err is not None:509 print('commit_schemes_quota_goals failed stagign: %s'%510 err)511 exit(1)512 return write_file(os.path.join(self.sysfs_dir(), 'state'),513 'commit_schemes_quota_goals')514 515class Kdamonds:516 kdamonds = []517 518 def __init__(self, kdamonds=[]):519 self.kdamonds = kdamonds520 for idx, kdamond in enumerate(self.kdamonds):521 kdamond.idx = idx522 kdamond.kdamonds = self523 524 def sysfs_dir(self):525 return os.path.join(sysfs_root, 'kdamonds')526 527 def start(self):528 err = write_file(os.path.join(self.sysfs_dir(), 'nr_kdamonds'),529 '%s' % len(self.kdamonds))530 if err is not None:531 return err532 for kdamond in self.kdamonds:533 err = kdamond.start()534 if err is not None:535 return err536 return None537 538 def stop(self):539 for kdamond in self.kdamonds:540 err = kdamond.stop()541 if err is not None:542 return err543 return None544