xref: /linux/tools/testing/selftests/damon/_damon_sysfs.py (revision bba2c3615bd6cfee7456d1130f2e6b01b3f4e9ba)
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=DamonAttrs(), targets=[],
628            schemes=[], pause=False):
629        self.ops = ops
630        self.monitoring_attrs = monitoring_attrs
631        self.monitoring_attrs.context = self
632
633        self.targets = targets
634        for idx, target in enumerate(self.targets):
635            target.idx = idx
636            target.context = self
637
638        self.schemes = schemes
639        for idx, scheme in enumerate(self.schemes):
640            scheme.idx = idx
641            scheme.context = self
642
643        self.pause=pause
644
645    def sysfs_dir(self):
646        return os.path.join(self.kdamond.sysfs_dir(), 'contexts',
647                '%d' % self.idx)
648
649    def stage(self):
650        err = write_file(
651                os.path.join(self.sysfs_dir(), 'operations'), self.ops)
652        if err is not None:
653            return err
654        err = self.monitoring_attrs.stage()
655        if err is not None:
656            return err
657
658        nr_targets_file = os.path.join(
659                self.sysfs_dir(), 'targets', 'nr_targets')
660        content, err = read_file(nr_targets_file)
661        if err is not None:
662            return err
663        if int(content) != len(self.targets):
664            err = write_file(nr_targets_file, '%d' % len(self.targets))
665            if err is not None:
666                return err
667        for target in self.targets:
668            err = target.stage()
669            if err is not None:
670                return err
671
672        nr_schemes_file = os.path.join(
673                self.sysfs_dir(), 'schemes', 'nr_schemes')
674        content, err = read_file(nr_schemes_file)
675        if err is not None:
676            return err
677        if int(content) != len(self.schemes):
678            err = write_file(nr_schemes_file, '%d' % len(self.schemes))
679            if err is not None:
680                return err
681        for scheme in self.schemes:
682            err = scheme.stage()
683            if err is not None:
684                return err
685
686        err = write_file(os.path.join(self.sysfs_dir(), 'pause'), self.pause)
687        if err is not None:
688            return err
689
690        return None
691
692class Kdamond:
693    state = None
694    pid = None
695    contexts = None
696    idx = None      # index of this kdamond between siblings
697    kdamonds = None # parent
698
699    def __init__(self, contexts=[]):
700        self.contexts = contexts
701        for idx, context in enumerate(self.contexts):
702            context.idx = idx
703            context.kdamond = self
704
705    def sysfs_dir(self):
706        return os.path.join(self.kdamonds.sysfs_dir(), '%d' % self.idx)
707
708    def start(self):
709        nr_contexts_file = os.path.join(self.sysfs_dir(),
710                'contexts', 'nr_contexts')
711        content, err = read_file(nr_contexts_file)
712        if err is not None:
713            return err
714        if int(content) != len(self.contexts):
715            err = write_file(nr_contexts_file, '%d' % len(self.contexts))
716            if err is not None:
717                return err
718
719        for context in self.contexts:
720            err = context.stage()
721            if err is not None:
722                return err
723        err = write_file(os.path.join(self.sysfs_dir(), 'state'), 'on')
724        if err is not None:
725            return err
726        self.pid, err = read_file(os.path.join(self.sysfs_dir(), 'pid'))
727        return err
728
729    def stop(self):
730        err = write_file(os.path.join(self.sysfs_dir(), 'state'), 'off')
731        return err
732
733    def update_schemes_tried_regions(self):
734        err = write_file(os.path.join(self.sysfs_dir(), 'state'),
735                         'update_schemes_tried_regions')
736        if err is not None:
737            return err
738        for context in self.contexts:
739            for scheme in context.schemes:
740                tried_regions = []
741                tried_regions_dir = os.path.join(
742                        scheme.sysfs_dir(), 'tried_regions')
743                region_indices = []
744                for filename in os.listdir(
745                        os.path.join(scheme.sysfs_dir(), 'tried_regions')):
746                    tried_region_dir = os.path.join(tried_regions_dir, filename)
747                    if not os.path.isdir(tried_region_dir):
748                        continue
749                    region_indices.append(int(filename))
750                for region_idx in sorted(region_indices):
751                    tried_region_dir = os.path.join(tried_regions_dir,
752                                                    '%d' % region_idx)
753                    region_values = []
754                    for f in ['start', 'end', 'nr_accesses', 'age']:
755                        content, err = read_file(
756                                os.path.join(tried_region_dir, f))
757                        if err is not None:
758                            return err
759                        region_values.append(int(content))
760                    tried_regions.append(DamosTriedRegion(*region_values))
761                scheme.tried_regions = tried_regions
762
763    def update_schemes_tried_bytes(self):
764        err = write_file(os.path.join(self.sysfs_dir(), 'state'),
765                'update_schemes_tried_bytes')
766        if err is not None:
767            return err
768        for context in self.contexts:
769            for scheme in context.schemes:
770                content, err = read_file(os.path.join(scheme.sysfs_dir(),
771                    'tried_regions', 'total_bytes'))
772                if err is not None:
773                    return err
774                scheme.tried_bytes = int(content)
775
776    def update_schemes_stats(self):
777        err = write_file(os.path.join(self.sysfs_dir(), 'state'),
778                'update_schemes_stats')
779        if err is not None:
780            return err
781        for context in self.contexts:
782            for scheme in context.schemes:
783                stat_values = []
784                for stat in ['nr_tried', 'sz_tried', 'nr_applied',
785                             'sz_applied', 'qt_exceeds']:
786                    content, err = read_file(
787                            os.path.join(scheme.sysfs_dir(), 'stats', stat))
788                    if err is not None:
789                        return err
790                    stat_values.append(int(content))
791                scheme.stats = DamosStats(*stat_values)
792
793    def update_schemes_effective_quotas(self):
794        err = write_file(os.path.join(self.sysfs_dir(), 'state'),
795                         'update_schemes_effective_quotas')
796        if err is not None:
797            return err
798        for context in self.contexts:
799            for scheme in context.schemes:
800                for goal in scheme.quota.goals:
801                    content, err = read_file(
802                            os.path.join(scheme.quota.sysfs_dir(),
803                                         'effective_bytes'))
804                    if err is not None:
805                        return err
806                    goal.effective_bytes = int(content)
807        return None
808
809    def commit(self):
810        nr_contexts_file = os.path.join(self.sysfs_dir(),
811                'contexts', 'nr_contexts')
812        content, err = read_file(nr_contexts_file)
813        if err is not None:
814            return err
815        if int(content) != len(self.contexts):
816            err = write_file(nr_contexts_file, '%d' % len(self.contexts))
817            if err is not None:
818                return err
819
820        for context in self.contexts:
821            err = context.stage()
822            if err is not None:
823                return err
824        err = write_file(os.path.join(self.sysfs_dir(), 'state'), 'commit')
825        return err
826
827
828    def commit_schemes_quota_goals(self):
829        for context in self.contexts:
830            for scheme in context.schemes:
831                for goal in scheme.quota.goals:
832                    err = goal.stage()
833                    if err is not None:
834                        print('commit_schemes_quota_goals failed stagign: %s'%
835                              err)
836                        exit(1)
837        return write_file(os.path.join(self.sysfs_dir(), 'state'),
838                         'commit_schemes_quota_goals')
839
840class Kdamonds:
841    kdamonds = []
842
843    def __init__(self, kdamonds=[]):
844        self.kdamonds = kdamonds
845        for idx, kdamond in enumerate(self.kdamonds):
846            kdamond.idx = idx
847            kdamond.kdamonds = self
848
849    def sysfs_dir(self):
850        return os.path.join(sysfs_root, 'kdamonds')
851
852    def start(self):
853        err = write_file(os.path.join(self.sysfs_dir(),  'nr_kdamonds'),
854                '%s' % len(self.kdamonds))
855        if err is not None:
856            return err
857        for kdamond in self.kdamonds:
858            err = kdamond.start()
859            if err is not None:
860                return err
861        return None
862
863    def stop(self):
864        for kdamond in self.kdamonds:
865            err = kdamond.stop()
866            if err is not None:
867                return err
868        return None
869