Merge tag 'trace-printf-v6.13' of git://git.kernel.org/pub/scm/linux/kernel/git/trace...
[drm/drm-misc.git] / tools / testing / selftests / damon / _damon_sysfs.py
blob6e136dc3df195ebfa528b578a24885f9d719829f
1 # SPDX-License-Identifier: GPL-2.0
3 import os
5 ksft_skip=4
7 sysfs_root = None
8 with 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
14 if sysfs_root is None:
15 print('Seems sysfs not mounted?')
16 exit(ksft_skip)
18 def write_file(path, string):
19 "Returns error string if failed, or None otherwise"
20 string = '%s' % string
21 try:
22 with open(path, 'w') as f:
23 f.write(string)
24 except Exception as e:
25 return '%s' % e
26 return None
28 def read_file(path):
29 '''Returns the read content and error string. The read content is None if
30 the reading failed'''
31 try:
32 with open(path, 'r') as f:
33 return f.read(), None
34 except Exception as e:
35 return None, '%s' % e
37 class DamosAccessPattern:
38 size = None
39 nr_accesses = None
40 age = None
41 scheme = None
43 def __init__(self, size=None, nr_accesses=None, age=None):
44 self.size = size
45 self.nr_accesses = nr_accesses
46 self.age = age
48 if self.size is None:
49 self.size = [0, 2**64 - 1]
50 if self.nr_accesses is None:
51 self.nr_accesses = [0, 2**64 - 1]
52 if self.age is None:
53 self.age = [0, 2**64 - 1]
55 def sysfs_dir(self):
56 return os.path.join(self.scheme.sysfs_dir(), 'access_pattern')
58 def stage(self):
59 err = write_file(
60 os.path.join(self.sysfs_dir(), 'sz', 'min'), self.size[0])
61 if err is not None:
62 return err
63 err = write_file(
64 os.path.join(self.sysfs_dir(), 'sz', 'max'), self.size[1])
65 if err is not None:
66 return err
67 err = write_file(os.path.join(self.sysfs_dir(), 'nr_accesses', 'min'),
68 self.nr_accesses[0])
69 if err is not None:
70 return err
71 err = write_file(os.path.join(self.sysfs_dir(), 'nr_accesses', 'max'),
72 self.nr_accesses[1])
73 if err is not None:
74 return err
75 err = write_file(
76 os.path.join(self.sysfs_dir(), 'age', 'min'), self.age[0])
77 if err is not None:
78 return err
79 err = write_file(
80 os.path.join(self.sysfs_dir(), 'age', 'max'), self.age[1])
81 if err is not None:
82 return err
84 qgoal_metric_user_input = 'user_input'
85 qgoal_metric_some_mem_psi_us = 'some_mem_psi_us'
86 qgoal_metrics = [qgoal_metric_user_input, qgoal_metric_some_mem_psi_us]
88 class DamosQuotaGoal:
89 metric = None
90 target_value = None
91 current_value = None
92 effective_bytes = None
93 quota = None # owner quota
94 idx = None
96 def __init__(self, metric, target_value=10000, current_value=0):
97 self.metric = metric
98 self.target_value = target_value
99 self.current_value = current_value
101 def sysfs_dir(self):
102 return os.path.join(self.quota.sysfs_dir(), 'goals', '%d' % self.idx)
104 def stage(self):
105 err = write_file(os.path.join(self.sysfs_dir(), 'target_metric'),
106 self.metric)
107 if err is not None:
108 return err
109 err = write_file(os.path.join(self.sysfs_dir(), 'target_value'),
110 self.target_value)
111 if err is not None:
112 return err
113 err = write_file(os.path.join(self.sysfs_dir(), 'current_value'),
114 self.current_value)
115 if err is not None:
116 return err
117 return None
119 class DamosQuota:
120 sz = None # size quota, in bytes
121 ms = None # time quota
122 goals = None # quota goals
123 reset_interval_ms = None # quota reset interval
124 scheme = None # owner scheme
126 def __init__(self, sz=0, ms=0, goals=None, reset_interval_ms=0):
127 self.sz = sz
128 self.ms = ms
129 self.reset_interval_ms = reset_interval_ms
130 self.goals = goals if goals is not None else []
131 for idx, goal in enumerate(self.goals):
132 goal.idx = idx
133 goal.quota = self
135 def sysfs_dir(self):
136 return os.path.join(self.scheme.sysfs_dir(), 'quotas')
138 def stage(self):
139 err = write_file(os.path.join(self.sysfs_dir(), 'bytes'), self.sz)
140 if err is not None:
141 return err
142 err = write_file(os.path.join(self.sysfs_dir(), 'ms'), self.ms)
143 if err is not None:
144 return err
145 err = write_file(os.path.join(self.sysfs_dir(), 'reset_interval_ms'),
146 self.reset_interval_ms)
147 if err is not None:
148 return err
150 nr_goals_file = os.path.join(self.sysfs_dir(), 'goals', 'nr_goals')
151 content, err = read_file(nr_goals_file)
152 if err is not None:
153 return err
154 if int(content) != len(self.goals):
155 err = write_file(nr_goals_file, len(self.goals))
156 if err is not None:
157 return err
158 for goal in self.goals:
159 err = goal.stage()
160 if err is not None:
161 return err
162 return None
164 class DamosStats:
165 nr_tried = None
166 sz_tried = None
167 nr_applied = None
168 sz_applied = None
169 qt_exceeds = None
171 def __init__(self, nr_tried, sz_tried, nr_applied, sz_applied, qt_exceeds):
172 self.nr_tried = nr_tried
173 self.sz_tried = sz_tried
174 self.nr_applied = nr_applied
175 self.sz_applied = sz_applied
176 self.qt_exceeds = qt_exceeds
178 class DamosTriedRegion:
179 def __init__(self, start, end, nr_accesses, age):
180 self.start = start
181 self.end = end
182 self.nr_accesses = nr_accesses
183 self.age = age
185 class Damos:
186 action = None
187 access_pattern = None
188 quota = None
189 apply_interval_us = None
190 # todo: Support watermarks, stats
191 idx = None
192 context = None
193 tried_bytes = None
194 stats = None
195 tried_regions = None
197 def __init__(self, action='stat', access_pattern=DamosAccessPattern(),
198 quota=DamosQuota(), apply_interval_us=0):
199 self.action = action
200 self.access_pattern = access_pattern
201 self.access_pattern.scheme = self
202 self.quota = quota
203 self.quota.scheme = self
204 self.apply_interval_us = apply_interval_us
206 def sysfs_dir(self):
207 return os.path.join(
208 self.context.sysfs_dir(), 'schemes', '%d' % self.idx)
210 def stage(self):
211 err = write_file(os.path.join(self.sysfs_dir(), 'action'), self.action)
212 if err is not None:
213 return err
214 err = self.access_pattern.stage()
215 if err is not None:
216 return err
217 err = write_file(os.path.join(self.sysfs_dir(), 'apply_interval_us'),
218 '%d' % self.apply_interval_us)
219 if err is not None:
220 return err
222 err = self.quota.stage()
223 if err is not None:
224 return err
226 # disable watermarks
227 err = write_file(
228 os.path.join(self.sysfs_dir(), 'watermarks', 'metric'), 'none')
229 if err is not None:
230 return err
232 # disable filters
233 err = write_file(
234 os.path.join(self.sysfs_dir(), 'filters', 'nr_filters'), '0')
235 if err is not None:
236 return err
238 class DamonTarget:
239 pid = None
240 # todo: Support target regions if test is made
241 idx = None
242 context = None
244 def __init__(self, pid):
245 self.pid = pid
247 def sysfs_dir(self):
248 return os.path.join(
249 self.context.sysfs_dir(), 'targets', '%d' % self.idx)
251 def stage(self):
252 err = write_file(
253 os.path.join(self.sysfs_dir(), 'regions', 'nr_regions'), '0')
254 if err is not None:
255 return err
256 return write_file(
257 os.path.join(self.sysfs_dir(), 'pid_target'), self.pid)
259 class DamonAttrs:
260 sample_us = None
261 aggr_us = None
262 update_us = None
263 min_nr_regions = None
264 max_nr_regions = None
265 context = None
267 def __init__(self, sample_us=5000, aggr_us=100000, update_us=1000000,
268 min_nr_regions=10, max_nr_regions=1000):
269 self.sample_us = sample_us
270 self.aggr_us = aggr_us
271 self.update_us = update_us
272 self.min_nr_regions = min_nr_regions
273 self.max_nr_regions = max_nr_regions
275 def interval_sysfs_dir(self):
276 return os.path.join(self.context.sysfs_dir(), 'monitoring_attrs',
277 'intervals')
279 def nr_regions_range_sysfs_dir(self):
280 return os.path.join(self.context.sysfs_dir(), 'monitoring_attrs',
281 'nr_regions')
283 def stage(self):
284 err = write_file(os.path.join(self.interval_sysfs_dir(), 'sample_us'),
285 self.sample_us)
286 if err is not None:
287 return err
288 err = write_file(os.path.join(self.interval_sysfs_dir(), 'aggr_us'),
289 self.aggr_us)
290 if err is not None:
291 return err
292 err = write_file(os.path.join(self.interval_sysfs_dir(), 'update_us'),
293 self.update_us)
294 if err is not None:
295 return err
297 err = write_file(
298 os.path.join(self.nr_regions_range_sysfs_dir(), 'min'),
299 self.min_nr_regions)
300 if err is not None:
301 return err
303 err = write_file(
304 os.path.join(self.nr_regions_range_sysfs_dir(), 'max'),
305 self.max_nr_regions)
306 if err is not None:
307 return err
309 class DamonCtx:
310 ops = None
311 monitoring_attrs = None
312 targets = None
313 schemes = None
314 kdamond = None
315 idx = None
317 def __init__(self, ops='paddr', monitoring_attrs=DamonAttrs(), targets=[],
318 schemes=[]):
319 self.ops = ops
320 self.monitoring_attrs = monitoring_attrs
321 self.monitoring_attrs.context = self
323 self.targets = targets
324 for idx, target in enumerate(self.targets):
325 target.idx = idx
326 target.context = self
328 self.schemes = schemes
329 for idx, scheme in enumerate(self.schemes):
330 scheme.idx = idx
331 scheme.context = self
333 def sysfs_dir(self):
334 return os.path.join(self.kdamond.sysfs_dir(), 'contexts',
335 '%d' % self.idx)
337 def stage(self):
338 err = write_file(
339 os.path.join(self.sysfs_dir(), 'operations'), self.ops)
340 if err is not None:
341 return err
342 err = self.monitoring_attrs.stage()
343 if err is not None:
344 return err
346 nr_targets_file = os.path.join(
347 self.sysfs_dir(), 'targets', 'nr_targets')
348 content, err = read_file(nr_targets_file)
349 if err is not None:
350 return err
351 if int(content) != len(self.targets):
352 err = write_file(nr_targets_file, '%d' % len(self.targets))
353 if err is not None:
354 return err
355 for target in self.targets:
356 err = target.stage()
357 if err is not None:
358 return err
360 nr_schemes_file = os.path.join(
361 self.sysfs_dir(), 'schemes', 'nr_schemes')
362 content, err = read_file(nr_schemes_file)
363 if err is not None:
364 return err
365 if int(content) != len(self.schemes):
366 err = write_file(nr_schemes_file, '%d' % len(self.schemes))
367 if err is not None:
368 return err
369 for scheme in self.schemes:
370 err = scheme.stage()
371 if err is not None:
372 return err
373 return None
375 class Kdamond:
376 state = None
377 pid = None
378 contexts = None
379 idx = None # index of this kdamond between siblings
380 kdamonds = None # parent
382 def __init__(self, contexts=[]):
383 self.contexts = contexts
384 for idx, context in enumerate(self.contexts):
385 context.idx = idx
386 context.kdamond = self
388 def sysfs_dir(self):
389 return os.path.join(self.kdamonds.sysfs_dir(), '%d' % self.idx)
391 def start(self):
392 nr_contexts_file = os.path.join(self.sysfs_dir(),
393 'contexts', 'nr_contexts')
394 content, err = read_file(nr_contexts_file)
395 if err is not None:
396 return err
397 if int(content) != len(self.contexts):
398 err = write_file(nr_contexts_file, '%d' % len(self.contexts))
399 if err is not None:
400 return err
402 for context in self.contexts:
403 err = context.stage()
404 if err is not None:
405 return err
406 err = write_file(os.path.join(self.sysfs_dir(), 'state'), 'on')
407 return err
409 def stop(self):
410 err = write_file(os.path.join(self.sysfs_dir(), 'state'), 'off')
411 return err
413 def update_schemes_tried_regions(self):
414 err = write_file(os.path.join(self.sysfs_dir(), 'state'),
415 'update_schemes_tried_regions')
416 if err is not None:
417 return err
418 for context in self.contexts:
419 for scheme in context.schemes:
420 tried_regions = []
421 tried_regions_dir = os.path.join(
422 scheme.sysfs_dir(), 'tried_regions')
423 for filename in os.listdir(
424 os.path.join(scheme.sysfs_dir(), 'tried_regions')):
425 tried_region_dir = os.path.join(tried_regions_dir, filename)
426 if not os.path.isdir(tried_region_dir):
427 continue
428 region_values = []
429 for f in ['start', 'end', 'nr_accesses', 'age']:
430 content, err = read_file(
431 os.path.join(tried_region_dir, f))
432 if err is not None:
433 return err
434 region_values.append(int(content))
435 tried_regions.append(DamosTriedRegion(*region_values))
436 scheme.tried_regions = tried_regions
438 def update_schemes_tried_bytes(self):
439 err = write_file(os.path.join(self.sysfs_dir(), 'state'),
440 'update_schemes_tried_bytes')
441 if err is not None:
442 return err
443 for context in self.contexts:
444 for scheme in context.schemes:
445 content, err = read_file(os.path.join(scheme.sysfs_dir(),
446 'tried_regions', 'total_bytes'))
447 if err is not None:
448 return err
449 scheme.tried_bytes = int(content)
451 def update_schemes_stats(self):
452 err = write_file(os.path.join(self.sysfs_dir(), 'state'),
453 'update_schemes_stats')
454 if err is not None:
455 return err
456 for context in self.contexts:
457 for scheme in context.schemes:
458 stat_values = []
459 for stat in ['nr_tried', 'sz_tried', 'nr_applied',
460 'sz_applied', 'qt_exceeds']:
461 content, err = read_file(
462 os.path.join(scheme.sysfs_dir(), 'stats', stat))
463 if err is not None:
464 return err
465 stat_values.append(int(content))
466 scheme.stats = DamosStats(*stat_values)
468 def update_schemes_effective_quotas(self):
469 err = write_file(os.path.join(self.sysfs_dir(), 'state'),
470 'update_schemes_effective_quotas')
471 if err is not None:
472 return err
473 for context in self.contexts:
474 for scheme in context.schemes:
475 for goal in scheme.quota.goals:
476 content, err = read_file(
477 os.path.join(scheme.quota.sysfs_dir(),
478 'effective_bytes'))
479 if err is not None:
480 return err
481 goal.effective_bytes = int(content)
482 return None
484 def commit(self):
485 nr_contexts_file = os.path.join(self.sysfs_dir(),
486 'contexts', 'nr_contexts')
487 content, err = read_file(nr_contexts_file)
488 if err is not None:
489 return err
490 if int(content) != len(self.contexts):
491 err = write_file(nr_contexts_file, '%d' % len(self.contexts))
492 if err is not None:
493 return err
495 for context in self.contexts:
496 err = context.stage()
497 if err is not None:
498 return err
499 err = write_file(os.path.join(self.sysfs_dir(), 'state'), 'commit')
500 return err
503 def commit_schemes_quota_goals(self):
504 for context in self.contexts:
505 for scheme in context.schemes:
506 for goal in scheme.quota.goals:
507 err = goal.stage()
508 if err is not None:
509 print('commit_schemes_quota_goals failed stagign: %s'%
510 err)
511 exit(1)
512 return write_file(os.path.join(self.sysfs_dir(), 'state'),
513 'commit_schemes_quota_goals')
515 class Kdamonds:
516 kdamonds = []
518 def __init__(self, kdamonds=[]):
519 self.kdamonds = kdamonds
520 for idx, kdamond in enumerate(self.kdamonds):
521 kdamond.idx = idx
522 kdamond.kdamonds = self
524 def sysfs_dir(self):
525 return os.path.join(sysfs_root, 'kdamonds')
527 def start(self):
528 err = write_file(os.path.join(self.sysfs_dir(), 'nr_kdamonds'),
529 '%s' % len(self.kdamonds))
530 if err is not None:
531 return err
532 for kdamond in self.kdamonds:
533 err = kdamond.start()
534 if err is not None:
535 return err
536 return None
538 def stop(self):
539 for kdamond in self.kdamonds:
540 err = kdamond.stop()
541 if err is not None:
542 return err
543 return None