xref: /linux/tools/testing/selftests/damon/_damon_sysfs.py (revision aec2f682d47c54ef434b2d440992626d80b1ebdc)
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    weight_sz_permil = None
136    weight_nr_accesses_permil = None
137    weight_age_permil = None
138    scheme = None               # owner scheme
139
140    def __init__(self, sz=0, ms=0, goals=None, goal_tuner='consist',
141                 reset_interval_ms=0, weight_sz_permil=0,
142                 weight_nr_accesses_permil=0, weight_age_permil=0):
143        self.sz = sz
144        self.ms = ms
145        self.reset_interval_ms = reset_interval_ms
146        self.weight_sz_permil = weight_sz_permil
147        self.weight_nr_accesses_permil = weight_nr_accesses_permil
148        self.weight_age_permil = weight_age_permil
149        self.goals = goals if goals is not None else []
150        self.goal_tuner = goal_tuner
151        for idx, goal in enumerate(self.goals):
152            goal.idx = idx
153            goal.quota = self
154
155    def sysfs_dir(self):
156        return os.path.join(self.scheme.sysfs_dir(), 'quotas')
157
158    def stage(self):
159        err = write_file(os.path.join(self.sysfs_dir(), 'bytes'), self.sz)
160        if err is not None:
161            return err
162        err = write_file(os.path.join(self.sysfs_dir(), 'ms'), self.ms)
163        if err is not None:
164            return err
165        err = write_file(os.path.join(self.sysfs_dir(), 'reset_interval_ms'),
166                         self.reset_interval_ms)
167        if err is not None:
168            return err
169
170        err = write_file(os.path.join(
171            self.sysfs_dir(), 'weights', 'sz_permil'), self.weight_sz_permil)
172        if err is not None:
173            return err
174        err = write_file(os.path.join(
175            self.sysfs_dir(), 'weights', 'nr_accesses_permil'),
176                         self.weight_nr_accesses_permil)
177        if err is not None:
178            return err
179        err = write_file(os.path.join(
180            self.sysfs_dir(), 'weights', 'age_permil'), self.weight_age_permil)
181        if err is not None:
182            return err
183
184        nr_goals_file = os.path.join(self.sysfs_dir(), 'goals', 'nr_goals')
185        content, err = read_file(nr_goals_file)
186        if err is not None:
187            return err
188        if int(content) != len(self.goals):
189            err = write_file(nr_goals_file, len(self.goals))
190            if err is not None:
191                return err
192        for goal in self.goals:
193            err = goal.stage()
194            if err is not None:
195                return err
196        err = write_file(
197                os.path.join(self.sysfs_dir(), 'goal_tuner'), self.goal_tuner)
198        if err is not None:
199            return err
200        return None
201
202class DamosWatermarks:
203    metric = None
204    interval = None
205    high = None
206    mid = None
207    low = None
208    scheme = None   # owner scheme
209
210    def __init__(self, metric='none', interval=0, high=0, mid=0, low=0):
211        self.metric = metric
212        self.interval = interval
213        self.high = high
214        self.mid = mid
215        self.low = low
216
217    def sysfs_dir(self):
218        return os.path.join(self.scheme.sysfs_dir(), 'watermarks')
219
220    def stage(self):
221        err = write_file(os.path.join(self.sysfs_dir(), 'metric'), self.metric)
222        if err is not None:
223            return err
224        err = write_file(os.path.join(self.sysfs_dir(), 'interval_us'),
225                         self.interval)
226        if err is not None:
227            return err
228        err = write_file(os.path.join(self.sysfs_dir(), 'high'), self.high)
229        if err is not None:
230            return err
231        err = write_file(os.path.join(self.sysfs_dir(), 'mid'), self.mid)
232        if err is not None:
233            return err
234        err = write_file(os.path.join(self.sysfs_dir(), 'low'), self.low)
235        if err is not None:
236            return err
237
238class DamosFilter:
239    type_ = None
240    matching = None
241    allow = None
242    memcg_path = None
243    addr_start = None
244    addr_end = None
245    target_idx = None
246    min_ = None
247    max_ = None
248    idx = None
249    filters = None  # owner filters
250
251    def __init__(self, type_='anon', matching=False, allow=False,
252                 memcg_path='', addr_start=0, addr_end=0, target_idx=0, min_=0,
253                 max_=0):
254        self.type_ = type_
255        self.matching = matching
256        self.allow = allow
257        self.memcg_path = memcg_path,
258        self.addr_start = addr_start
259        self.addr_end = addr_end
260        self.target_idx = target_idx
261        self.min_ = min_
262        self.max_ = max_
263
264    def sysfs_dir(self):
265        return os.path.join(self.filters.sysfs_dir(), '%d' % self.idx)
266
267    def stage(self):
268        err = write_file(os.path.join(self.sysfs_dir(), 'type'), self.type_)
269        if err is not None:
270            return err
271        err = write_file(os.path.join(self.sysfs_dir(), 'matching'),
272                         self.matching)
273        if err is not None:
274            return err
275        err = write_file(os.path.join(self.sysfs_dir(), 'allow'), self.allow)
276        if err is not None:
277            return err
278        err = write_file(os.path.join(self.sysfs_dir(), 'memcg_path'),
279                         self.memcg_path)
280        if err is not None:
281            return err
282        err = write_file(os.path.join(self.sysfs_dir(), 'addr_start'),
283                         self.addr_start)
284        if err is not None:
285            return err
286        err = write_file(os.path.join(self.sysfs_dir(), 'addr_end'),
287                         self.addr_end)
288        if err is not None:
289            return err
290        err = write_file(os.path.join(self.sysfs_dir(), 'damon_target_idx'),
291                         self.target_idx)
292        if err is not None:
293            return err
294        err = write_file(os.path.join(self.sysfs_dir(), 'min'), self.min_)
295        if err is not None:
296            return err
297        err = write_file(os.path.join(self.sysfs_dir(), 'max'), self.max_)
298        if err is not None:
299            return err
300        return None
301
302class DamosFilters:
303    name = None
304    filters = None
305    scheme = None   # owner scheme
306
307    def __init__(self, name, filters=[]):
308        self.name = name
309        self.filters = filters
310        for idx, filter_ in enumerate(self.filters):
311            filter_.idx = idx
312            filter_.filters = self
313
314    def sysfs_dir(self):
315        return os.path.join(self.scheme.sysfs_dir(), self.name)
316
317    def stage(self):
318        err = write_file(os.path.join(self.sysfs_dir(), 'nr_filters'),
319                         len(self.filters))
320        if err is not None:
321            return err
322        for filter_ in self.filters:
323            err = filter_.stage()
324            if err is not None:
325                return err
326        return None
327
328class DamosDest:
329    id = None
330    weight = None
331    idx = None
332    dests = None    # owner dests
333
334    def __init__(self, id=0, weight=0):
335        self.id = id
336        self.weight = weight
337
338    def sysfs_dir(self):
339        return os.path.join(self.dests.sysfs_dir(), '%d' % self.idx)
340
341    def stage(self):
342        err = write_file(os.path.join(self.sysfs_dir(), 'id'), self.id)
343        if err is not None:
344            return err
345        err = write_file(os.path.join(self.sysfs_dir(), 'weight'), self.weight)
346        if err is not None:
347            return err
348        return None
349
350class DamosDests:
351    dests = None
352    scheme = None   # owner scheme
353
354    def __init__(self, dests=[]):
355        self.dests = dests
356        for idx, dest in enumerate(self.dests):
357            dest.idx = idx
358            dest.dests = self
359
360    def sysfs_dir(self):
361        return os.path.join(self.scheme.sysfs_dir(), 'dests')
362
363    def stage(self):
364        err = write_file(os.path.join(self.sysfs_dir(), 'nr_dests'),
365                         len(self.dests))
366        if err is not None:
367            return err
368        for dest in self.dests:
369            err = dest.stage()
370            if err is not None:
371                return err
372        return None
373
374class DamosStats:
375    nr_tried = None
376    sz_tried = None
377    nr_applied = None
378    sz_applied = None
379    qt_exceeds = None
380
381    def __init__(self, nr_tried, sz_tried, nr_applied, sz_applied, qt_exceeds):
382        self.nr_tried = nr_tried
383        self.sz_tried = sz_tried
384        self.nr_applied = nr_applied
385        self.sz_applied = sz_applied
386        self.qt_exceeds = qt_exceeds
387
388class DamosTriedRegion:
389    def __init__(self, start, end, nr_accesses, age):
390        self.start = start
391        self.end = end
392        self.nr_accesses = nr_accesses
393        self.age = age
394
395class Damos:
396    action = None
397    access_pattern = None
398    quota = None
399    watermarks = None
400    core_filters = None
401    ops_filters = None
402    filters = None
403    apply_interval_us = None
404    target_nid = None
405    dests = None
406    idx = None
407    context = None
408    tried_bytes = None
409    stats = None
410    tried_regions = None
411
412    def __init__(self, action='stat', access_pattern=DamosAccessPattern(),
413                 quota=DamosQuota(), watermarks=DamosWatermarks(),
414                 core_filters=[], ops_filters=[], filters=[], target_nid=0,
415                 dests=DamosDests(), apply_interval_us=0):
416        self.action = action
417        self.access_pattern = access_pattern
418        self.access_pattern.scheme = self
419        self.quota = quota
420        self.quota.scheme = self
421        self.watermarks = watermarks
422        self.watermarks.scheme = self
423
424        self.core_filters = DamosFilters(name='core_filters',
425                                         filters=core_filters)
426        self.core_filters.scheme = self
427        self.ops_filters = DamosFilters(name='ops_filters',
428                                         filters=ops_filters)
429        self.ops_filters.scheme = self
430        self.filters = DamosFilters(name='filters', filters=filters)
431        self.filters.scheme = self
432
433        self.target_nid = target_nid
434        self.dests = dests
435        self.dests.scheme = self
436
437        self.apply_interval_us = apply_interval_us
438
439    def sysfs_dir(self):
440        return os.path.join(
441                self.context.sysfs_dir(), 'schemes', '%d' % self.idx)
442
443    def stage(self):
444        err = write_file(os.path.join(self.sysfs_dir(), 'action'), self.action)
445        if err is not None:
446            return err
447        err = self.access_pattern.stage()
448        if err is not None:
449            return err
450        err = write_file(os.path.join(self.sysfs_dir(), 'apply_interval_us'),
451                         '%d' % self.apply_interval_us)
452        if err is not None:
453            return err
454
455        err = self.quota.stage()
456        if err is not None:
457            return err
458
459        err = self.watermarks.stage()
460        if err is not None:
461            return err
462
463        err = self.core_filters.stage()
464        if err is not None:
465            return err
466        err = self.ops_filters.stage()
467        if err is not None:
468            return err
469        err = self.filters.stage()
470        if err is not None:
471            return err
472
473        err = write_file(os.path.join(self.sysfs_dir(), 'target_nid'), '%d' %
474                         self.target_nid)
475        if err is not None:
476            return err
477
478        err = self.dests.stage()
479        if err is not None:
480            return err
481
482class DamonTarget:
483    pid = None
484    obsolete = None
485    # todo: Support target regions if test is made
486    idx = None
487    context = None
488
489    def __init__(self, pid, obsolete=False):
490        self.pid = pid
491        self.obsolete = obsolete
492
493    def sysfs_dir(self):
494        return os.path.join(
495                self.context.sysfs_dir(), 'targets', '%d' % self.idx)
496
497    def stage(self):
498        err = write_file(
499                os.path.join(self.sysfs_dir(), 'regions', 'nr_regions'), '0')
500        if err is not None:
501            return err
502        err = write_file(
503                os.path.join(self.sysfs_dir(), 'pid_target'), self.pid)
504        if err is not None:
505            return err
506        return write_file(
507                os.path.join(self.sysfs_dir(), 'obsolete_target'),
508                'Y' if self.obsolete else 'N')
509
510class IntervalsGoal:
511    access_bp = None
512    aggrs = None
513    min_sample_us = None
514    max_sample_us = None
515    attrs = None    # owner DamonAttrs
516
517    def __init__(self, access_bp=0, aggrs=0, min_sample_us=0, max_sample_us=0):
518        self.access_bp = access_bp
519        self.aggrs = aggrs
520        self.min_sample_us = min_sample_us
521        self.max_sample_us = max_sample_us
522
523    def sysfs_dir(self):
524        return os.path.join(self.attrs.interval_sysfs_dir(), 'intervals_goal')
525
526    def stage(self):
527        err = write_file(
528                os.path.join(self.sysfs_dir(), 'access_bp'), self.access_bp)
529        if err is not None:
530            return err
531        err = write_file(os.path.join(self.sysfs_dir(), 'aggrs'), self.aggrs)
532        if err is not None:
533            return err
534        err = write_file(os.path.join(self.sysfs_dir(), 'min_sample_us'),
535                         self.min_sample_us)
536        if err is not None:
537            return err
538        err = write_file(os.path.join(self.sysfs_dir(), 'max_sample_us'),
539                         self.max_sample_us)
540        if err is not None:
541            return err
542        return None
543
544class DamonAttrs:
545    sample_us = None
546    aggr_us = None
547    intervals_goal = None
548    update_us = None
549    min_nr_regions = None
550    max_nr_regions = None
551    context = None
552
553    def __init__(self, sample_us=5000, aggr_us=100000,
554                 intervals_goal=IntervalsGoal(), update_us=1000000,
555            min_nr_regions=10, max_nr_regions=1000):
556        self.sample_us = sample_us
557        self.aggr_us = aggr_us
558        self.intervals_goal = intervals_goal
559        self.intervals_goal.attrs = self
560        self.update_us = update_us
561        self.min_nr_regions = min_nr_regions
562        self.max_nr_regions = max_nr_regions
563
564    def interval_sysfs_dir(self):
565        return os.path.join(self.context.sysfs_dir(), 'monitoring_attrs',
566                'intervals')
567
568    def nr_regions_range_sysfs_dir(self):
569        return os.path.join(self.context.sysfs_dir(), 'monitoring_attrs',
570                'nr_regions')
571
572    def stage(self):
573        err = write_file(os.path.join(self.interval_sysfs_dir(), 'sample_us'),
574                self.sample_us)
575        if err is not None:
576            return err
577        err = write_file(os.path.join(self.interval_sysfs_dir(), 'aggr_us'),
578                self.aggr_us)
579        if err is not None:
580            return err
581        err = self.intervals_goal.stage()
582        if err is not None:
583            return err
584        err = write_file(os.path.join(self.interval_sysfs_dir(), 'update_us'),
585                self.update_us)
586        if err is not None:
587            return err
588
589        err = write_file(
590                os.path.join(self.nr_regions_range_sysfs_dir(), 'min'),
591                self.min_nr_regions)
592        if err is not None:
593            return err
594
595        err = write_file(
596                os.path.join(self.nr_regions_range_sysfs_dir(), 'max'),
597                self.max_nr_regions)
598        if err is not None:
599            return err
600
601class DamonCtx:
602    ops = None
603    monitoring_attrs = None
604    targets = None
605    schemes = None
606    kdamond = None
607    idx = None
608
609    def __init__(self, ops='paddr', monitoring_attrs=DamonAttrs(), targets=[],
610            schemes=[]):
611        self.ops = ops
612        self.monitoring_attrs = monitoring_attrs
613        self.monitoring_attrs.context = self
614
615        self.targets = targets
616        for idx, target in enumerate(self.targets):
617            target.idx = idx
618            target.context = self
619
620        self.schemes = schemes
621        for idx, scheme in enumerate(self.schemes):
622            scheme.idx = idx
623            scheme.context = self
624
625    def sysfs_dir(self):
626        return os.path.join(self.kdamond.sysfs_dir(), 'contexts',
627                '%d' % self.idx)
628
629    def stage(self):
630        err = write_file(
631                os.path.join(self.sysfs_dir(), 'operations'), self.ops)
632        if err is not None:
633            return err
634        err = self.monitoring_attrs.stage()
635        if err is not None:
636            return err
637
638        nr_targets_file = os.path.join(
639                self.sysfs_dir(), 'targets', 'nr_targets')
640        content, err = read_file(nr_targets_file)
641        if err is not None:
642            return err
643        if int(content) != len(self.targets):
644            err = write_file(nr_targets_file, '%d' % len(self.targets))
645            if err is not None:
646                return err
647        for target in self.targets:
648            err = target.stage()
649            if err is not None:
650                return err
651
652        nr_schemes_file = os.path.join(
653                self.sysfs_dir(), 'schemes', 'nr_schemes')
654        content, err = read_file(nr_schemes_file)
655        if err is not None:
656            return err
657        if int(content) != len(self.schemes):
658            err = write_file(nr_schemes_file, '%d' % len(self.schemes))
659            if err is not None:
660                return err
661        for scheme in self.schemes:
662            err = scheme.stage()
663            if err is not None:
664                return err
665        return None
666
667class Kdamond:
668    state = None
669    pid = None
670    contexts = None
671    idx = None      # index of this kdamond between siblings
672    kdamonds = None # parent
673
674    def __init__(self, contexts=[]):
675        self.contexts = contexts
676        for idx, context in enumerate(self.contexts):
677            context.idx = idx
678            context.kdamond = self
679
680    def sysfs_dir(self):
681        return os.path.join(self.kdamonds.sysfs_dir(), '%d' % self.idx)
682
683    def start(self):
684        nr_contexts_file = os.path.join(self.sysfs_dir(),
685                'contexts', 'nr_contexts')
686        content, err = read_file(nr_contexts_file)
687        if err is not None:
688            return err
689        if int(content) != len(self.contexts):
690            err = write_file(nr_contexts_file, '%d' % len(self.contexts))
691            if err is not None:
692                return err
693
694        for context in self.contexts:
695            err = context.stage()
696            if err is not None:
697                return err
698        err = write_file(os.path.join(self.sysfs_dir(), 'state'), 'on')
699        if err is not None:
700            return err
701        self.pid, err = read_file(os.path.join(self.sysfs_dir(), 'pid'))
702        return err
703
704    def stop(self):
705        err = write_file(os.path.join(self.sysfs_dir(), 'state'), 'off')
706        return err
707
708    def update_schemes_tried_regions(self):
709        err = write_file(os.path.join(self.sysfs_dir(), 'state'),
710                         'update_schemes_tried_regions')
711        if err is not None:
712            return err
713        for context in self.contexts:
714            for scheme in context.schemes:
715                tried_regions = []
716                tried_regions_dir = os.path.join(
717                        scheme.sysfs_dir(), 'tried_regions')
718                region_indices = []
719                for filename in os.listdir(
720                        os.path.join(scheme.sysfs_dir(), 'tried_regions')):
721                    tried_region_dir = os.path.join(tried_regions_dir, filename)
722                    if not os.path.isdir(tried_region_dir):
723                        continue
724                    region_indices.append(int(filename))
725                for region_idx in sorted(region_indices):
726                    tried_region_dir = os.path.join(tried_regions_dir,
727                                                    '%d' % region_idx)
728                    region_values = []
729                    for f in ['start', 'end', 'nr_accesses', 'age']:
730                        content, err = read_file(
731                                os.path.join(tried_region_dir, f))
732                        if err is not None:
733                            return err
734                        region_values.append(int(content))
735                    tried_regions.append(DamosTriedRegion(*region_values))
736                scheme.tried_regions = tried_regions
737
738    def update_schemes_tried_bytes(self):
739        err = write_file(os.path.join(self.sysfs_dir(), 'state'),
740                'update_schemes_tried_bytes')
741        if err is not None:
742            return err
743        for context in self.contexts:
744            for scheme in context.schemes:
745                content, err = read_file(os.path.join(scheme.sysfs_dir(),
746                    'tried_regions', 'total_bytes'))
747                if err is not None:
748                    return err
749                scheme.tried_bytes = int(content)
750
751    def update_schemes_stats(self):
752        err = write_file(os.path.join(self.sysfs_dir(), 'state'),
753                'update_schemes_stats')
754        if err is not None:
755            return err
756        for context in self.contexts:
757            for scheme in context.schemes:
758                stat_values = []
759                for stat in ['nr_tried', 'sz_tried', 'nr_applied',
760                             'sz_applied', 'qt_exceeds']:
761                    content, err = read_file(
762                            os.path.join(scheme.sysfs_dir(), 'stats', stat))
763                    if err is not None:
764                        return err
765                    stat_values.append(int(content))
766                scheme.stats = DamosStats(*stat_values)
767
768    def update_schemes_effective_quotas(self):
769        err = write_file(os.path.join(self.sysfs_dir(), 'state'),
770                         'update_schemes_effective_quotas')
771        if err is not None:
772            return err
773        for context in self.contexts:
774            for scheme in context.schemes:
775                for goal in scheme.quota.goals:
776                    content, err = read_file(
777                            os.path.join(scheme.quota.sysfs_dir(),
778                                         'effective_bytes'))
779                    if err is not None:
780                        return err
781                    goal.effective_bytes = int(content)
782        return None
783
784    def commit(self):
785        nr_contexts_file = os.path.join(self.sysfs_dir(),
786                'contexts', 'nr_contexts')
787        content, err = read_file(nr_contexts_file)
788        if err is not None:
789            return err
790        if int(content) != len(self.contexts):
791            err = write_file(nr_contexts_file, '%d' % len(self.contexts))
792            if err is not None:
793                return err
794
795        for context in self.contexts:
796            err = context.stage()
797            if err is not None:
798                return err
799        err = write_file(os.path.join(self.sysfs_dir(), 'state'), 'commit')
800        return err
801
802
803    def commit_schemes_quota_goals(self):
804        for context in self.contexts:
805            for scheme in context.schemes:
806                for goal in scheme.quota.goals:
807                    err = goal.stage()
808                    if err is not None:
809                        print('commit_schemes_quota_goals failed stagign: %s'%
810                              err)
811                        exit(1)
812        return write_file(os.path.join(self.sysfs_dir(), 'state'),
813                         'commit_schemes_quota_goals')
814
815class Kdamonds:
816    kdamonds = []
817
818    def __init__(self, kdamonds=[]):
819        self.kdamonds = kdamonds
820        for idx, kdamond in enumerate(self.kdamonds):
821            kdamond.idx = idx
822            kdamond.kdamonds = self
823
824    def sysfs_dir(self):
825        return os.path.join(sysfs_root, 'kdamonds')
826
827    def start(self):
828        err = write_file(os.path.join(self.sysfs_dir(),  'nr_kdamonds'),
829                '%s' % len(self.kdamonds))
830        if err is not None:
831            return err
832        for kdamond in self.kdamonds:
833            err = kdamond.start()
834            if err is not None:
835                return err
836        return None
837
838    def stop(self):
839        for kdamond in self.kdamonds:
840            err = kdamond.stop()
841            if err is not None:
842                return err
843        return None
844