Commit | Line | Data |
---|---|---|
c8e7b4d0 SP |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * DAMON sysfs Interface | |
4 | * | |
5 | * Copyright (c) 2022 SeongJae Park <sj@kernel.org> | |
6 | */ | |
7 | ||
8 | #include <linux/slab.h> | |
9 | ||
10 | #include "sysfs-common.h" | |
11 | ||
9277d036 SP |
12 | /* |
13 | * scheme region directory | |
14 | */ | |
15 | ||
16 | struct damon_sysfs_scheme_region { | |
17 | struct kobject kobj; | |
18 | struct damon_addr_range ar; | |
19 | unsigned int nr_accesses; | |
20 | unsigned int age; | |
21 | struct list_head list; | |
22 | }; | |
23 | ||
24 | static struct damon_sysfs_scheme_region *damon_sysfs_scheme_region_alloc( | |
25 | struct damon_region *region) | |
26 | { | |
27 | struct damon_sysfs_scheme_region *sysfs_region = kmalloc( | |
28 | sizeof(*sysfs_region), GFP_KERNEL); | |
29 | ||
30 | if (!sysfs_region) | |
31 | return NULL; | |
32 | sysfs_region->kobj = (struct kobject){}; | |
33 | sysfs_region->ar = region->ar; | |
34 | sysfs_region->nr_accesses = region->nr_accesses; | |
35 | sysfs_region->age = region->age; | |
36 | INIT_LIST_HEAD(&sysfs_region->list); | |
37 | return sysfs_region; | |
38 | } | |
39 | ||
40 | static ssize_t start_show(struct kobject *kobj, struct kobj_attribute *attr, | |
41 | char *buf) | |
42 | { | |
43 | struct damon_sysfs_scheme_region *region = container_of(kobj, | |
44 | struct damon_sysfs_scheme_region, kobj); | |
45 | ||
46 | return sysfs_emit(buf, "%lu\n", region->ar.start); | |
47 | } | |
48 | ||
49 | static ssize_t end_show(struct kobject *kobj, struct kobj_attribute *attr, | |
50 | char *buf) | |
51 | { | |
52 | struct damon_sysfs_scheme_region *region = container_of(kobj, | |
53 | struct damon_sysfs_scheme_region, kobj); | |
54 | ||
55 | return sysfs_emit(buf, "%lu\n", region->ar.end); | |
56 | } | |
57 | ||
58 | static ssize_t nr_accesses_show(struct kobject *kobj, | |
59 | struct kobj_attribute *attr, char *buf) | |
60 | { | |
61 | struct damon_sysfs_scheme_region *region = container_of(kobj, | |
62 | struct damon_sysfs_scheme_region, kobj); | |
63 | ||
64 | return sysfs_emit(buf, "%u\n", region->nr_accesses); | |
65 | } | |
66 | ||
67 | static ssize_t age_show(struct kobject *kobj, struct kobj_attribute *attr, | |
68 | char *buf) | |
69 | { | |
70 | struct damon_sysfs_scheme_region *region = container_of(kobj, | |
71 | struct damon_sysfs_scheme_region, kobj); | |
72 | ||
73 | return sysfs_emit(buf, "%u\n", region->age); | |
74 | } | |
75 | ||
76 | static void damon_sysfs_scheme_region_release(struct kobject *kobj) | |
77 | { | |
78 | struct damon_sysfs_scheme_region *region = container_of(kobj, | |
79 | struct damon_sysfs_scheme_region, kobj); | |
80 | ||
81 | list_del(®ion->list); | |
82 | kfree(region); | |
83 | } | |
84 | ||
85 | static struct kobj_attribute damon_sysfs_scheme_region_start_attr = | |
86 | __ATTR_RO_MODE(start, 0400); | |
87 | ||
88 | static struct kobj_attribute damon_sysfs_scheme_region_end_attr = | |
89 | __ATTR_RO_MODE(end, 0400); | |
90 | ||
91 | static struct kobj_attribute damon_sysfs_scheme_region_nr_accesses_attr = | |
92 | __ATTR_RO_MODE(nr_accesses, 0400); | |
93 | ||
94 | static struct kobj_attribute damon_sysfs_scheme_region_age_attr = | |
95 | __ATTR_RO_MODE(age, 0400); | |
96 | ||
97 | static struct attribute *damon_sysfs_scheme_region_attrs[] = { | |
98 | &damon_sysfs_scheme_region_start_attr.attr, | |
99 | &damon_sysfs_scheme_region_end_attr.attr, | |
100 | &damon_sysfs_scheme_region_nr_accesses_attr.attr, | |
101 | &damon_sysfs_scheme_region_age_attr.attr, | |
102 | NULL, | |
103 | }; | |
104 | ATTRIBUTE_GROUPS(damon_sysfs_scheme_region); | |
105 | ||
106 | static struct kobj_type damon_sysfs_scheme_region_ktype = { | |
107 | .release = damon_sysfs_scheme_region_release, | |
108 | .sysfs_ops = &kobj_sysfs_ops, | |
109 | .default_groups = damon_sysfs_scheme_region_groups, | |
110 | }; | |
111 | ||
5181b75f SP |
112 | /* |
113 | * scheme regions directory | |
114 | */ | |
115 | ||
116 | struct damon_sysfs_scheme_regions { | |
117 | struct kobject kobj; | |
9277d036 SP |
118 | struct list_head regions_list; |
119 | int nr_regions; | |
5181b75f SP |
120 | }; |
121 | ||
122 | static struct damon_sysfs_scheme_regions * | |
123 | damon_sysfs_scheme_regions_alloc(void) | |
124 | { | |
9277d036 SP |
125 | struct damon_sysfs_scheme_regions *regions = kmalloc(sizeof(*regions), |
126 | GFP_KERNEL); | |
127 | ||
128 | regions->kobj = (struct kobject){}; | |
129 | INIT_LIST_HEAD(®ions->regions_list); | |
130 | regions->nr_regions = 0; | |
131 | return regions; | |
132 | } | |
133 | ||
134 | static void damon_sysfs_scheme_regions_rm_dirs( | |
135 | struct damon_sysfs_scheme_regions *regions) | |
136 | { | |
137 | struct damon_sysfs_scheme_region *r, *next; | |
138 | ||
139 | list_for_each_entry_safe(r, next, ®ions->regions_list, list) { | |
140 | /* release function deletes it from the list */ | |
141 | kobject_put(&r->kobj); | |
142 | regions->nr_regions--; | |
143 | } | |
5181b75f SP |
144 | } |
145 | ||
146 | static void damon_sysfs_scheme_regions_release(struct kobject *kobj) | |
147 | { | |
148 | kfree(container_of(kobj, struct damon_sysfs_scheme_regions, kobj)); | |
149 | } | |
150 | ||
151 | static struct attribute *damon_sysfs_scheme_regions_attrs[] = { | |
152 | NULL, | |
153 | }; | |
154 | ATTRIBUTE_GROUPS(damon_sysfs_scheme_regions); | |
155 | ||
156 | static struct kobj_type damon_sysfs_scheme_regions_ktype = { | |
157 | .release = damon_sysfs_scheme_regions_release, | |
158 | .sysfs_ops = &kobj_sysfs_ops, | |
159 | .default_groups = damon_sysfs_scheme_regions_groups, | |
160 | }; | |
161 | ||
c8e7b4d0 SP |
162 | /* |
163 | * schemes/stats directory | |
164 | */ | |
165 | ||
166 | struct damon_sysfs_stats { | |
167 | struct kobject kobj; | |
168 | unsigned long nr_tried; | |
169 | unsigned long sz_tried; | |
170 | unsigned long nr_applied; | |
171 | unsigned long sz_applied; | |
172 | unsigned long qt_exceeds; | |
173 | }; | |
174 | ||
175 | static struct damon_sysfs_stats *damon_sysfs_stats_alloc(void) | |
176 | { | |
177 | return kzalloc(sizeof(struct damon_sysfs_stats), GFP_KERNEL); | |
178 | } | |
179 | ||
180 | static ssize_t nr_tried_show(struct kobject *kobj, struct kobj_attribute *attr, | |
181 | char *buf) | |
182 | { | |
183 | struct damon_sysfs_stats *stats = container_of(kobj, | |
184 | struct damon_sysfs_stats, kobj); | |
185 | ||
186 | return sysfs_emit(buf, "%lu\n", stats->nr_tried); | |
187 | } | |
188 | ||
189 | static ssize_t sz_tried_show(struct kobject *kobj, struct kobj_attribute *attr, | |
190 | char *buf) | |
191 | { | |
192 | struct damon_sysfs_stats *stats = container_of(kobj, | |
193 | struct damon_sysfs_stats, kobj); | |
194 | ||
195 | return sysfs_emit(buf, "%lu\n", stats->sz_tried); | |
196 | } | |
197 | ||
198 | static ssize_t nr_applied_show(struct kobject *kobj, | |
199 | struct kobj_attribute *attr, char *buf) | |
200 | { | |
201 | struct damon_sysfs_stats *stats = container_of(kobj, | |
202 | struct damon_sysfs_stats, kobj); | |
203 | ||
204 | return sysfs_emit(buf, "%lu\n", stats->nr_applied); | |
205 | } | |
206 | ||
207 | static ssize_t sz_applied_show(struct kobject *kobj, | |
208 | struct kobj_attribute *attr, char *buf) | |
209 | { | |
210 | struct damon_sysfs_stats *stats = container_of(kobj, | |
211 | struct damon_sysfs_stats, kobj); | |
212 | ||
213 | return sysfs_emit(buf, "%lu\n", stats->sz_applied); | |
214 | } | |
215 | ||
216 | static ssize_t qt_exceeds_show(struct kobject *kobj, | |
217 | struct kobj_attribute *attr, char *buf) | |
218 | { | |
219 | struct damon_sysfs_stats *stats = container_of(kobj, | |
220 | struct damon_sysfs_stats, kobj); | |
221 | ||
222 | return sysfs_emit(buf, "%lu\n", stats->qt_exceeds); | |
223 | } | |
224 | ||
225 | static void damon_sysfs_stats_release(struct kobject *kobj) | |
226 | { | |
227 | kfree(container_of(kobj, struct damon_sysfs_stats, kobj)); | |
228 | } | |
229 | ||
230 | static struct kobj_attribute damon_sysfs_stats_nr_tried_attr = | |
231 | __ATTR_RO_MODE(nr_tried, 0400); | |
232 | ||
233 | static struct kobj_attribute damon_sysfs_stats_sz_tried_attr = | |
234 | __ATTR_RO_MODE(sz_tried, 0400); | |
235 | ||
236 | static struct kobj_attribute damon_sysfs_stats_nr_applied_attr = | |
237 | __ATTR_RO_MODE(nr_applied, 0400); | |
238 | ||
239 | static struct kobj_attribute damon_sysfs_stats_sz_applied_attr = | |
240 | __ATTR_RO_MODE(sz_applied, 0400); | |
241 | ||
242 | static struct kobj_attribute damon_sysfs_stats_qt_exceeds_attr = | |
243 | __ATTR_RO_MODE(qt_exceeds, 0400); | |
244 | ||
245 | static struct attribute *damon_sysfs_stats_attrs[] = { | |
246 | &damon_sysfs_stats_nr_tried_attr.attr, | |
247 | &damon_sysfs_stats_sz_tried_attr.attr, | |
248 | &damon_sysfs_stats_nr_applied_attr.attr, | |
249 | &damon_sysfs_stats_sz_applied_attr.attr, | |
250 | &damon_sysfs_stats_qt_exceeds_attr.attr, | |
251 | NULL, | |
252 | }; | |
253 | ATTRIBUTE_GROUPS(damon_sysfs_stats); | |
254 | ||
255 | static struct kobj_type damon_sysfs_stats_ktype = { | |
256 | .release = damon_sysfs_stats_release, | |
257 | .sysfs_ops = &kobj_sysfs_ops, | |
258 | .default_groups = damon_sysfs_stats_groups, | |
259 | }; | |
260 | ||
261 | /* | |
262 | * watermarks directory | |
263 | */ | |
264 | ||
265 | struct damon_sysfs_watermarks { | |
266 | struct kobject kobj; | |
267 | enum damos_wmark_metric metric; | |
268 | unsigned long interval_us; | |
269 | unsigned long high; | |
270 | unsigned long mid; | |
271 | unsigned long low; | |
272 | }; | |
273 | ||
274 | static struct damon_sysfs_watermarks *damon_sysfs_watermarks_alloc( | |
275 | enum damos_wmark_metric metric, unsigned long interval_us, | |
276 | unsigned long high, unsigned long mid, unsigned long low) | |
277 | { | |
278 | struct damon_sysfs_watermarks *watermarks = kmalloc( | |
279 | sizeof(*watermarks), GFP_KERNEL); | |
280 | ||
281 | if (!watermarks) | |
282 | return NULL; | |
283 | watermarks->kobj = (struct kobject){}; | |
284 | watermarks->metric = metric; | |
285 | watermarks->interval_us = interval_us; | |
286 | watermarks->high = high; | |
287 | watermarks->mid = mid; | |
288 | watermarks->low = low; | |
289 | return watermarks; | |
290 | } | |
291 | ||
292 | /* Should match with enum damos_wmark_metric */ | |
293 | static const char * const damon_sysfs_wmark_metric_strs[] = { | |
294 | "none", | |
295 | "free_mem_rate", | |
296 | }; | |
297 | ||
298 | static ssize_t metric_show(struct kobject *kobj, struct kobj_attribute *attr, | |
299 | char *buf) | |
300 | { | |
301 | struct damon_sysfs_watermarks *watermarks = container_of(kobj, | |
302 | struct damon_sysfs_watermarks, kobj); | |
303 | ||
304 | return sysfs_emit(buf, "%s\n", | |
305 | damon_sysfs_wmark_metric_strs[watermarks->metric]); | |
306 | } | |
307 | ||
308 | static ssize_t metric_store(struct kobject *kobj, struct kobj_attribute *attr, | |
309 | const char *buf, size_t count) | |
310 | { | |
311 | struct damon_sysfs_watermarks *watermarks = container_of(kobj, | |
312 | struct damon_sysfs_watermarks, kobj); | |
313 | enum damos_wmark_metric metric; | |
314 | ||
315 | for (metric = 0; metric < NR_DAMOS_WMARK_METRICS; metric++) { | |
316 | if (sysfs_streq(buf, damon_sysfs_wmark_metric_strs[metric])) { | |
317 | watermarks->metric = metric; | |
318 | return count; | |
319 | } | |
320 | } | |
321 | return -EINVAL; | |
322 | } | |
323 | ||
324 | static ssize_t interval_us_show(struct kobject *kobj, | |
325 | struct kobj_attribute *attr, char *buf) | |
326 | { | |
327 | struct damon_sysfs_watermarks *watermarks = container_of(kobj, | |
328 | struct damon_sysfs_watermarks, kobj); | |
329 | ||
330 | return sysfs_emit(buf, "%lu\n", watermarks->interval_us); | |
331 | } | |
332 | ||
333 | static ssize_t interval_us_store(struct kobject *kobj, | |
334 | struct kobj_attribute *attr, const char *buf, size_t count) | |
335 | { | |
336 | struct damon_sysfs_watermarks *watermarks = container_of(kobj, | |
337 | struct damon_sysfs_watermarks, kobj); | |
338 | int err = kstrtoul(buf, 0, &watermarks->interval_us); | |
339 | ||
340 | return err ? err : count; | |
341 | } | |
342 | ||
343 | static ssize_t high_show(struct kobject *kobj, | |
344 | struct kobj_attribute *attr, char *buf) | |
345 | { | |
346 | struct damon_sysfs_watermarks *watermarks = container_of(kobj, | |
347 | struct damon_sysfs_watermarks, kobj); | |
348 | ||
349 | return sysfs_emit(buf, "%lu\n", watermarks->high); | |
350 | } | |
351 | ||
352 | static ssize_t high_store(struct kobject *kobj, | |
353 | struct kobj_attribute *attr, const char *buf, size_t count) | |
354 | { | |
355 | struct damon_sysfs_watermarks *watermarks = container_of(kobj, | |
356 | struct damon_sysfs_watermarks, kobj); | |
357 | int err = kstrtoul(buf, 0, &watermarks->high); | |
358 | ||
359 | return err ? err : count; | |
360 | } | |
361 | ||
362 | static ssize_t mid_show(struct kobject *kobj, | |
363 | struct kobj_attribute *attr, char *buf) | |
364 | { | |
365 | struct damon_sysfs_watermarks *watermarks = container_of(kobj, | |
366 | struct damon_sysfs_watermarks, kobj); | |
367 | ||
368 | return sysfs_emit(buf, "%lu\n", watermarks->mid); | |
369 | } | |
370 | ||
371 | static ssize_t mid_store(struct kobject *kobj, | |
372 | struct kobj_attribute *attr, const char *buf, size_t count) | |
373 | { | |
374 | struct damon_sysfs_watermarks *watermarks = container_of(kobj, | |
375 | struct damon_sysfs_watermarks, kobj); | |
376 | int err = kstrtoul(buf, 0, &watermarks->mid); | |
377 | ||
378 | return err ? err : count; | |
379 | } | |
380 | ||
381 | static ssize_t low_show(struct kobject *kobj, | |
382 | struct kobj_attribute *attr, char *buf) | |
383 | { | |
384 | struct damon_sysfs_watermarks *watermarks = container_of(kobj, | |
385 | struct damon_sysfs_watermarks, kobj); | |
386 | ||
387 | return sysfs_emit(buf, "%lu\n", watermarks->low); | |
388 | } | |
389 | ||
390 | static ssize_t low_store(struct kobject *kobj, | |
391 | struct kobj_attribute *attr, const char *buf, size_t count) | |
392 | { | |
393 | struct damon_sysfs_watermarks *watermarks = container_of(kobj, | |
394 | struct damon_sysfs_watermarks, kobj); | |
395 | int err = kstrtoul(buf, 0, &watermarks->low); | |
396 | ||
397 | return err ? err : count; | |
398 | } | |
399 | ||
400 | static void damon_sysfs_watermarks_release(struct kobject *kobj) | |
401 | { | |
402 | kfree(container_of(kobj, struct damon_sysfs_watermarks, kobj)); | |
403 | } | |
404 | ||
405 | static struct kobj_attribute damon_sysfs_watermarks_metric_attr = | |
406 | __ATTR_RW_MODE(metric, 0600); | |
407 | ||
408 | static struct kobj_attribute damon_sysfs_watermarks_interval_us_attr = | |
409 | __ATTR_RW_MODE(interval_us, 0600); | |
410 | ||
411 | static struct kobj_attribute damon_sysfs_watermarks_high_attr = | |
412 | __ATTR_RW_MODE(high, 0600); | |
413 | ||
414 | static struct kobj_attribute damon_sysfs_watermarks_mid_attr = | |
415 | __ATTR_RW_MODE(mid, 0600); | |
416 | ||
417 | static struct kobj_attribute damon_sysfs_watermarks_low_attr = | |
418 | __ATTR_RW_MODE(low, 0600); | |
419 | ||
420 | static struct attribute *damon_sysfs_watermarks_attrs[] = { | |
421 | &damon_sysfs_watermarks_metric_attr.attr, | |
422 | &damon_sysfs_watermarks_interval_us_attr.attr, | |
423 | &damon_sysfs_watermarks_high_attr.attr, | |
424 | &damon_sysfs_watermarks_mid_attr.attr, | |
425 | &damon_sysfs_watermarks_low_attr.attr, | |
426 | NULL, | |
427 | }; | |
428 | ATTRIBUTE_GROUPS(damon_sysfs_watermarks); | |
429 | ||
430 | static struct kobj_type damon_sysfs_watermarks_ktype = { | |
431 | .release = damon_sysfs_watermarks_release, | |
432 | .sysfs_ops = &kobj_sysfs_ops, | |
433 | .default_groups = damon_sysfs_watermarks_groups, | |
434 | }; | |
435 | ||
436 | /* | |
437 | * scheme/weights directory | |
438 | */ | |
439 | ||
440 | struct damon_sysfs_weights { | |
441 | struct kobject kobj; | |
442 | unsigned int sz; | |
443 | unsigned int nr_accesses; | |
444 | unsigned int age; | |
445 | }; | |
446 | ||
447 | static struct damon_sysfs_weights *damon_sysfs_weights_alloc(unsigned int sz, | |
448 | unsigned int nr_accesses, unsigned int age) | |
449 | { | |
450 | struct damon_sysfs_weights *weights = kmalloc(sizeof(*weights), | |
451 | GFP_KERNEL); | |
452 | ||
453 | if (!weights) | |
454 | return NULL; | |
455 | weights->kobj = (struct kobject){}; | |
456 | weights->sz = sz; | |
457 | weights->nr_accesses = nr_accesses; | |
458 | weights->age = age; | |
459 | return weights; | |
460 | } | |
461 | ||
462 | static ssize_t sz_permil_show(struct kobject *kobj, | |
463 | struct kobj_attribute *attr, char *buf) | |
464 | { | |
465 | struct damon_sysfs_weights *weights = container_of(kobj, | |
466 | struct damon_sysfs_weights, kobj); | |
467 | ||
468 | return sysfs_emit(buf, "%u\n", weights->sz); | |
469 | } | |
470 | ||
471 | static ssize_t sz_permil_store(struct kobject *kobj, | |
472 | struct kobj_attribute *attr, const char *buf, size_t count) | |
473 | { | |
474 | struct damon_sysfs_weights *weights = container_of(kobj, | |
475 | struct damon_sysfs_weights, kobj); | |
476 | int err = kstrtouint(buf, 0, &weights->sz); | |
477 | ||
478 | return err ? err : count; | |
479 | } | |
480 | ||
481 | static ssize_t nr_accesses_permil_show(struct kobject *kobj, | |
482 | struct kobj_attribute *attr, char *buf) | |
483 | { | |
484 | struct damon_sysfs_weights *weights = container_of(kobj, | |
485 | struct damon_sysfs_weights, kobj); | |
486 | ||
487 | return sysfs_emit(buf, "%u\n", weights->nr_accesses); | |
488 | } | |
489 | ||
490 | static ssize_t nr_accesses_permil_store(struct kobject *kobj, | |
491 | struct kobj_attribute *attr, const char *buf, size_t count) | |
492 | { | |
493 | struct damon_sysfs_weights *weights = container_of(kobj, | |
494 | struct damon_sysfs_weights, kobj); | |
495 | int err = kstrtouint(buf, 0, &weights->nr_accesses); | |
496 | ||
497 | return err ? err : count; | |
498 | } | |
499 | ||
500 | static ssize_t age_permil_show(struct kobject *kobj, | |
501 | struct kobj_attribute *attr, char *buf) | |
502 | { | |
503 | struct damon_sysfs_weights *weights = container_of(kobj, | |
504 | struct damon_sysfs_weights, kobj); | |
505 | ||
506 | return sysfs_emit(buf, "%u\n", weights->age); | |
507 | } | |
508 | ||
509 | static ssize_t age_permil_store(struct kobject *kobj, | |
510 | struct kobj_attribute *attr, const char *buf, size_t count) | |
511 | { | |
512 | struct damon_sysfs_weights *weights = container_of(kobj, | |
513 | struct damon_sysfs_weights, kobj); | |
514 | int err = kstrtouint(buf, 0, &weights->age); | |
515 | ||
516 | return err ? err : count; | |
517 | } | |
518 | ||
519 | static void damon_sysfs_weights_release(struct kobject *kobj) | |
520 | { | |
521 | kfree(container_of(kobj, struct damon_sysfs_weights, kobj)); | |
522 | } | |
523 | ||
524 | static struct kobj_attribute damon_sysfs_weights_sz_attr = | |
525 | __ATTR_RW_MODE(sz_permil, 0600); | |
526 | ||
527 | static struct kobj_attribute damon_sysfs_weights_nr_accesses_attr = | |
528 | __ATTR_RW_MODE(nr_accesses_permil, 0600); | |
529 | ||
530 | static struct kobj_attribute damon_sysfs_weights_age_attr = | |
531 | __ATTR_RW_MODE(age_permil, 0600); | |
532 | ||
533 | static struct attribute *damon_sysfs_weights_attrs[] = { | |
534 | &damon_sysfs_weights_sz_attr.attr, | |
535 | &damon_sysfs_weights_nr_accesses_attr.attr, | |
536 | &damon_sysfs_weights_age_attr.attr, | |
537 | NULL, | |
538 | }; | |
539 | ATTRIBUTE_GROUPS(damon_sysfs_weights); | |
540 | ||
541 | static struct kobj_type damon_sysfs_weights_ktype = { | |
542 | .release = damon_sysfs_weights_release, | |
543 | .sysfs_ops = &kobj_sysfs_ops, | |
544 | .default_groups = damon_sysfs_weights_groups, | |
545 | }; | |
546 | ||
547 | /* | |
548 | * quotas directory | |
549 | */ | |
550 | ||
551 | struct damon_sysfs_quotas { | |
552 | struct kobject kobj; | |
553 | struct damon_sysfs_weights *weights; | |
554 | unsigned long ms; | |
555 | unsigned long sz; | |
556 | unsigned long reset_interval_ms; | |
557 | }; | |
558 | ||
559 | static struct damon_sysfs_quotas *damon_sysfs_quotas_alloc(void) | |
560 | { | |
561 | return kzalloc(sizeof(struct damon_sysfs_quotas), GFP_KERNEL); | |
562 | } | |
563 | ||
564 | static int damon_sysfs_quotas_add_dirs(struct damon_sysfs_quotas *quotas) | |
565 | { | |
566 | struct damon_sysfs_weights *weights; | |
567 | int err; | |
568 | ||
569 | weights = damon_sysfs_weights_alloc(0, 0, 0); | |
570 | if (!weights) | |
571 | return -ENOMEM; | |
572 | ||
573 | err = kobject_init_and_add(&weights->kobj, &damon_sysfs_weights_ktype, | |
574 | "as->kobj, "weights"); | |
575 | if (err) | |
576 | kobject_put(&weights->kobj); | |
577 | else | |
578 | quotas->weights = weights; | |
579 | return err; | |
580 | } | |
581 | ||
582 | static void damon_sysfs_quotas_rm_dirs(struct damon_sysfs_quotas *quotas) | |
583 | { | |
584 | kobject_put("as->weights->kobj); | |
585 | } | |
586 | ||
587 | static ssize_t ms_show(struct kobject *kobj, struct kobj_attribute *attr, | |
588 | char *buf) | |
589 | { | |
590 | struct damon_sysfs_quotas *quotas = container_of(kobj, | |
591 | struct damon_sysfs_quotas, kobj); | |
592 | ||
593 | return sysfs_emit(buf, "%lu\n", quotas->ms); | |
594 | } | |
595 | ||
596 | static ssize_t ms_store(struct kobject *kobj, struct kobj_attribute *attr, | |
597 | const char *buf, size_t count) | |
598 | { | |
599 | struct damon_sysfs_quotas *quotas = container_of(kobj, | |
600 | struct damon_sysfs_quotas, kobj); | |
601 | int err = kstrtoul(buf, 0, "as->ms); | |
602 | ||
603 | if (err) | |
604 | return -EINVAL; | |
605 | return count; | |
606 | } | |
607 | ||
608 | static ssize_t bytes_show(struct kobject *kobj, struct kobj_attribute *attr, | |
609 | char *buf) | |
610 | { | |
611 | struct damon_sysfs_quotas *quotas = container_of(kobj, | |
612 | struct damon_sysfs_quotas, kobj); | |
613 | ||
614 | return sysfs_emit(buf, "%lu\n", quotas->sz); | |
615 | } | |
616 | ||
617 | static ssize_t bytes_store(struct kobject *kobj, | |
618 | struct kobj_attribute *attr, const char *buf, size_t count) | |
619 | { | |
620 | struct damon_sysfs_quotas *quotas = container_of(kobj, | |
621 | struct damon_sysfs_quotas, kobj); | |
622 | int err = kstrtoul(buf, 0, "as->sz); | |
623 | ||
624 | if (err) | |
625 | return -EINVAL; | |
626 | return count; | |
627 | } | |
628 | ||
629 | static ssize_t reset_interval_ms_show(struct kobject *kobj, | |
630 | struct kobj_attribute *attr, char *buf) | |
631 | { | |
632 | struct damon_sysfs_quotas *quotas = container_of(kobj, | |
633 | struct damon_sysfs_quotas, kobj); | |
634 | ||
635 | return sysfs_emit(buf, "%lu\n", quotas->reset_interval_ms); | |
636 | } | |
637 | ||
638 | static ssize_t reset_interval_ms_store(struct kobject *kobj, | |
639 | struct kobj_attribute *attr, const char *buf, size_t count) | |
640 | { | |
641 | struct damon_sysfs_quotas *quotas = container_of(kobj, | |
642 | struct damon_sysfs_quotas, kobj); | |
643 | int err = kstrtoul(buf, 0, "as->reset_interval_ms); | |
644 | ||
645 | if (err) | |
646 | return -EINVAL; | |
647 | return count; | |
648 | } | |
649 | ||
650 | static void damon_sysfs_quotas_release(struct kobject *kobj) | |
651 | { | |
652 | kfree(container_of(kobj, struct damon_sysfs_quotas, kobj)); | |
653 | } | |
654 | ||
655 | static struct kobj_attribute damon_sysfs_quotas_ms_attr = | |
656 | __ATTR_RW_MODE(ms, 0600); | |
657 | ||
658 | static struct kobj_attribute damon_sysfs_quotas_sz_attr = | |
659 | __ATTR_RW_MODE(bytes, 0600); | |
660 | ||
661 | static struct kobj_attribute damon_sysfs_quotas_reset_interval_ms_attr = | |
662 | __ATTR_RW_MODE(reset_interval_ms, 0600); | |
663 | ||
664 | static struct attribute *damon_sysfs_quotas_attrs[] = { | |
665 | &damon_sysfs_quotas_ms_attr.attr, | |
666 | &damon_sysfs_quotas_sz_attr.attr, | |
667 | &damon_sysfs_quotas_reset_interval_ms_attr.attr, | |
668 | NULL, | |
669 | }; | |
670 | ATTRIBUTE_GROUPS(damon_sysfs_quotas); | |
671 | ||
672 | static struct kobj_type damon_sysfs_quotas_ktype = { | |
673 | .release = damon_sysfs_quotas_release, | |
674 | .sysfs_ops = &kobj_sysfs_ops, | |
675 | .default_groups = damon_sysfs_quotas_groups, | |
676 | }; | |
677 | ||
678 | /* | |
679 | * access_pattern directory | |
680 | */ | |
681 | ||
682 | struct damon_sysfs_access_pattern { | |
683 | struct kobject kobj; | |
684 | struct damon_sysfs_ul_range *sz; | |
685 | struct damon_sysfs_ul_range *nr_accesses; | |
686 | struct damon_sysfs_ul_range *age; | |
687 | }; | |
688 | ||
689 | static | |
690 | struct damon_sysfs_access_pattern *damon_sysfs_access_pattern_alloc(void) | |
691 | { | |
692 | struct damon_sysfs_access_pattern *access_pattern = | |
693 | kmalloc(sizeof(*access_pattern), GFP_KERNEL); | |
694 | ||
695 | if (!access_pattern) | |
696 | return NULL; | |
697 | access_pattern->kobj = (struct kobject){}; | |
698 | return access_pattern; | |
699 | } | |
700 | ||
701 | static int damon_sysfs_access_pattern_add_range_dir( | |
702 | struct damon_sysfs_access_pattern *access_pattern, | |
703 | struct damon_sysfs_ul_range **range_dir_ptr, | |
704 | char *name) | |
705 | { | |
706 | struct damon_sysfs_ul_range *range = damon_sysfs_ul_range_alloc(0, 0); | |
707 | int err; | |
708 | ||
709 | if (!range) | |
710 | return -ENOMEM; | |
711 | err = kobject_init_and_add(&range->kobj, &damon_sysfs_ul_range_ktype, | |
712 | &access_pattern->kobj, name); | |
713 | if (err) | |
714 | kobject_put(&range->kobj); | |
715 | else | |
716 | *range_dir_ptr = range; | |
717 | return err; | |
718 | } | |
719 | ||
720 | static int damon_sysfs_access_pattern_add_dirs( | |
721 | struct damon_sysfs_access_pattern *access_pattern) | |
722 | { | |
723 | int err; | |
724 | ||
725 | err = damon_sysfs_access_pattern_add_range_dir(access_pattern, | |
726 | &access_pattern->sz, "sz"); | |
727 | if (err) | |
728 | goto put_sz_out; | |
729 | ||
730 | err = damon_sysfs_access_pattern_add_range_dir(access_pattern, | |
731 | &access_pattern->nr_accesses, "nr_accesses"); | |
732 | if (err) | |
733 | goto put_nr_accesses_sz_out; | |
734 | ||
735 | err = damon_sysfs_access_pattern_add_range_dir(access_pattern, | |
736 | &access_pattern->age, "age"); | |
737 | if (err) | |
738 | goto put_age_nr_accesses_sz_out; | |
739 | return 0; | |
740 | ||
741 | put_age_nr_accesses_sz_out: | |
742 | kobject_put(&access_pattern->age->kobj); | |
743 | access_pattern->age = NULL; | |
744 | put_nr_accesses_sz_out: | |
745 | kobject_put(&access_pattern->nr_accesses->kobj); | |
746 | access_pattern->nr_accesses = NULL; | |
747 | put_sz_out: | |
748 | kobject_put(&access_pattern->sz->kobj); | |
749 | access_pattern->sz = NULL; | |
750 | return err; | |
751 | } | |
752 | ||
753 | static void damon_sysfs_access_pattern_rm_dirs( | |
754 | struct damon_sysfs_access_pattern *access_pattern) | |
755 | { | |
756 | kobject_put(&access_pattern->sz->kobj); | |
757 | kobject_put(&access_pattern->nr_accesses->kobj); | |
758 | kobject_put(&access_pattern->age->kobj); | |
759 | } | |
760 | ||
761 | static void damon_sysfs_access_pattern_release(struct kobject *kobj) | |
762 | { | |
763 | kfree(container_of(kobj, struct damon_sysfs_access_pattern, kobj)); | |
764 | } | |
765 | ||
766 | static struct attribute *damon_sysfs_access_pattern_attrs[] = { | |
767 | NULL, | |
768 | }; | |
769 | ATTRIBUTE_GROUPS(damon_sysfs_access_pattern); | |
770 | ||
771 | static struct kobj_type damon_sysfs_access_pattern_ktype = { | |
772 | .release = damon_sysfs_access_pattern_release, | |
773 | .sysfs_ops = &kobj_sysfs_ops, | |
774 | .default_groups = damon_sysfs_access_pattern_groups, | |
775 | }; | |
776 | ||
777 | /* | |
778 | * scheme directory | |
779 | */ | |
780 | ||
781 | struct damon_sysfs_scheme { | |
782 | struct kobject kobj; | |
783 | enum damos_action action; | |
784 | struct damon_sysfs_access_pattern *access_pattern; | |
785 | struct damon_sysfs_quotas *quotas; | |
786 | struct damon_sysfs_watermarks *watermarks; | |
787 | struct damon_sysfs_stats *stats; | |
5181b75f | 788 | struct damon_sysfs_scheme_regions *tried_regions; |
c8e7b4d0 SP |
789 | }; |
790 | ||
791 | /* This should match with enum damos_action */ | |
792 | static const char * const damon_sysfs_damos_action_strs[] = { | |
793 | "willneed", | |
794 | "cold", | |
795 | "pageout", | |
796 | "hugepage", | |
797 | "nohugepage", | |
798 | "lru_prio", | |
799 | "lru_deprio", | |
800 | "stat", | |
801 | }; | |
802 | ||
803 | static struct damon_sysfs_scheme *damon_sysfs_scheme_alloc( | |
804 | enum damos_action action) | |
805 | { | |
806 | struct damon_sysfs_scheme *scheme = kmalloc(sizeof(*scheme), | |
807 | GFP_KERNEL); | |
808 | ||
809 | if (!scheme) | |
810 | return NULL; | |
811 | scheme->kobj = (struct kobject){}; | |
812 | scheme->action = action; | |
813 | return scheme; | |
814 | } | |
815 | ||
816 | static int damon_sysfs_scheme_set_access_pattern( | |
817 | struct damon_sysfs_scheme *scheme) | |
818 | { | |
819 | struct damon_sysfs_access_pattern *access_pattern; | |
820 | int err; | |
821 | ||
822 | access_pattern = damon_sysfs_access_pattern_alloc(); | |
823 | if (!access_pattern) | |
824 | return -ENOMEM; | |
825 | err = kobject_init_and_add(&access_pattern->kobj, | |
826 | &damon_sysfs_access_pattern_ktype, &scheme->kobj, | |
827 | "access_pattern"); | |
828 | if (err) | |
829 | goto out; | |
830 | err = damon_sysfs_access_pattern_add_dirs(access_pattern); | |
831 | if (err) | |
832 | goto out; | |
833 | scheme->access_pattern = access_pattern; | |
834 | return 0; | |
835 | ||
836 | out: | |
837 | kobject_put(&access_pattern->kobj); | |
838 | return err; | |
839 | } | |
840 | ||
841 | static int damon_sysfs_scheme_set_quotas(struct damon_sysfs_scheme *scheme) | |
842 | { | |
843 | struct damon_sysfs_quotas *quotas = damon_sysfs_quotas_alloc(); | |
844 | int err; | |
845 | ||
846 | if (!quotas) | |
847 | return -ENOMEM; | |
848 | err = kobject_init_and_add("as->kobj, &damon_sysfs_quotas_ktype, | |
849 | &scheme->kobj, "quotas"); | |
850 | if (err) | |
851 | goto out; | |
852 | err = damon_sysfs_quotas_add_dirs(quotas); | |
853 | if (err) | |
854 | goto out; | |
855 | scheme->quotas = quotas; | |
856 | return 0; | |
857 | ||
858 | out: | |
859 | kobject_put("as->kobj); | |
860 | return err; | |
861 | } | |
862 | ||
863 | static int damon_sysfs_scheme_set_watermarks(struct damon_sysfs_scheme *scheme) | |
864 | { | |
865 | struct damon_sysfs_watermarks *watermarks = | |
866 | damon_sysfs_watermarks_alloc(DAMOS_WMARK_NONE, 0, 0, 0, 0); | |
867 | int err; | |
868 | ||
869 | if (!watermarks) | |
870 | return -ENOMEM; | |
871 | err = kobject_init_and_add(&watermarks->kobj, | |
872 | &damon_sysfs_watermarks_ktype, &scheme->kobj, | |
873 | "watermarks"); | |
874 | if (err) | |
875 | kobject_put(&watermarks->kobj); | |
876 | else | |
877 | scheme->watermarks = watermarks; | |
878 | return err; | |
879 | } | |
880 | ||
881 | static int damon_sysfs_scheme_set_stats(struct damon_sysfs_scheme *scheme) | |
882 | { | |
883 | struct damon_sysfs_stats *stats = damon_sysfs_stats_alloc(); | |
884 | int err; | |
885 | ||
886 | if (!stats) | |
887 | return -ENOMEM; | |
888 | err = kobject_init_and_add(&stats->kobj, &damon_sysfs_stats_ktype, | |
889 | &scheme->kobj, "stats"); | |
890 | if (err) | |
891 | kobject_put(&stats->kobj); | |
892 | else | |
893 | scheme->stats = stats; | |
894 | return err; | |
895 | } | |
896 | ||
5181b75f SP |
897 | static int damon_sysfs_scheme_set_tried_regions( |
898 | struct damon_sysfs_scheme *scheme) | |
899 | { | |
900 | struct damon_sysfs_scheme_regions *tried_regions = | |
901 | damon_sysfs_scheme_regions_alloc(); | |
902 | int err; | |
903 | ||
904 | if (!tried_regions) | |
905 | return -ENOMEM; | |
906 | err = kobject_init_and_add(&tried_regions->kobj, | |
907 | &damon_sysfs_scheme_regions_ktype, &scheme->kobj, | |
908 | "tried_regions"); | |
909 | if (err) | |
910 | kobject_put(&tried_regions->kobj); | |
911 | else | |
912 | scheme->tried_regions = tried_regions; | |
913 | return err; | |
914 | } | |
915 | ||
c8e7b4d0 SP |
916 | static int damon_sysfs_scheme_add_dirs(struct damon_sysfs_scheme *scheme) |
917 | { | |
918 | int err; | |
919 | ||
920 | err = damon_sysfs_scheme_set_access_pattern(scheme); | |
921 | if (err) | |
922 | return err; | |
923 | err = damon_sysfs_scheme_set_quotas(scheme); | |
924 | if (err) | |
925 | goto put_access_pattern_out; | |
926 | err = damon_sysfs_scheme_set_watermarks(scheme); | |
927 | if (err) | |
928 | goto put_quotas_access_pattern_out; | |
929 | err = damon_sysfs_scheme_set_stats(scheme); | |
930 | if (err) | |
931 | goto put_watermarks_quotas_access_pattern_out; | |
5181b75f SP |
932 | err = damon_sysfs_scheme_set_tried_regions(scheme); |
933 | if (err) | |
934 | goto put_tried_regions_out; | |
c8e7b4d0 SP |
935 | return 0; |
936 | ||
5181b75f SP |
937 | put_tried_regions_out: |
938 | kobject_put(&scheme->tried_regions->kobj); | |
939 | scheme->tried_regions = NULL; | |
c8e7b4d0 SP |
940 | put_watermarks_quotas_access_pattern_out: |
941 | kobject_put(&scheme->watermarks->kobj); | |
942 | scheme->watermarks = NULL; | |
943 | put_quotas_access_pattern_out: | |
944 | kobject_put(&scheme->quotas->kobj); | |
945 | scheme->quotas = NULL; | |
946 | put_access_pattern_out: | |
947 | kobject_put(&scheme->access_pattern->kobj); | |
948 | scheme->access_pattern = NULL; | |
949 | return err; | |
950 | } | |
951 | ||
952 | static void damon_sysfs_scheme_rm_dirs(struct damon_sysfs_scheme *scheme) | |
953 | { | |
954 | damon_sysfs_access_pattern_rm_dirs(scheme->access_pattern); | |
955 | kobject_put(&scheme->access_pattern->kobj); | |
956 | damon_sysfs_quotas_rm_dirs(scheme->quotas); | |
957 | kobject_put(&scheme->quotas->kobj); | |
958 | kobject_put(&scheme->watermarks->kobj); | |
959 | kobject_put(&scheme->stats->kobj); | |
9277d036 | 960 | damon_sysfs_scheme_regions_rm_dirs(scheme->tried_regions); |
5181b75f | 961 | kobject_put(&scheme->tried_regions->kobj); |
c8e7b4d0 SP |
962 | } |
963 | ||
964 | static ssize_t action_show(struct kobject *kobj, struct kobj_attribute *attr, | |
965 | char *buf) | |
966 | { | |
967 | struct damon_sysfs_scheme *scheme = container_of(kobj, | |
968 | struct damon_sysfs_scheme, kobj); | |
969 | ||
970 | return sysfs_emit(buf, "%s\n", | |
971 | damon_sysfs_damos_action_strs[scheme->action]); | |
972 | } | |
973 | ||
974 | static ssize_t action_store(struct kobject *kobj, struct kobj_attribute *attr, | |
975 | const char *buf, size_t count) | |
976 | { | |
977 | struct damon_sysfs_scheme *scheme = container_of(kobj, | |
978 | struct damon_sysfs_scheme, kobj); | |
979 | enum damos_action action; | |
980 | ||
981 | for (action = 0; action < NR_DAMOS_ACTIONS; action++) { | |
982 | if (sysfs_streq(buf, damon_sysfs_damos_action_strs[action])) { | |
983 | scheme->action = action; | |
984 | return count; | |
985 | } | |
986 | } | |
987 | return -EINVAL; | |
988 | } | |
989 | ||
990 | static void damon_sysfs_scheme_release(struct kobject *kobj) | |
991 | { | |
992 | kfree(container_of(kobj, struct damon_sysfs_scheme, kobj)); | |
993 | } | |
994 | ||
995 | static struct kobj_attribute damon_sysfs_scheme_action_attr = | |
996 | __ATTR_RW_MODE(action, 0600); | |
997 | ||
998 | static struct attribute *damon_sysfs_scheme_attrs[] = { | |
999 | &damon_sysfs_scheme_action_attr.attr, | |
1000 | NULL, | |
1001 | }; | |
1002 | ATTRIBUTE_GROUPS(damon_sysfs_scheme); | |
1003 | ||
1004 | static struct kobj_type damon_sysfs_scheme_ktype = { | |
1005 | .release = damon_sysfs_scheme_release, | |
1006 | .sysfs_ops = &kobj_sysfs_ops, | |
1007 | .default_groups = damon_sysfs_scheme_groups, | |
1008 | }; | |
1009 | ||
1010 | /* | |
1011 | * schemes directory | |
1012 | */ | |
1013 | ||
1014 | struct damon_sysfs_schemes *damon_sysfs_schemes_alloc(void) | |
1015 | { | |
1016 | return kzalloc(sizeof(struct damon_sysfs_schemes), GFP_KERNEL); | |
1017 | } | |
1018 | ||
1019 | void damon_sysfs_schemes_rm_dirs(struct damon_sysfs_schemes *schemes) | |
1020 | { | |
1021 | struct damon_sysfs_scheme **schemes_arr = schemes->schemes_arr; | |
1022 | int i; | |
1023 | ||
1024 | for (i = 0; i < schemes->nr; i++) { | |
1025 | damon_sysfs_scheme_rm_dirs(schemes_arr[i]); | |
1026 | kobject_put(&schemes_arr[i]->kobj); | |
1027 | } | |
1028 | schemes->nr = 0; | |
1029 | kfree(schemes_arr); | |
1030 | schemes->schemes_arr = NULL; | |
1031 | } | |
1032 | ||
1033 | static int damon_sysfs_schemes_add_dirs(struct damon_sysfs_schemes *schemes, | |
1034 | int nr_schemes) | |
1035 | { | |
1036 | struct damon_sysfs_scheme **schemes_arr, *scheme; | |
1037 | int err, i; | |
1038 | ||
1039 | damon_sysfs_schemes_rm_dirs(schemes); | |
1040 | if (!nr_schemes) | |
1041 | return 0; | |
1042 | ||
1043 | schemes_arr = kmalloc_array(nr_schemes, sizeof(*schemes_arr), | |
1044 | GFP_KERNEL | __GFP_NOWARN); | |
1045 | if (!schemes_arr) | |
1046 | return -ENOMEM; | |
1047 | schemes->schemes_arr = schemes_arr; | |
1048 | ||
1049 | for (i = 0; i < nr_schemes; i++) { | |
1050 | scheme = damon_sysfs_scheme_alloc(DAMOS_STAT); | |
1051 | if (!scheme) { | |
1052 | damon_sysfs_schemes_rm_dirs(schemes); | |
1053 | return -ENOMEM; | |
1054 | } | |
1055 | ||
1056 | err = kobject_init_and_add(&scheme->kobj, | |
1057 | &damon_sysfs_scheme_ktype, &schemes->kobj, | |
1058 | "%d", i); | |
1059 | if (err) | |
1060 | goto out; | |
1061 | err = damon_sysfs_scheme_add_dirs(scheme); | |
1062 | if (err) | |
1063 | goto out; | |
1064 | ||
1065 | schemes_arr[i] = scheme; | |
1066 | schemes->nr++; | |
1067 | } | |
1068 | return 0; | |
1069 | ||
1070 | out: | |
1071 | damon_sysfs_schemes_rm_dirs(schemes); | |
1072 | kobject_put(&scheme->kobj); | |
1073 | return err; | |
1074 | } | |
1075 | ||
1076 | static ssize_t nr_schemes_show(struct kobject *kobj, | |
1077 | struct kobj_attribute *attr, char *buf) | |
1078 | { | |
1079 | struct damon_sysfs_schemes *schemes = container_of(kobj, | |
1080 | struct damon_sysfs_schemes, kobj); | |
1081 | ||
1082 | return sysfs_emit(buf, "%d\n", schemes->nr); | |
1083 | } | |
1084 | ||
1085 | static ssize_t nr_schemes_store(struct kobject *kobj, | |
1086 | struct kobj_attribute *attr, const char *buf, size_t count) | |
1087 | { | |
1088 | struct damon_sysfs_schemes *schemes; | |
1089 | int nr, err = kstrtoint(buf, 0, &nr); | |
1090 | ||
1091 | if (err) | |
1092 | return err; | |
1093 | if (nr < 0) | |
1094 | return -EINVAL; | |
1095 | ||
1096 | schemes = container_of(kobj, struct damon_sysfs_schemes, kobj); | |
1097 | ||
1098 | if (!mutex_trylock(&damon_sysfs_lock)) | |
1099 | return -EBUSY; | |
1100 | err = damon_sysfs_schemes_add_dirs(schemes, nr); | |
1101 | mutex_unlock(&damon_sysfs_lock); | |
1102 | if (err) | |
1103 | return err; | |
1104 | return count; | |
1105 | } | |
1106 | ||
1107 | static void damon_sysfs_schemes_release(struct kobject *kobj) | |
1108 | { | |
1109 | kfree(container_of(kobj, struct damon_sysfs_schemes, kobj)); | |
1110 | } | |
1111 | ||
1112 | static struct kobj_attribute damon_sysfs_schemes_nr_attr = | |
1113 | __ATTR_RW_MODE(nr_schemes, 0600); | |
1114 | ||
1115 | static struct attribute *damon_sysfs_schemes_attrs[] = { | |
1116 | &damon_sysfs_schemes_nr_attr.attr, | |
1117 | NULL, | |
1118 | }; | |
1119 | ATTRIBUTE_GROUPS(damon_sysfs_schemes); | |
1120 | ||
1121 | struct kobj_type damon_sysfs_schemes_ktype = { | |
1122 | .release = damon_sysfs_schemes_release, | |
1123 | .sysfs_ops = &kobj_sysfs_ops, | |
1124 | .default_groups = damon_sysfs_schemes_groups, | |
1125 | }; | |
1126 | ||
1127 | static struct damos *damon_sysfs_mk_scheme( | |
1128 | struct damon_sysfs_scheme *sysfs_scheme) | |
1129 | { | |
1130 | struct damon_sysfs_access_pattern *access_pattern = | |
1131 | sysfs_scheme->access_pattern; | |
1132 | struct damon_sysfs_quotas *sysfs_quotas = sysfs_scheme->quotas; | |
1133 | struct damon_sysfs_weights *sysfs_weights = sysfs_quotas->weights; | |
1134 | struct damon_sysfs_watermarks *sysfs_wmarks = sysfs_scheme->watermarks; | |
1135 | ||
1136 | struct damos_access_pattern pattern = { | |
1137 | .min_sz_region = access_pattern->sz->min, | |
1138 | .max_sz_region = access_pattern->sz->max, | |
1139 | .min_nr_accesses = access_pattern->nr_accesses->min, | |
1140 | .max_nr_accesses = access_pattern->nr_accesses->max, | |
1141 | .min_age_region = access_pattern->age->min, | |
1142 | .max_age_region = access_pattern->age->max, | |
1143 | }; | |
1144 | struct damos_quota quota = { | |
1145 | .ms = sysfs_quotas->ms, | |
1146 | .sz = sysfs_quotas->sz, | |
1147 | .reset_interval = sysfs_quotas->reset_interval_ms, | |
1148 | .weight_sz = sysfs_weights->sz, | |
1149 | .weight_nr_accesses = sysfs_weights->nr_accesses, | |
1150 | .weight_age = sysfs_weights->age, | |
1151 | }; | |
1152 | struct damos_watermarks wmarks = { | |
1153 | .metric = sysfs_wmarks->metric, | |
1154 | .interval = sysfs_wmarks->interval_us, | |
1155 | .high = sysfs_wmarks->high, | |
1156 | .mid = sysfs_wmarks->mid, | |
1157 | .low = sysfs_wmarks->low, | |
1158 | }; | |
1159 | ||
1160 | return damon_new_scheme(&pattern, sysfs_scheme->action, "a, | |
1161 | &wmarks); | |
1162 | } | |
1163 | ||
1164 | static void damon_sysfs_update_scheme(struct damos *scheme, | |
1165 | struct damon_sysfs_scheme *sysfs_scheme) | |
1166 | { | |
1167 | struct damon_sysfs_access_pattern *access_pattern = | |
1168 | sysfs_scheme->access_pattern; | |
1169 | struct damon_sysfs_quotas *sysfs_quotas = sysfs_scheme->quotas; | |
1170 | struct damon_sysfs_weights *sysfs_weights = sysfs_quotas->weights; | |
1171 | struct damon_sysfs_watermarks *sysfs_wmarks = sysfs_scheme->watermarks; | |
1172 | ||
1173 | scheme->pattern.min_sz_region = access_pattern->sz->min; | |
1174 | scheme->pattern.max_sz_region = access_pattern->sz->max; | |
1175 | scheme->pattern.min_nr_accesses = access_pattern->nr_accesses->min; | |
1176 | scheme->pattern.max_nr_accesses = access_pattern->nr_accesses->max; | |
1177 | scheme->pattern.min_age_region = access_pattern->age->min; | |
1178 | scheme->pattern.max_age_region = access_pattern->age->max; | |
1179 | ||
1180 | scheme->action = sysfs_scheme->action; | |
1181 | ||
1182 | scheme->quota.ms = sysfs_quotas->ms; | |
1183 | scheme->quota.sz = sysfs_quotas->sz; | |
1184 | scheme->quota.reset_interval = sysfs_quotas->reset_interval_ms; | |
1185 | scheme->quota.weight_sz = sysfs_weights->sz; | |
1186 | scheme->quota.weight_nr_accesses = sysfs_weights->nr_accesses; | |
1187 | scheme->quota.weight_age = sysfs_weights->age; | |
1188 | ||
1189 | scheme->wmarks.metric = sysfs_wmarks->metric; | |
1190 | scheme->wmarks.interval = sysfs_wmarks->interval_us; | |
1191 | scheme->wmarks.high = sysfs_wmarks->high; | |
1192 | scheme->wmarks.mid = sysfs_wmarks->mid; | |
1193 | scheme->wmarks.low = sysfs_wmarks->low; | |
1194 | } | |
1195 | ||
1196 | int damon_sysfs_set_schemes(struct damon_ctx *ctx, | |
1197 | struct damon_sysfs_schemes *sysfs_schemes) | |
1198 | { | |
1199 | struct damos *scheme, *next; | |
1200 | int i = 0; | |
1201 | ||
1202 | damon_for_each_scheme_safe(scheme, next, ctx) { | |
1203 | if (i < sysfs_schemes->nr) | |
1204 | damon_sysfs_update_scheme(scheme, | |
1205 | sysfs_schemes->schemes_arr[i]); | |
1206 | else | |
1207 | damon_destroy_scheme(scheme); | |
1208 | i++; | |
1209 | } | |
1210 | ||
1211 | for (; i < sysfs_schemes->nr; i++) { | |
1212 | struct damos *scheme, *next; | |
1213 | ||
1214 | scheme = damon_sysfs_mk_scheme(sysfs_schemes->schemes_arr[i]); | |
1215 | if (!scheme) { | |
1216 | damon_for_each_scheme_safe(scheme, next, ctx) | |
1217 | damon_destroy_scheme(scheme); | |
1218 | return -ENOMEM; | |
1219 | } | |
1220 | damon_add_scheme(ctx, scheme); | |
1221 | } | |
1222 | return 0; | |
1223 | } | |
1224 | ||
1225 | void damon_sysfs_schemes_update_stats( | |
1226 | struct damon_sysfs_schemes *sysfs_schemes, | |
1227 | struct damon_ctx *ctx) | |
1228 | { | |
1229 | struct damos *scheme; | |
1230 | int schemes_idx = 0; | |
1231 | ||
1232 | damon_for_each_scheme(scheme, ctx) { | |
1233 | struct damon_sysfs_stats *sysfs_stats; | |
1234 | ||
1235 | /* user could have removed the scheme sysfs dir */ | |
1236 | if (schemes_idx >= sysfs_schemes->nr) | |
1237 | break; | |
1238 | ||
1239 | sysfs_stats = sysfs_schemes->schemes_arr[schemes_idx++]->stats; | |
1240 | sysfs_stats->nr_tried = scheme->stat.nr_tried; | |
1241 | sysfs_stats->sz_tried = scheme->stat.sz_tried; | |
1242 | sysfs_stats->nr_applied = scheme->stat.nr_applied; | |
1243 | sysfs_stats->sz_applied = scheme->stat.sz_applied; | |
1244 | sysfs_stats->qt_exceeds = scheme->stat.qt_exceeds; | |
1245 | } | |
1246 | } | |
f1d13cac SP |
1247 | |
1248 | /* | |
1249 | * damon_sysfs_schemes that need to update its schemes regions dir. Protected | |
1250 | * by damon_sysfs_lock | |
1251 | */ | |
1252 | static struct damon_sysfs_schemes *damon_sysfs_schemes_for_damos_callback; | |
1253 | static int damon_sysfs_schemes_region_idx; | |
1254 | ||
1255 | /* | |
1256 | * DAMON callback that called before damos apply. While this callback is | |
1257 | * registered, damon_sysfs_lock should be held to ensure the regions | |
1258 | * directories exist. | |
1259 | */ | |
1260 | static int damon_sysfs_before_damos_apply(struct damon_ctx *ctx, | |
1261 | struct damon_target *t, struct damon_region *r, | |
1262 | struct damos *s) | |
1263 | { | |
1264 | struct damos *scheme; | |
1265 | struct damon_sysfs_scheme_regions *sysfs_regions; | |
1266 | struct damon_sysfs_scheme_region *region; | |
1267 | struct damon_sysfs_schemes *sysfs_schemes = | |
1268 | damon_sysfs_schemes_for_damos_callback; | |
1269 | int schemes_idx = 0; | |
1270 | ||
1271 | damon_for_each_scheme(scheme, ctx) { | |
1272 | if (scheme == s) | |
1273 | break; | |
1274 | schemes_idx++; | |
1275 | } | |
1276 | ||
1277 | /* user could have removed the scheme sysfs dir */ | |
1278 | if (schemes_idx >= sysfs_schemes->nr) | |
1279 | return 0; | |
1280 | ||
1281 | sysfs_regions = sysfs_schemes->schemes_arr[schemes_idx]->tried_regions; | |
1282 | region = damon_sysfs_scheme_region_alloc(r); | |
1283 | list_add_tail(®ion->list, &sysfs_regions->regions_list); | |
1284 | sysfs_regions->nr_regions++; | |
1285 | if (kobject_init_and_add(®ion->kobj, | |
1286 | &damon_sysfs_scheme_region_ktype, | |
1287 | &sysfs_regions->kobj, "%d", | |
1288 | damon_sysfs_schemes_region_idx++)) { | |
1289 | kobject_put(®ion->kobj); | |
1290 | } | |
1291 | return 0; | |
1292 | } | |
1293 | ||
1294 | /* Called from damon_sysfs_cmd_request_callback under damon_sysfs_lock */ | |
772c15e5 | 1295 | int damon_sysfs_schemes_clear_regions( |
f1d13cac SP |
1296 | struct damon_sysfs_schemes *sysfs_schemes, |
1297 | struct damon_ctx *ctx) | |
1298 | { | |
1299 | struct damos *scheme; | |
1300 | int schemes_idx = 0; | |
1301 | ||
1302 | damon_for_each_scheme(scheme, ctx) { | |
1303 | struct damon_sysfs_scheme *sysfs_scheme; | |
1304 | ||
772c15e5 SP |
1305 | /* user could have removed the scheme sysfs dir */ |
1306 | if (schemes_idx >= sysfs_schemes->nr) | |
1307 | break; | |
1308 | ||
f1d13cac SP |
1309 | sysfs_scheme = sysfs_schemes->schemes_arr[schemes_idx++]; |
1310 | damon_sysfs_scheme_regions_rm_dirs( | |
1311 | sysfs_scheme->tried_regions); | |
1312 | } | |
772c15e5 SP |
1313 | return 0; |
1314 | } | |
f1d13cac | 1315 | |
772c15e5 SP |
1316 | /* Called from damon_sysfs_cmd_request_callback under damon_sysfs_lock */ |
1317 | int damon_sysfs_schemes_update_regions_start( | |
1318 | struct damon_sysfs_schemes *sysfs_schemes, | |
1319 | struct damon_ctx *ctx) | |
1320 | { | |
1321 | damon_sysfs_schemes_clear_regions(sysfs_schemes, ctx); | |
f1d13cac SP |
1322 | damon_sysfs_schemes_for_damos_callback = sysfs_schemes; |
1323 | ctx->callback.before_damos_apply = damon_sysfs_before_damos_apply; | |
1324 | return 0; | |
1325 | } | |
1326 | ||
1327 | /* | |
1328 | * Called from damon_sysfs_cmd_request_callback under damon_sysfs_lock. Caller | |
1329 | * should unlock damon_sysfs_lock which held before | |
1330 | * damon_sysfs_schemes_update_regions_start() | |
1331 | */ | |
1332 | int damon_sysfs_schemes_update_regions_stop(struct damon_ctx *ctx) | |
1333 | { | |
1334 | damon_sysfs_schemes_for_damos_callback = NULL; | |
1335 | ctx->callback.before_damos_apply = NULL; | |
1336 | damon_sysfs_schemes_region_idx = 0; | |
1337 | return 0; | |
1338 | } |