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