1# SPDX-License-Identifier: GPL-2.0 2 3import os 4 5ksft_skip=4 6 7sysfs_root = None 8with 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_point 13 break 14if sysfs_root is None: 15 print('Seems sysfs not mounted?') 16 exit(ksft_skip) 17 18if not os.path.exists(sysfs_root): 19 print('Seems DAMON disabled?') 20 exit(ksft_skip) 21 22def write_file(path, string): 23 "Returns error string if failed, or None otherwise" 24 string = '%s' % string 25 try: 26 with open(path, 'w') as f: 27 f.write(string) 28 except Exception as e: 29 return '%s' % e 30 return None 31 32def read_file(path): 33 '''Returns the read content and error string. The read content is None if 34 the reading failed''' 35 try: 36 with open(path, 'r') as f: 37 return f.read(), None 38 except Exception as e: 39 return None, '%s' % e 40 41class DamosAccessPattern: 42 size = None 43 nr_accesses = None 44 age = None 45 scheme = None 46 47 def __init__(self, size=None, nr_accesses=None, age=None): 48 self.size = size 49 self.nr_accesses = nr_accesses 50 self.age = age 51 52 if self.size is None: 53 self.size = [0, 2**64 - 1] 54 if self.nr_accesses is None: 55 self.nr_accesses = [0, 2**32 - 1] 56 if self.age is None: 57 self.age = [0, 2**32 - 1] 58 59 def sysfs_dir(self): 60 return os.path.join(self.scheme.sysfs_dir(), 'access_pattern') 61 62 def stage(self): 63 err = write_file( 64 os.path.join(self.sysfs_dir(), 'sz', 'min'), self.size[0]) 65 if err is not None: 66 return err 67 err = write_file( 68 os.path.join(self.sysfs_dir(), 'sz', 'max'), self.size[1]) 69 if err is not None: 70 return err 71 err = write_file(os.path.join(self.sysfs_dir(), 'nr_accesses', 'min'), 72 self.nr_accesses[0]) 73 if err is not None: 74 return err 75 err = write_file(os.path.join(self.sysfs_dir(), 'nr_accesses', 'max'), 76 self.nr_accesses[1]) 77 if err is not None: 78 return err 79 err = write_file( 80 os.path.join(self.sysfs_dir(), 'age', 'min'), self.age[0]) 81 if err is not None: 82 return err 83 err = write_file( 84 os.path.join(self.sysfs_dir(), 'age', 'max'), self.age[1]) 85 if err is not None: 86 return err 87 88qgoal_metric_user_input = 'user_input' 89qgoal_metric_some_mem_psi_us = 'some_mem_psi_us' 90qgoal_metrics = [qgoal_metric_user_input, qgoal_metric_some_mem_psi_us] 91 92class DamosQuotaGoal: 93 metric = None 94 target_value = None 95 current_value = None 96 nid = None 97 effective_bytes = None 98 quota = None # owner quota 99 idx = None 100 101 def __init__(self, metric, target_value=10000, current_value=0, nid=0): 102 self.metric = metric 103 self.target_value = target_value 104 self.current_value = current_value 105 self.nid = nid 106 107 def sysfs_dir(self): 108 return os.path.join(self.quota.sysfs_dir(), 'goals', '%d' % self.idx) 109 110 def stage(self): 111 err = write_file(os.path.join(self.sysfs_dir(), 'target_metric'), 112 self.metric) 113 if err is not None: 114 return err 115 err = write_file(os.path.join(self.sysfs_dir(), 'target_value'), 116 self.target_value) 117 if err is not None: 118 return err 119 err = write_file(os.path.join(self.sysfs_dir(), 'current_value'), 120 self.current_value) 121 if err is not None: 122 return err 123 err = write_file(os.path.join(self.sysfs_dir(), 'nid'), self.nid) 124 if err is not None: 125 return err 126 127 return None 128 129class DamosQuota: 130 sz = None # size quota, in bytes 131 ms = None # time quota 132 goals = None # quota goals 133 goal_tuner = None # quota goal tuner 134 reset_interval_ms = None # quota reset interval 135 fail_charge_num = None 136 fail_charge_denom = None 137 weight_sz_permil = None 138 weight_nr_accesses_permil = None 139 weight_age_permil = None 140 scheme = None # owner scheme 141 142 def __init__(self, sz=0, ms=0, goals=None, goal_tuner='consist', 143 reset_interval_ms=0, fail_charge_num=0, fail_charge_denom=0, 144 weight_sz_permil=0, weight_nr_accesses_permil=0, 145 weight_age_permil=0): 146 self.sz = sz 147 self.ms = ms 148 self.reset_interval_ms = reset_interval_ms 149 self.weight_sz_permil = weight_sz_permil 150 self.weight_nr_accesses_permil = weight_nr_accesses_permil 151 self.weight_age_permil = weight_age_permil 152 self.goals = goals if goals is not None else [] 153 self.goal_tuner = goal_tuner 154 for idx, goal in enumerate(self.goals): 155 goal.idx = idx 156 goal.quota = self 157 self.fail_charge_num = fail_charge_num 158 self.fail_charge_denom = fail_charge_denom 159 160 def sysfs_dir(self): 161 return os.path.join(self.scheme.sysfs_dir(), 'quotas') 162 163 def stage(self): 164 err = write_file(os.path.join(self.sysfs_dir(), 'bytes'), self.sz) 165 if err is not None: 166 return err 167 err = write_file(os.path.join(self.sysfs_dir(), 'ms'), self.ms) 168 if err is not None: 169 return err 170 err = write_file(os.path.join(self.sysfs_dir(), 'reset_interval_ms'), 171 self.reset_interval_ms) 172 if err is not None: 173 return err 174 175 err = write_file(os.path.join( 176 self.sysfs_dir(), 'weights', 'sz_permil'), self.weight_sz_permil) 177 if err is not None: 178 return err 179 err = write_file(os.path.join( 180 self.sysfs_dir(), 'weights', 'nr_accesses_permil'), 181 self.weight_nr_accesses_permil) 182 if err is not None: 183 return err 184 err = write_file(os.path.join( 185 self.sysfs_dir(), 'weights', 'age_permil'), self.weight_age_permil) 186 if err is not None: 187 return err 188 189 nr_goals_file = os.path.join(self.sysfs_dir(), 'goals', 'nr_goals') 190 content, err = read_file(nr_goals_file) 191 if err is not None: 192 return err 193 if int(content) != len(self.goals): 194 err = write_file(nr_goals_file, len(self.goals)) 195 if err is not None: 196 return err 197 for goal in self.goals: 198 err = goal.stage() 199 if err is not None: 200 return err 201 err = write_file( 202 os.path.join(self.sysfs_dir(), 'goal_tuner'), self.goal_tuner) 203 if err is not None: 204 return err 205 206 err = write_file( 207 os.path.join(self.sysfs_dir(), 'fail_charge_num'), 208 self.fail_charge_num) 209 if err is not None: 210 return err 211 err = write_file( 212 os.path.join(self.sysfs_dir(), 'fail_charge_denom'), 213 self.fail_charge_denom) 214 if err is not None: 215 return err 216 217 return None 218 219class DamosWatermarks: 220 metric = None 221 interval = None 222 high = None 223 mid = None 224 low = None 225 scheme = None # owner scheme 226 227 def __init__(self, metric='none', interval=0, high=0, mid=0, low=0): 228 self.metric = metric 229 self.interval = interval 230 self.high = high 231 self.mid = mid 232 self.low = low 233 234 def sysfs_dir(self): 235 return os.path.join(self.scheme.sysfs_dir(), 'watermarks') 236 237 def stage(self): 238 err = write_file(os.path.join(self.sysfs_dir(), 'metric'), self.metric) 239 if err is not None: 240 return err 241 err = write_file(os.path.join(self.sysfs_dir(), 'interval_us'), 242 self.interval) 243 if err is not None: 244 return err 245 err = write_file(os.path.join(self.sysfs_dir(), 'high'), self.high) 246 if err is not None: 247 return err 248 err = write_file(os.path.join(self.sysfs_dir(), 'mid'), self.mid) 249 if err is not None: 250 return err 251 err = write_file(os.path.join(self.sysfs_dir(), 'low'), self.low) 252 if err is not None: 253 return err 254 255class DamosFilter: 256 type_ = None 257 matching = None 258 allow = None 259 memcg_path = None 260 addr_start = None 261 addr_end = None 262 target_idx = None 263 min_ = None 264 max_ = None 265 idx = None 266 filters = None # owner filters 267 268 def __init__(self, type_='anon', matching=False, allow=False, 269 memcg_path='', addr_start=0, addr_end=0, target_idx=0, min_=0, 270 max_=0): 271 self.type_ = type_ 272 self.matching = matching 273 self.allow = allow 274 self.memcg_path = memcg_path 275 self.addr_start = addr_start 276 self.addr_end = addr_end 277 self.target_idx = target_idx 278 self.min_ = min_ 279 self.max_ = max_ 280 281 def sysfs_dir(self): 282 return os.path.join(self.filters.sysfs_dir(), '%d' % self.idx) 283 284 def stage(self): 285 err = write_file(os.path.join(self.sysfs_dir(), 'type'), self.type_) 286 if err is not None: 287 return err 288 err = write_file(os.path.join(self.sysfs_dir(), 'matching'), 289 self.matching) 290 if err is not None: 291 return err 292 err = write_file(os.path.join(self.sysfs_dir(), 'allow'), self.allow) 293 if err is not None: 294 return err 295 err = write_file(os.path.join(self.sysfs_dir(), 'memcg_path'), 296 self.memcg_path) 297 if err is not None: 298 return err 299 err = write_file(os.path.join(self.sysfs_dir(), 'addr_start'), 300 self.addr_start) 301 if err is not None: 302 return err 303 err = write_file(os.path.join(self.sysfs_dir(), 'addr_end'), 304 self.addr_end) 305 if err is not None: 306 return err 307 err = write_file(os.path.join(self.sysfs_dir(), 'damon_target_idx'), 308 self.target_idx) 309 if err is not None: 310 return err 311 err = write_file(os.path.join(self.sysfs_dir(), 'min'), self.min_) 312 if err is not None: 313 return err 314 err = write_file(os.path.join(self.sysfs_dir(), 'max'), self.max_) 315 if err is not None: 316 return err 317 return None 318 319class DamosFilters: 320 name = None 321 filters = None 322 scheme = None # owner scheme 323 324 def __init__(self, name, filters=None): 325 self.name = name 326 if filters is None: 327 filters = [] 328 self.filters = filters 329 for idx, filter_ in enumerate(self.filters): 330 filter_.idx = idx 331 filter_.filters = self 332 333 def sysfs_dir(self): 334 return os.path.join(self.scheme.sysfs_dir(), self.name) 335 336 def stage(self): 337 err = write_file(os.path.join(self.sysfs_dir(), 'nr_filters'), 338 len(self.filters)) 339 if err is not None: 340 return err 341 for filter_ in self.filters: 342 err = filter_.stage() 343 if err is not None: 344 return err 345 return None 346 347class DamosDest: 348 id = None 349 weight = None 350 idx = None 351 dests = None # owner dests 352 353 def __init__(self, id=0, weight=0): 354 self.id = id 355 self.weight = weight 356 357 def sysfs_dir(self): 358 return os.path.join(self.dests.sysfs_dir(), '%d' % self.idx) 359 360 def stage(self): 361 err = write_file(os.path.join(self.sysfs_dir(), 'id'), self.id) 362 if err is not None: 363 return err 364 err = write_file(os.path.join(self.sysfs_dir(), 'weight'), self.weight) 365 if err is not None: 366 return err 367 return None 368 369class DamosDests: 370 dests = None 371 scheme = None # owner scheme 372 373 def __init__(self, dests=None): 374 if dests is None: 375 dests = [] 376 self.dests = dests 377 for idx, dest in enumerate(self.dests): 378 dest.idx = idx 379 dest.dests = self 380 381 def sysfs_dir(self): 382 return os.path.join(self.scheme.sysfs_dir(), 'dests') 383 384 def stage(self): 385 err = write_file(os.path.join(self.sysfs_dir(), 'nr_dests'), 386 len(self.dests)) 387 if err is not None: 388 return err 389 for dest in self.dests: 390 err = dest.stage() 391 if err is not None: 392 return err 393 return None 394 395class DamosStats: 396 nr_tried = None 397 sz_tried = None 398 nr_applied = None 399 sz_applied = None 400 qt_exceeds = None 401 402 def __init__(self, nr_tried, sz_tried, nr_applied, sz_applied, qt_exceeds): 403 self.nr_tried = nr_tried 404 self.sz_tried = sz_tried 405 self.nr_applied = nr_applied 406 self.sz_applied = sz_applied 407 self.qt_exceeds = qt_exceeds 408 409class DamosTriedRegion: 410 def __init__(self, start, end, nr_accesses, age): 411 self.start = start 412 self.end = end 413 self.nr_accesses = nr_accesses 414 self.age = age 415 416class Damos: 417 action = None 418 access_pattern = None 419 quota = None 420 watermarks = None 421 core_filters = None 422 ops_filters = None 423 filters = None 424 apply_interval_us = None 425 target_nid = None 426 dests = None 427 idx = None 428 context = None 429 tried_bytes = None 430 stats = None 431 tried_regions = None 432 433 def __init__(self, action='stat', access_pattern=None, quota=None, 434 watermarks=None, core_filters=None, ops_filters=None, 435 filters=None, target_nid=0, dests=None, 436 apply_interval_us=0): 437 self.action = action 438 if access_pattern is None: 439 access_pattern = DamosAccessPattern() 440 self.access_pattern = access_pattern 441 self.access_pattern.scheme = self 442 if quota is None: 443 quota = DamosQuota() 444 self.quota = quota 445 self.quota.scheme = self 446 if watermarks is None: 447 watermarks = DamosWatermarks() 448 self.watermarks = watermarks 449 self.watermarks.scheme = self 450 451 self.core_filters = DamosFilters(name='core_filters', 452 filters=core_filters) 453 self.core_filters.scheme = self 454 self.ops_filters = DamosFilters(name='ops_filters', 455 filters=ops_filters) 456 self.ops_filters.scheme = self 457 self.filters = DamosFilters(name='filters', filters=filters) 458 self.filters.scheme = self 459 460 self.target_nid = target_nid 461 if dests is None: 462 dests = DamosDests() 463 self.dests = dests 464 self.dests.scheme = self 465 466 self.apply_interval_us = apply_interval_us 467 468 def sysfs_dir(self): 469 return os.path.join( 470 self.context.sysfs_dir(), 'schemes', '%d' % self.idx) 471 472 def stage(self): 473 err = write_file(os.path.join(self.sysfs_dir(), 'action'), self.action) 474 if err is not None: 475 return err 476 err = self.access_pattern.stage() 477 if err is not None: 478 return err 479 err = write_file(os.path.join(self.sysfs_dir(), 'apply_interval_us'), 480 '%d' % self.apply_interval_us) 481 if err is not None: 482 return err 483 484 err = self.quota.stage() 485 if err is not None: 486 return err 487 488 err = self.watermarks.stage() 489 if err is not None: 490 return err 491 492 err = self.core_filters.stage() 493 if err is not None: 494 return err 495 err = self.ops_filters.stage() 496 if err is not None: 497 return err 498 err = self.filters.stage() 499 if err is not None: 500 return err 501 502 err = write_file(os.path.join(self.sysfs_dir(), 'target_nid'), '%d' % 503 self.target_nid) 504 if err is not None: 505 return err 506 507 err = self.dests.stage() 508 if err is not None: 509 return err 510 511class DamonTarget: 512 pid = None 513 obsolete = None 514 # todo: Support target regions if test is made 515 idx = None 516 context = None 517 518 def __init__(self, pid, obsolete=False): 519 self.pid = pid 520 self.obsolete = obsolete 521 522 def sysfs_dir(self): 523 return os.path.join( 524 self.context.sysfs_dir(), 'targets', '%d' % self.idx) 525 526 def stage(self): 527 err = write_file( 528 os.path.join(self.sysfs_dir(), 'regions', 'nr_regions'), '0') 529 if err is not None: 530 return err 531 err = write_file( 532 os.path.join(self.sysfs_dir(), 'pid_target'), self.pid) 533 if err is not None: 534 return err 535 return write_file( 536 os.path.join(self.sysfs_dir(), 'obsolete_target'), 537 'Y' if self.obsolete else 'N') 538 539class IntervalsGoal: 540 access_bp = None 541 aggrs = None 542 min_sample_us = None 543 max_sample_us = None 544 attrs = None # owner DamonAttrs 545 546 def __init__(self, access_bp=0, aggrs=0, min_sample_us=0, max_sample_us=0): 547 self.access_bp = access_bp 548 self.aggrs = aggrs 549 self.min_sample_us = min_sample_us 550 self.max_sample_us = max_sample_us 551 552 def sysfs_dir(self): 553 return os.path.join(self.attrs.interval_sysfs_dir(), 'intervals_goal') 554 555 def stage(self): 556 err = write_file( 557 os.path.join(self.sysfs_dir(), 'access_bp'), self.access_bp) 558 if err is not None: 559 return err 560 err = write_file(os.path.join(self.sysfs_dir(), 'aggrs'), self.aggrs) 561 if err is not None: 562 return err 563 err = write_file(os.path.join(self.sysfs_dir(), 'min_sample_us'), 564 self.min_sample_us) 565 if err is not None: 566 return err 567 err = write_file(os.path.join(self.sysfs_dir(), 'max_sample_us'), 568 self.max_sample_us) 569 if err is not None: 570 return err 571 return None 572 573class DamonFilter: 574 type_ = None 575 matching = None 576 allow = None 577 filters = None 578 path = None 579 idx = None 580 581 def __init__(self, type_='anon', matching=False, allow=False, path=None): 582 self.type_ = type_ 583 self.matching = matching 584 self.allow = allow 585 self.path = path 586 587 def sysfs_dir(self): 588 return os.path.join(self.filters.sysfs_dir(), '%d' % self.idx) 589 590 def stage(self): 591 err = write_file(os.path.join(self.sysfs_dir(), 'type'), self.type_) 592 if err is not None: 593 return err 594 err = write_file(os.path.join(self.sysfs_dir(), 'matching'), 595 'Y' if self.matching else 'N') 596 if err is not None: 597 return err 598 err = write_file(os.path.join(self.sysfs_dir(), 'allow'), 599 'Y' if self.allow else 'N') 600 if err is not None: 601 return err 602 if self.type_ == 'memcg': 603 err = write_file(os.path.join(self.sysfs_dir(), 'path'), self.path) 604 if err is not None: 605 return err 606 return None 607 608class DamonFilters: 609 filters = None 610 probe = None 611 612 def __init__(self, filters=None): 613 if filters is None: 614 filters = [] 615 self.filters = filters 616 for idx, filter in enumerate(self.filters): 617 filter.filters = self 618 filter.idx = idx 619 620 def sysfs_dir(self): 621 return os.path.join(self.probe.sysfs_dir(), 'filters') 622 623 def stage(self): 624 err = write_file( 625 os.path.join(self.sysfs_dir(), 'nr_filters'), 626 len(self.filters)) 627 if err is not None: 628 return err 629 for filter in self.filters: 630 err = filter.stage() 631 if err is not None: 632 return err 633 return None 634 635class DamonProbe: 636 weight = None 637 filters = None 638 probes = None 639 idx = None 640 641 def __init__(self, weight=0, filters=None): 642 self.weight = weight 643 if filters is None: 644 filters = DamonFilters() 645 self.filters = filters 646 self.filters.probe = self 647 648 def sysfs_dir(self): 649 return os.path.join(self.probes.sysfs_dir(), '%d' % self.idx) 650 651 def stage(self): 652 err = write_file( 653 os.path.join(self.sysfs_dir(), 'weight'), '%d' % self.weight) 654 if err is not None: 655 return err 656 return self.filters.stage() 657 658class DamonProbes: 659 probes = None 660 attrs = None 661 662 def __init__(self, probes=None): 663 if probes is None: 664 probes = [] 665 self.probes = probes 666 for idx, probe in enumerate(self.probes): 667 probe.probes = self 668 probe.idx = idx 669 670 def sysfs_dir(self): 671 return os.path.join(self.attrs.sysfs_dir(), 'probes') 672 673 def stage(self): 674 err = write_file(os.path.join(self.sysfs_dir(), 'nr_probes'), 675 len(self.probes)) 676 if err is not None: 677 return err 678 for probe in self.probes: 679 err = probe.stage() 680 if err is not None: 681 return err 682 return None 683 684class DamonAttrs: 685 sample_us = None 686 aggr_us = None 687 intervals_goal = None 688 update_us = None 689 min_nr_regions = None 690 max_nr_regions = None 691 probes = None 692 context = None 693 694 def __init__(self, sample_us=5000, aggr_us=100000, 695 intervals_goal=None, update_us=1000000, min_nr_regions=10, 696 max_nr_regions=1000, probes=None): 697 self.sample_us = sample_us 698 self.aggr_us = aggr_us 699 if intervals_goal is None: 700 intervals_goal = IntervalsGoal() 701 self.intervals_goal = intervals_goal 702 self.intervals_goal.attrs = self 703 self.update_us = update_us 704 self.min_nr_regions = min_nr_regions 705 self.max_nr_regions = max_nr_regions 706 if probes is None: 707 probes = DamonProbes() 708 self.probes = probes 709 self.probes.attrs = self 710 711 def interval_sysfs_dir(self): 712 return os.path.join(self.context.sysfs_dir(), 'monitoring_attrs', 713 'intervals') 714 715 def nr_regions_range_sysfs_dir(self): 716 return os.path.join(self.context.sysfs_dir(), 'monitoring_attrs', 717 'nr_regions') 718 719 def sysfs_dir(self): 720 return os.path.join(self.context.sysfs_dir(), 'monitoring_attrs') 721 722 def stage(self): 723 err = write_file(os.path.join(self.interval_sysfs_dir(), 'sample_us'), 724 self.sample_us) 725 if err is not None: 726 return err 727 err = write_file(os.path.join(self.interval_sysfs_dir(), 'aggr_us'), 728 self.aggr_us) 729 if err is not None: 730 return err 731 err = self.intervals_goal.stage() 732 if err is not None: 733 return err 734 err = write_file(os.path.join(self.interval_sysfs_dir(), 'update_us'), 735 self.update_us) 736 if err is not None: 737 return err 738 739 err = write_file( 740 os.path.join(self.nr_regions_range_sysfs_dir(), 'min'), 741 self.min_nr_regions) 742 if err is not None: 743 return err 744 745 err = write_file( 746 os.path.join(self.nr_regions_range_sysfs_dir(), 'max'), 747 self.max_nr_regions) 748 if err is not None: 749 return err 750 751 err = self.probes.stage() 752 if err is not None: 753 return err 754 755class DamonCtx: 756 ops = None 757 monitoring_attrs = None 758 targets = None 759 schemes = None 760 kdamond = None 761 pause = None 762 idx = None 763 764 def __init__(self, ops='paddr', monitoring_attrs=None, targets=None, 765 schemes=None, pause=False): 766 self.ops = ops 767 if monitoring_attrs is None: 768 monitoring_attrs = DamonAttrs() 769 self.monitoring_attrs = monitoring_attrs 770 self.monitoring_attrs.context = self 771 772 if targets is None: 773 targets = [] 774 self.targets = targets 775 for idx, target in enumerate(self.targets): 776 target.idx = idx 777 target.context = self 778 779 if schemes is None: 780 schemes = [] 781 self.schemes = schemes 782 for idx, scheme in enumerate(self.schemes): 783 scheme.idx = idx 784 scheme.context = self 785 786 self.pause=pause 787 788 def sysfs_dir(self): 789 return os.path.join(self.kdamond.sysfs_dir(), 'contexts', 790 '%d' % self.idx) 791 792 def stage(self): 793 err = write_file( 794 os.path.join(self.sysfs_dir(), 'operations'), self.ops) 795 if err is not None: 796 return err 797 err = self.monitoring_attrs.stage() 798 if err is not None: 799 return err 800 801 nr_targets_file = os.path.join( 802 self.sysfs_dir(), 'targets', 'nr_targets') 803 content, err = read_file(nr_targets_file) 804 if err is not None: 805 return err 806 if int(content) != len(self.targets): 807 err = write_file(nr_targets_file, '%d' % len(self.targets)) 808 if err is not None: 809 return err 810 for target in self.targets: 811 err = target.stage() 812 if err is not None: 813 return err 814 815 nr_schemes_file = os.path.join( 816 self.sysfs_dir(), 'schemes', 'nr_schemes') 817 content, err = read_file(nr_schemes_file) 818 if err is not None: 819 return err 820 if int(content) != len(self.schemes): 821 err = write_file(nr_schemes_file, '%d' % len(self.schemes)) 822 if err is not None: 823 return err 824 for scheme in self.schemes: 825 err = scheme.stage() 826 if err is not None: 827 return err 828 829 err = write_file(os.path.join(self.sysfs_dir(), 'pause'), self.pause) 830 if err is not None: 831 return err 832 833 return None 834 835class Kdamond: 836 state = None 837 pid = None 838 refresh_ms = None 839 contexts = None 840 idx = None # index of this kdamond between siblings 841 kdamonds = None # parent 842 843 def __init__(self, contexts=None, refresh_ms=None): 844 if contexts is None: 845 contexts = [] 846 self.contexts = contexts 847 self.refresh_ms = refresh_ms 848 for idx, context in enumerate(self.contexts): 849 context.idx = idx 850 context.kdamond = self 851 852 def sysfs_dir(self): 853 return os.path.join(self.kdamonds.sysfs_dir(), '%d' % self.idx) 854 855 def start(self): 856 nr_contexts_file = os.path.join(self.sysfs_dir(), 857 'contexts', 'nr_contexts') 858 content, err = read_file(nr_contexts_file) 859 if err is not None: 860 return err 861 if int(content) != len(self.contexts): 862 err = write_file(nr_contexts_file, '%d' % len(self.contexts)) 863 if err is not None: 864 return err 865 866 for context in self.contexts: 867 err = context.stage() 868 if err is not None: 869 return err 870 if self.refresh_ms is not None: 871 err = write_file(os.path.join(self.sysfs_dir(), 'refresh_ms'), 872 '%d' % self.refresh_ms) 873 if err is not None: 874 return err 875 err = write_file(os.path.join(self.sysfs_dir(), 'state'), 'on') 876 if err is not None: 877 return err 878 self.pid, err = read_file(os.path.join(self.sysfs_dir(), 'pid')) 879 return err 880 881 def stop(self): 882 err = write_file(os.path.join(self.sysfs_dir(), 'state'), 'off') 883 return err 884 885 def update_schemes_tried_regions(self): 886 err = write_file(os.path.join(self.sysfs_dir(), 'state'), 887 'update_schemes_tried_regions') 888 if err is not None: 889 return err 890 for context in self.contexts: 891 for scheme in context.schemes: 892 tried_regions = [] 893 tried_regions_dir = os.path.join( 894 scheme.sysfs_dir(), 'tried_regions') 895 region_indices = [] 896 for filename in os.listdir( 897 os.path.join(scheme.sysfs_dir(), 'tried_regions')): 898 tried_region_dir = os.path.join(tried_regions_dir, filename) 899 if not os.path.isdir(tried_region_dir): 900 continue 901 region_indices.append(int(filename)) 902 for region_idx in sorted(region_indices): 903 tried_region_dir = os.path.join(tried_regions_dir, 904 '%d' % region_idx) 905 region_values = [] 906 for f in ['start', 'end', 'nr_accesses', 'age']: 907 content, err = read_file( 908 os.path.join(tried_region_dir, f)) 909 if err is not None: 910 return err 911 region_values.append(int(content)) 912 tried_regions.append(DamosTriedRegion(*region_values)) 913 scheme.tried_regions = tried_regions 914 915 def update_schemes_tried_bytes(self): 916 err = write_file(os.path.join(self.sysfs_dir(), 'state'), 917 'update_schemes_tried_bytes') 918 if err is not None: 919 return err 920 for context in self.contexts: 921 for scheme in context.schemes: 922 content, err = read_file(os.path.join(scheme.sysfs_dir(), 923 'tried_regions', 'total_bytes')) 924 if err is not None: 925 return err 926 scheme.tried_bytes = int(content) 927 928 def update_schemes_stats(self): 929 err = write_file(os.path.join(self.sysfs_dir(), 'state'), 930 'update_schemes_stats') 931 if err is not None: 932 return err 933 for context in self.contexts: 934 for scheme in context.schemes: 935 stat_values = [] 936 for stat in ['nr_tried', 'sz_tried', 'nr_applied', 937 'sz_applied', 'qt_exceeds']: 938 content, err = read_file( 939 os.path.join(scheme.sysfs_dir(), 'stats', stat)) 940 if err is not None: 941 return err 942 stat_values.append(int(content)) 943 scheme.stats = DamosStats(*stat_values) 944 945 def update_schemes_effective_quotas(self): 946 err = write_file(os.path.join(self.sysfs_dir(), 'state'), 947 'update_schemes_effective_quotas') 948 if err is not None: 949 return err 950 for context in self.contexts: 951 for scheme in context.schemes: 952 for goal in scheme.quota.goals: 953 content, err = read_file( 954 os.path.join(scheme.quota.sysfs_dir(), 955 'effective_bytes')) 956 if err is not None: 957 return err 958 goal.effective_bytes = int(content) 959 return None 960 961 def commit(self): 962 nr_contexts_file = os.path.join(self.sysfs_dir(), 963 'contexts', 'nr_contexts') 964 content, err = read_file(nr_contexts_file) 965 if err is not None: 966 return err 967 if int(content) != len(self.contexts): 968 err = write_file(nr_contexts_file, '%d' % len(self.contexts)) 969 if err is not None: 970 return err 971 972 for context in self.contexts: 973 err = context.stage() 974 if err is not None: 975 return err 976 err = write_file(os.path.join(self.sysfs_dir(), 'state'), 'commit') 977 return err 978 979 980 def commit_schemes_quota_goals(self): 981 for context in self.contexts: 982 for scheme in context.schemes: 983 for goal in scheme.quota.goals: 984 err = goal.stage() 985 if err is not None: 986 print('commit_schemes_quota_goals failed staging: %s'% 987 err) 988 exit(1) 989 return write_file(os.path.join(self.sysfs_dir(), 'state'), 990 'commit_schemes_quota_goals') 991 992class Kdamonds: 993 kdamonds = [] 994 995 def __init__(self, kdamonds=None): 996 if kdamonds is None: 997 kdamonds = [] 998 self.kdamonds = kdamonds 999 for idx, kdamond in enumerate(self.kdamonds): 1000 kdamond.idx = idx 1001 kdamond.kdamonds = self 1002 1003 def sysfs_dir(self): 1004 return os.path.join(sysfs_root, 'kdamonds') 1005 1006 def start(self): 1007 err = write_file(os.path.join(self.sysfs_dir(), 'nr_kdamonds'), 1008 '%s' % len(self.kdamonds)) 1009 if err is not None: 1010 return err 1011 for kdamond in self.kdamonds: 1012 err = kdamond.start() 1013 if err is not None: 1014 return err 1015 return None 1016 1017 def stop(self): 1018 for kdamond in self.kdamonds: 1019 err = kdamond.stop() 1020 if err is not None: 1021 return err 1022 return None 1023