xref: /linux/tools/testing/selftests/damon/_damon_sysfs.py (revision c435bce6af9b2a277662698875a689c389358f17)
1# SPDX-License-Identifier: GPL-2.0
2
3import os
4
5sysfs_root = '/sys/kernel/mm/damon/admin'
6
7def write_file(path, string):
8    "Returns error string if failed, or None otherwise"
9    string = '%s' % string
10    try:
11        with open(path, 'w') as f:
12            f.write(string)
13    except Exception as e:
14        return '%s' % e
15    return None
16
17def read_file(path):
18    '''Returns the read content and error string.  The read content is None if
19    the reading failed'''
20    try:
21        with open(path, 'r') as f:
22            return f.read(), None
23    except Exception as e:
24        return None, '%s' % e
25
26class DamosAccessPattern:
27    size = None
28    nr_accesses = None
29    age = None
30    scheme = None
31
32    def __init__(self, size=None, nr_accesses=None, age=None):
33        self.size = size
34        self.nr_accesses = nr_accesses
35        self.age = age
36
37        if self.size == None:
38            self.size = [0, 2**64 - 1]
39        if self.nr_accesses == None:
40            self.nr_accesses = [0, 2**64 - 1]
41        if self.age == None:
42            self.age = [0, 2**64 - 1]
43
44    def sysfs_dir(self):
45        return os.path.join(self.scheme.sysfs_dir(), 'access_pattern')
46
47    def stage(self):
48        err = write_file(
49                os.path.join(self.sysfs_dir(), 'sz', 'min'), self.size[0])
50        if err != None:
51            return err
52        err = write_file(
53                os.path.join(self.sysfs_dir(), 'sz', 'max'), self.size[1])
54        if err != None:
55            return err
56        err = write_file(os.path.join(self.sysfs_dir(), 'nr_accesses', 'min'),
57                self.nr_accesses[0])
58        if err != None:
59            return err
60        err = write_file(os.path.join(self.sysfs_dir(), 'nr_accesses', 'max'),
61                self.nr_accesses[1])
62        if err != None:
63            return err
64        err = write_file(
65                os.path.join(self.sysfs_dir(), 'age', 'min'), self.age[0])
66        if err != None:
67            return err
68        err = write_file(
69                os.path.join(self.sysfs_dir(), 'age', 'max'), self.age[1])
70        if err != None:
71            return err
72
73class Damos:
74    action = None
75    access_pattern = None
76    # todo: Support quotas, watermarks, stats, tried_regions
77    idx = None
78    context = None
79    tried_bytes = None
80
81    def __init__(self, action='stat', access_pattern=DamosAccessPattern()):
82        self.action = action
83        self.access_pattern = access_pattern
84        self.access_pattern.scheme = self
85
86    def sysfs_dir(self):
87        return os.path.join(
88                self.context.sysfs_dir(), 'schemes', '%d' % self.idx)
89
90    def stage(self):
91        err = write_file(os.path.join(self.sysfs_dir(), 'action'), self.action)
92        if err != None:
93            return err
94        err = self.access_pattern.stage()
95        if err != None:
96            return err
97
98        # disable quotas
99        err = write_file(os.path.join(self.sysfs_dir(), 'quotas', 'ms'), '0')
100        if err != None:
101            return err
102        err = write_file(
103                os.path.join(self.sysfs_dir(), 'quotas', 'bytes'), '0')
104        if err != None:
105            return err
106
107        # disable watermarks
108        err = write_file(
109                os.path.join(self.sysfs_dir(), 'watermarks', 'metric'), 'none')
110        if err != None:
111            return err
112
113        # disable filters
114        err = write_file(
115                os.path.join(self.sysfs_dir(), 'filters', 'nr_filters'), '0')
116        if err != None:
117            return err
118
119class DamonTarget:
120    pid = None
121    # todo: Support target regions if test is made
122    idx = None
123    context = None
124
125    def __init__(self, pid):
126        self.pid = pid
127
128    def sysfs_dir(self):
129        return os.path.join(
130                self.context.sysfs_dir(), 'targets', '%d' % self.idx)
131
132    def stage(self):
133        err = write_file(
134                os.path.join(self.sysfs_dir(), 'regions', 'nr_regions'), '0')
135        if err != None:
136            return err
137        return write_file(
138                os.path.join(self.sysfs_dir(), 'pid_target'), self.pid)
139
140class DamonAttrs:
141    sample_us = None
142    aggr_us = None
143    update_us = None
144    min_nr_regions = None
145    max_nr_regions = None
146    context = None
147
148    def __init__(self, sample_us=5000, aggr_us=100000, update_us=1000000,
149            min_nr_regions=10, max_nr_regions=1000):
150        self.sample_us = sample_us
151        self.aggr_us = aggr_us
152        self.update_us = update_us
153        self.min_nr_regions = min_nr_regions
154        self.max_nr_regions = max_nr_regions
155
156    def interval_sysfs_dir(self):
157        return os.path.join(self.context.sysfs_dir(), 'monitoring_attrs',
158                'intervals')
159
160    def nr_regions_range_sysfs_dir(self):
161        return os.path.join(self.context.sysfs_dir(), 'monitoring_attrs',
162                'nr_regions')
163
164    def stage(self):
165        err = write_file(os.path.join(self.interval_sysfs_dir(), 'sample_us'),
166                self.sample_us)
167        if err != None:
168            return err
169        err = write_file(os.path.join(self.interval_sysfs_dir(), 'aggr_us'),
170                self.aggr_us)
171        if err != None:
172            return err
173        err = write_file(os.path.join(self.interval_sysfs_dir(), 'update_us'),
174                self.update_us)
175        if err != None:
176            return err
177
178        err = write_file(
179                os.path.join(self.nr_regions_range_sysfs_dir(), 'min'),
180                self.min_nr_regions)
181        if err != None:
182            return err
183
184        err = write_file(
185                os.path.join(self.nr_regions_range_sysfs_dir(), 'max'),
186                self.max_nr_regions)
187        if err != None:
188            return err
189
190class DamonCtx:
191    ops = None
192    monitoring_attrs = None
193    targets = None
194    schemes = None
195    kdamond = None
196    idx = None
197
198    def __init__(self, ops='paddr', monitoring_attrs=DamonAttrs(), targets=[],
199            schemes=[]):
200        self.ops = ops
201        self.monitoring_attrs = monitoring_attrs
202        self.monitoring_attrs.context = self
203
204        self.targets = targets
205        for idx, target in enumerate(self.targets):
206            target.idx = idx
207            target.context = self
208
209        self.schemes = schemes
210        for idx, scheme in enumerate(self.schemes):
211            scheme.idx = idx
212            scheme.context = self
213
214    def sysfs_dir(self):
215        return os.path.join(self.kdamond.sysfs_dir(), 'contexts',
216                '%d' % self.idx)
217
218    def stage(self):
219        err = write_file(
220                os.path.join(self.sysfs_dir(), 'operations'), self.ops)
221        if err != None:
222            return err
223        err = self.monitoring_attrs.stage()
224        if err != None:
225            return err
226
227        nr_targets_file = os.path.join(
228                self.sysfs_dir(), 'targets', 'nr_targets')
229        content, err = read_file(nr_targets_file)
230        if err != None:
231            return err
232        if int(content) != len(self.targets):
233            err = write_file(nr_targets_file, '%d' % len(self.targets))
234            if err != None:
235                return err
236        for target in self.targets:
237            err = target.stage()
238            if err != None:
239                return err
240
241        nr_schemes_file = os.path.join(
242                self.sysfs_dir(), 'schemes', 'nr_schemes')
243        content, err = read_file(nr_schemes_file)
244        if int(content) != len(self.schemes):
245            err = write_file(nr_schemes_file, '%d' % len(self.schemes))
246            if err != None:
247                return err
248        for scheme in self.schemes:
249            err = scheme.stage()
250            if err != None:
251                return err
252        return None
253
254class Kdamond:
255    state = None
256    pid = None
257    contexts = None
258    idx = None      # index of this kdamond between siblings
259    kdamonds = None # parent
260
261    def __init__(self, contexts=[]):
262        self.contexts = contexts
263        for idx, context in enumerate(self.contexts):
264            context.idx = idx
265            context.kdamond = self
266
267    def sysfs_dir(self):
268        return os.path.join(self.kdamonds.sysfs_dir(), '%d' % self.idx)
269
270    def start(self):
271        nr_contexts_file = os.path.join(self.sysfs_dir(),
272                'contexts', 'nr_contexts')
273        content, err = read_file(nr_contexts_file)
274        if err != None:
275            return err
276        if int(content) != len(self.contexts):
277            err = write_file(nr_contexts_file, '%d' % len(self.contexts))
278            if err != None:
279                return err
280
281        for context in self.contexts:
282            err = context.stage()
283            if err != None:
284                return err
285        err = write_file(os.path.join(self.sysfs_dir(), 'state'), 'on')
286        return err
287
288    def update_schemes_tried_bytes(self):
289        err = write_file(os.path.join(self.sysfs_dir(), 'state'),
290                'update_schemes_tried_bytes')
291        if err != None:
292            return err
293        for context in self.contexts:
294            for scheme in context.schemes:
295                content, err = read_file(os.path.join(scheme.sysfs_dir(),
296                    'tried_regions', 'total_bytes'))
297                if err != None:
298                    return err
299                scheme.tried_bytes = int(content)
300
301class Kdamonds:
302    kdamonds = []
303
304    def __init__(self, kdamonds=[]):
305        self.kdamonds = kdamonds
306        for idx, kdamond in enumerate(self.kdamonds):
307            kdamond.idx = idx
308            kdamond.kdamonds = self
309
310    def sysfs_dir(self):
311        return os.path.join(sysfs_root, 'kdamonds')
312
313    def start(self):
314        err = write_file(os.path.join(self.sysfs_dir(),  'nr_kdamonds'),
315                '%s' % len(self.kdamonds))
316        if err != None:
317            return err
318        for kdamond in self.kdamonds:
319            err = kdamond.start()
320            if err != None:
321                return err
322        return None
323