xref: /linux/tools/testing/selftests/damon/_damon_sysfs.py (revision ed3b875bea55a3ec4837113356df2ead11115af9)
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=[]):
325        self.name = name
326        self.filters = filters
327        for idx, filter_ in enumerate(self.filters):
328            filter_.idx = idx
329            filter_.filters = self
330
331    def sysfs_dir(self):
332        return os.path.join(self.scheme.sysfs_dir(), self.name)
333
334    def stage(self):
335        err = write_file(os.path.join(self.sysfs_dir(), 'nr_filters'),
336                         len(self.filters))
337        if err is not None:
338            return err
339        for filter_ in self.filters:
340            err = filter_.stage()
341            if err is not None:
342                return err
343        return None
344
345class DamosDest:
346    id = None
347    weight = None
348    idx = None
349    dests = None    # owner dests
350
351    def __init__(self, id=0, weight=0):
352        self.id = id
353        self.weight = weight
354
355    def sysfs_dir(self):
356        return os.path.join(self.dests.sysfs_dir(), '%d' % self.idx)
357
358    def stage(self):
359        err = write_file(os.path.join(self.sysfs_dir(), 'id'), self.id)
360        if err is not None:
361            return err
362        err = write_file(os.path.join(self.sysfs_dir(), 'weight'), self.weight)
363        if err is not None:
364            return err
365        return None
366
367class DamosDests:
368    dests = None
369    scheme = None   # owner scheme
370
371    def __init__(self, dests=[]):
372        self.dests = dests
373        for idx, dest in enumerate(self.dests):
374            dest.idx = idx
375            dest.dests = self
376
377    def sysfs_dir(self):
378        return os.path.join(self.scheme.sysfs_dir(), 'dests')
379
380    def stage(self):
381        err = write_file(os.path.join(self.sysfs_dir(), 'nr_dests'),
382                         len(self.dests))
383        if err is not None:
384            return err
385        for dest in self.dests:
386            err = dest.stage()
387            if err is not None:
388                return err
389        return None
390
391class DamosStats:
392    nr_tried = None
393    sz_tried = None
394    nr_applied = None
395    sz_applied = None
396    qt_exceeds = None
397
398    def __init__(self, nr_tried, sz_tried, nr_applied, sz_applied, qt_exceeds):
399        self.nr_tried = nr_tried
400        self.sz_tried = sz_tried
401        self.nr_applied = nr_applied
402        self.sz_applied = sz_applied
403        self.qt_exceeds = qt_exceeds
404
405class DamosTriedRegion:
406    def __init__(self, start, end, nr_accesses, age):
407        self.start = start
408        self.end = end
409        self.nr_accesses = nr_accesses
410        self.age = age
411
412class Damos:
413    action = None
414    access_pattern = None
415    quota = None
416    watermarks = None
417    core_filters = None
418    ops_filters = None
419    filters = None
420    apply_interval_us = None
421    target_nid = None
422    dests = None
423    idx = None
424    context = None
425    tried_bytes = None
426    stats = None
427    tried_regions = None
428
429    def __init__(self, action='stat', access_pattern=DamosAccessPattern(),
430                 quota=DamosQuota(), watermarks=DamosWatermarks(),
431                 core_filters=[], ops_filters=[], filters=[], target_nid=0,
432                 dests=DamosDests(), apply_interval_us=0):
433        self.action = action
434        self.access_pattern = access_pattern
435        self.access_pattern.scheme = self
436        self.quota = quota
437        self.quota.scheme = self
438        self.watermarks = watermarks
439        self.watermarks.scheme = self
440
441        self.core_filters = DamosFilters(name='core_filters',
442                                         filters=core_filters)
443        self.core_filters.scheme = self
444        self.ops_filters = DamosFilters(name='ops_filters',
445                                         filters=ops_filters)
446        self.ops_filters.scheme = self
447        self.filters = DamosFilters(name='filters', filters=filters)
448        self.filters.scheme = self
449
450        self.target_nid = target_nid
451        self.dests = dests
452        self.dests.scheme = self
453
454        self.apply_interval_us = apply_interval_us
455
456    def sysfs_dir(self):
457        return os.path.join(
458                self.context.sysfs_dir(), 'schemes', '%d' % self.idx)
459
460    def stage(self):
461        err = write_file(os.path.join(self.sysfs_dir(), 'action'), self.action)
462        if err is not None:
463            return err
464        err = self.access_pattern.stage()
465        if err is not None:
466            return err
467        err = write_file(os.path.join(self.sysfs_dir(), 'apply_interval_us'),
468                         '%d' % self.apply_interval_us)
469        if err is not None:
470            return err
471
472        err = self.quota.stage()
473        if err is not None:
474            return err
475
476        err = self.watermarks.stage()
477        if err is not None:
478            return err
479
480        err = self.core_filters.stage()
481        if err is not None:
482            return err
483        err = self.ops_filters.stage()
484        if err is not None:
485            return err
486        err = self.filters.stage()
487        if err is not None:
488            return err
489
490        err = write_file(os.path.join(self.sysfs_dir(), 'target_nid'), '%d' %
491                         self.target_nid)
492        if err is not None:
493            return err
494
495        err = self.dests.stage()
496        if err is not None:
497            return err
498
499class DamonTarget:
500    pid = None
501    obsolete = None
502    # todo: Support target regions if test is made
503    idx = None
504    context = None
505
506    def __init__(self, pid, obsolete=False):
507        self.pid = pid
508        self.obsolete = obsolete
509
510    def sysfs_dir(self):
511        return os.path.join(
512                self.context.sysfs_dir(), 'targets', '%d' % self.idx)
513
514    def stage(self):
515        err = write_file(
516                os.path.join(self.sysfs_dir(), 'regions', 'nr_regions'), '0')
517        if err is not None:
518            return err
519        err = write_file(
520                os.path.join(self.sysfs_dir(), 'pid_target'), self.pid)
521        if err is not None:
522            return err
523        return write_file(
524                os.path.join(self.sysfs_dir(), 'obsolete_target'),
525                'Y' if self.obsolete else 'N')
526
527class IntervalsGoal:
528    access_bp = None
529    aggrs = None
530    min_sample_us = None
531    max_sample_us = None
532    attrs = None    # owner DamonAttrs
533
534    def __init__(self, access_bp=0, aggrs=0, min_sample_us=0, max_sample_us=0):
535        self.access_bp = access_bp
536        self.aggrs = aggrs
537        self.min_sample_us = min_sample_us
538        self.max_sample_us = max_sample_us
539
540    def sysfs_dir(self):
541        return os.path.join(self.attrs.interval_sysfs_dir(), 'intervals_goal')
542
543    def stage(self):
544        err = write_file(
545                os.path.join(self.sysfs_dir(), 'access_bp'), self.access_bp)
546        if err is not None:
547            return err
548        err = write_file(os.path.join(self.sysfs_dir(), 'aggrs'), self.aggrs)
549        if err is not None:
550            return err
551        err = write_file(os.path.join(self.sysfs_dir(), 'min_sample_us'),
552                         self.min_sample_us)
553        if err is not None:
554            return err
555        err = write_file(os.path.join(self.sysfs_dir(), 'max_sample_us'),
556                         self.max_sample_us)
557        if err is not None:
558            return err
559        return None
560
561class DamonAttrs:
562    sample_us = None
563    aggr_us = None
564    intervals_goal = None
565    update_us = None
566    min_nr_regions = None
567    max_nr_regions = None
568    context = None
569
570    def __init__(self, sample_us=5000, aggr_us=100000,
571                 intervals_goal=IntervalsGoal(), update_us=1000000,
572            min_nr_regions=10, max_nr_regions=1000):
573        self.sample_us = sample_us
574        self.aggr_us = aggr_us
575        self.intervals_goal = intervals_goal
576        self.intervals_goal.attrs = self
577        self.update_us = update_us
578        self.min_nr_regions = min_nr_regions
579        self.max_nr_regions = max_nr_regions
580
581    def interval_sysfs_dir(self):
582        return os.path.join(self.context.sysfs_dir(), 'monitoring_attrs',
583                'intervals')
584
585    def nr_regions_range_sysfs_dir(self):
586        return os.path.join(self.context.sysfs_dir(), 'monitoring_attrs',
587                'nr_regions')
588
589    def stage(self):
590        err = write_file(os.path.join(self.interval_sysfs_dir(), 'sample_us'),
591                self.sample_us)
592        if err is not None:
593            return err
594        err = write_file(os.path.join(self.interval_sysfs_dir(), 'aggr_us'),
595                self.aggr_us)
596        if err is not None:
597            return err
598        err = self.intervals_goal.stage()
599        if err is not None:
600            return err
601        err = write_file(os.path.join(self.interval_sysfs_dir(), 'update_us'),
602                self.update_us)
603        if err is not None:
604            return err
605
606        err = write_file(
607                os.path.join(self.nr_regions_range_sysfs_dir(), 'min'),
608                self.min_nr_regions)
609        if err is not None:
610            return err
611
612        err = write_file(
613                os.path.join(self.nr_regions_range_sysfs_dir(), 'max'),
614                self.max_nr_regions)
615        if err is not None:
616            return err
617
618class DamonCtx:
619    ops = None
620    monitoring_attrs = None
621    targets = None
622    schemes = None
623    kdamond = None
624    pause = None
625    idx = None
626
627    def __init__(self, ops='paddr', monitoring_attrs=None, targets=None,
628            schemes=None, pause=False):
629        self.ops = ops
630        if monitoring_attrs is None:
631            monitoring_attrs = DamonAttrs()
632        self.monitoring_attrs = monitoring_attrs
633        self.monitoring_attrs.context = self
634
635        if targets is None:
636            targets = []
637        self.targets = targets
638        for idx, target in enumerate(self.targets):
639            target.idx = idx
640            target.context = self
641
642        if schemes is None:
643            schemes = []
644        self.schemes = schemes
645        for idx, scheme in enumerate(self.schemes):
646            scheme.idx = idx
647            scheme.context = self
648
649        self.pause=pause
650
651    def sysfs_dir(self):
652        return os.path.join(self.kdamond.sysfs_dir(), 'contexts',
653                '%d' % self.idx)
654
655    def stage(self):
656        err = write_file(
657                os.path.join(self.sysfs_dir(), 'operations'), self.ops)
658        if err is not None:
659            return err
660        err = self.monitoring_attrs.stage()
661        if err is not None:
662            return err
663
664        nr_targets_file = os.path.join(
665                self.sysfs_dir(), 'targets', 'nr_targets')
666        content, err = read_file(nr_targets_file)
667        if err is not None:
668            return err
669        if int(content) != len(self.targets):
670            err = write_file(nr_targets_file, '%d' % len(self.targets))
671            if err is not None:
672                return err
673        for target in self.targets:
674            err = target.stage()
675            if err is not None:
676                return err
677
678        nr_schemes_file = os.path.join(
679                self.sysfs_dir(), 'schemes', 'nr_schemes')
680        content, err = read_file(nr_schemes_file)
681        if err is not None:
682            return err
683        if int(content) != len(self.schemes):
684            err = write_file(nr_schemes_file, '%d' % len(self.schemes))
685            if err is not None:
686                return err
687        for scheme in self.schemes:
688            err = scheme.stage()
689            if err is not None:
690                return err
691
692        err = write_file(os.path.join(self.sysfs_dir(), 'pause'), self.pause)
693        if err is not None:
694            return err
695
696        return None
697
698class Kdamond:
699    state = None
700    pid = None
701    refresh_ms = None
702    contexts = None
703    idx = None      # index of this kdamond between siblings
704    kdamonds = None # parent
705
706    def __init__(self, contexts=[], refresh_ms=None):
707        self.contexts = contexts
708        self.refresh_ms = refresh_ms
709        for idx, context in enumerate(self.contexts):
710            context.idx = idx
711            context.kdamond = self
712
713    def sysfs_dir(self):
714        return os.path.join(self.kdamonds.sysfs_dir(), '%d' % self.idx)
715
716    def start(self):
717        nr_contexts_file = os.path.join(self.sysfs_dir(),
718                'contexts', 'nr_contexts')
719        content, err = read_file(nr_contexts_file)
720        if err is not None:
721            return err
722        if int(content) != len(self.contexts):
723            err = write_file(nr_contexts_file, '%d' % len(self.contexts))
724            if err is not None:
725                return err
726
727        for context in self.contexts:
728            err = context.stage()
729            if err is not None:
730                return err
731        if self.refresh_ms is not None:
732            err = write_file(os.path.join(self.sysfs_dir(), 'refresh_ms'),
733                             '%d' % self.refresh_ms)
734            if err is not None:
735                return err
736        err = write_file(os.path.join(self.sysfs_dir(), 'state'), 'on')
737        if err is not None:
738            return err
739        self.pid, err = read_file(os.path.join(self.sysfs_dir(), 'pid'))
740        return err
741
742    def stop(self):
743        err = write_file(os.path.join(self.sysfs_dir(), 'state'), 'off')
744        return err
745
746    def update_schemes_tried_regions(self):
747        err = write_file(os.path.join(self.sysfs_dir(), 'state'),
748                         'update_schemes_tried_regions')
749        if err is not None:
750            return err
751        for context in self.contexts:
752            for scheme in context.schemes:
753                tried_regions = []
754                tried_regions_dir = os.path.join(
755                        scheme.sysfs_dir(), 'tried_regions')
756                region_indices = []
757                for filename in os.listdir(
758                        os.path.join(scheme.sysfs_dir(), 'tried_regions')):
759                    tried_region_dir = os.path.join(tried_regions_dir, filename)
760                    if not os.path.isdir(tried_region_dir):
761                        continue
762                    region_indices.append(int(filename))
763                for region_idx in sorted(region_indices):
764                    tried_region_dir = os.path.join(tried_regions_dir,
765                                                    '%d' % region_idx)
766                    region_values = []
767                    for f in ['start', 'end', 'nr_accesses', 'age']:
768                        content, err = read_file(
769                                os.path.join(tried_region_dir, f))
770                        if err is not None:
771                            return err
772                        region_values.append(int(content))
773                    tried_regions.append(DamosTriedRegion(*region_values))
774                scheme.tried_regions = tried_regions
775
776    def update_schemes_tried_bytes(self):
777        err = write_file(os.path.join(self.sysfs_dir(), 'state'),
778                'update_schemes_tried_bytes')
779        if err is not None:
780            return err
781        for context in self.contexts:
782            for scheme in context.schemes:
783                content, err = read_file(os.path.join(scheme.sysfs_dir(),
784                    'tried_regions', 'total_bytes'))
785                if err is not None:
786                    return err
787                scheme.tried_bytes = int(content)
788
789    def update_schemes_stats(self):
790        err = write_file(os.path.join(self.sysfs_dir(), 'state'),
791                'update_schemes_stats')
792        if err is not None:
793            return err
794        for context in self.contexts:
795            for scheme in context.schemes:
796                stat_values = []
797                for stat in ['nr_tried', 'sz_tried', 'nr_applied',
798                             'sz_applied', 'qt_exceeds']:
799                    content, err = read_file(
800                            os.path.join(scheme.sysfs_dir(), 'stats', stat))
801                    if err is not None:
802                        return err
803                    stat_values.append(int(content))
804                scheme.stats = DamosStats(*stat_values)
805
806    def update_schemes_effective_quotas(self):
807        err = write_file(os.path.join(self.sysfs_dir(), 'state'),
808                         'update_schemes_effective_quotas')
809        if err is not None:
810            return err
811        for context in self.contexts:
812            for scheme in context.schemes:
813                for goal in scheme.quota.goals:
814                    content, err = read_file(
815                            os.path.join(scheme.quota.sysfs_dir(),
816                                         'effective_bytes'))
817                    if err is not None:
818                        return err
819                    goal.effective_bytes = int(content)
820        return None
821
822    def commit(self):
823        nr_contexts_file = os.path.join(self.sysfs_dir(),
824                'contexts', 'nr_contexts')
825        content, err = read_file(nr_contexts_file)
826        if err is not None:
827            return err
828        if int(content) != len(self.contexts):
829            err = write_file(nr_contexts_file, '%d' % len(self.contexts))
830            if err is not None:
831                return err
832
833        for context in self.contexts:
834            err = context.stage()
835            if err is not None:
836                return err
837        err = write_file(os.path.join(self.sysfs_dir(), 'state'), 'commit')
838        return err
839
840
841    def commit_schemes_quota_goals(self):
842        for context in self.contexts:
843            for scheme in context.schemes:
844                for goal in scheme.quota.goals:
845                    err = goal.stage()
846                    if err is not None:
847                        print('commit_schemes_quota_goals failed staging: %s'%
848                              err)
849                        exit(1)
850        return write_file(os.path.join(self.sysfs_dir(), 'state'),
851                         'commit_schemes_quota_goals')
852
853class Kdamonds:
854    kdamonds = []
855
856    def __init__(self, kdamonds=[]):
857        self.kdamonds = kdamonds
858        for idx, kdamond in enumerate(self.kdamonds):
859            kdamond.idx = idx
860            kdamond.kdamonds = self
861
862    def sysfs_dir(self):
863        return os.path.join(sysfs_root, 'kdamonds')
864
865    def start(self):
866        err = write_file(os.path.join(self.sysfs_dir(),  'nr_kdamonds'),
867                '%s' % len(self.kdamonds))
868        if err is not None:
869            return err
870        for kdamond in self.kdamonds:
871            err = kdamond.start()
872            if err is not None:
873                return err
874        return None
875
876    def stop(self):
877        for kdamond in self.kdamonds:
878            err = kdamond.stop()
879            if err is not None:
880                return err
881        return None
882