blob: 48e434cd43d8e87e50672248f8a0e5a3d00c1f0d [file] [log] [blame]
SeongJae Parkc951cd32022-03-22 14:49:27 -07001// 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/damon.h>
9#include <linux/kobject.h>
10#include <linux/pid.h>
11#include <linux/sched.h>
12#include <linux/slab.h>
13
14static DEFINE_MUTEX(damon_sysfs_lock);
15
16/*
17 * unsigned long range directory
18 */
19
20struct damon_sysfs_ul_range {
21 struct kobject kobj;
22 unsigned long min;
23 unsigned long max;
24};
25
26static struct damon_sysfs_ul_range *damon_sysfs_ul_range_alloc(
27 unsigned long min,
28 unsigned long max)
29{
30 struct damon_sysfs_ul_range *range = kmalloc(sizeof(*range),
31 GFP_KERNEL);
32
33 if (!range)
34 return NULL;
35 range->kobj = (struct kobject){};
36 range->min = min;
37 range->max = max;
38
39 return range;
40}
41
42static ssize_t min_show(struct kobject *kobj, struct kobj_attribute *attr,
43 char *buf)
44{
45 struct damon_sysfs_ul_range *range = container_of(kobj,
46 struct damon_sysfs_ul_range, kobj);
47
48 return sysfs_emit(buf, "%lu\n", range->min);
49}
50
51static ssize_t min_store(struct kobject *kobj, struct kobj_attribute *attr,
52 const char *buf, size_t count)
53{
54 struct damon_sysfs_ul_range *range = container_of(kobj,
55 struct damon_sysfs_ul_range, kobj);
56 unsigned long min;
57 int err;
58
59 err = kstrtoul(buf, 0, &min);
60 if (err)
61 return -EINVAL;
62
63 range->min = min;
64 return count;
65}
66
67static ssize_t max_show(struct kobject *kobj, struct kobj_attribute *attr,
68 char *buf)
69{
70 struct damon_sysfs_ul_range *range = container_of(kobj,
71 struct damon_sysfs_ul_range, kobj);
72
73 return sysfs_emit(buf, "%lu\n", range->max);
74}
75
76static ssize_t max_store(struct kobject *kobj, struct kobj_attribute *attr,
77 const char *buf, size_t count)
78{
79 struct damon_sysfs_ul_range *range = container_of(kobj,
80 struct damon_sysfs_ul_range, kobj);
81 unsigned long max;
82 int err;
83
84 err = kstrtoul(buf, 0, &max);
85 if (err)
86 return -EINVAL;
87
88 range->max = max;
89 return count;
90}
91
92static void damon_sysfs_ul_range_release(struct kobject *kobj)
93{
94 kfree(container_of(kobj, struct damon_sysfs_ul_range, kobj));
95}
96
97static struct kobj_attribute damon_sysfs_ul_range_min_attr =
98 __ATTR_RW_MODE(min, 0600);
99
100static struct kobj_attribute damon_sysfs_ul_range_max_attr =
101 __ATTR_RW_MODE(max, 0600);
102
103static struct attribute *damon_sysfs_ul_range_attrs[] = {
104 &damon_sysfs_ul_range_min_attr.attr,
105 &damon_sysfs_ul_range_max_attr.attr,
106 NULL,
107};
108ATTRIBUTE_GROUPS(damon_sysfs_ul_range);
109
110static struct kobj_type damon_sysfs_ul_range_ktype = {
111 .release = damon_sysfs_ul_range_release,
112 .sysfs_ops = &kobj_sysfs_ops,
113 .default_groups = damon_sysfs_ul_range_groups,
114};
115
116/*
SeongJae Park0ac32b82022-03-22 14:49:49 -0700117 * schemes/stats directory
118 */
119
120struct damon_sysfs_stats {
121 struct kobject kobj;
122 unsigned long nr_tried;
123 unsigned long sz_tried;
124 unsigned long nr_applied;
125 unsigned long sz_applied;
126 unsigned long qt_exceeds;
127};
128
129static struct damon_sysfs_stats *damon_sysfs_stats_alloc(void)
130{
131 return kzalloc(sizeof(struct damon_sysfs_stats), GFP_KERNEL);
132}
133
134static ssize_t nr_tried_show(struct kobject *kobj, struct kobj_attribute *attr,
135 char *buf)
136{
137 struct damon_sysfs_stats *stats = container_of(kobj,
138 struct damon_sysfs_stats, kobj);
139
140 return sysfs_emit(buf, "%lu\n", stats->nr_tried);
141}
142
143static ssize_t sz_tried_show(struct kobject *kobj, struct kobj_attribute *attr,
144 char *buf)
145{
146 struct damon_sysfs_stats *stats = container_of(kobj,
147 struct damon_sysfs_stats, kobj);
148
149 return sysfs_emit(buf, "%lu\n", stats->sz_tried);
150}
151
152static ssize_t nr_applied_show(struct kobject *kobj,
153 struct kobj_attribute *attr, char *buf)
154{
155 struct damon_sysfs_stats *stats = container_of(kobj,
156 struct damon_sysfs_stats, kobj);
157
158 return sysfs_emit(buf, "%lu\n", stats->nr_applied);
159}
160
161static ssize_t sz_applied_show(struct kobject *kobj,
162 struct kobj_attribute *attr, char *buf)
163{
164 struct damon_sysfs_stats *stats = container_of(kobj,
165 struct damon_sysfs_stats, kobj);
166
167 return sysfs_emit(buf, "%lu\n", stats->sz_applied);
168}
169
170static ssize_t qt_exceeds_show(struct kobject *kobj,
171 struct kobj_attribute *attr, char *buf)
172{
173 struct damon_sysfs_stats *stats = container_of(kobj,
174 struct damon_sysfs_stats, kobj);
175
176 return sysfs_emit(buf, "%lu\n", stats->qt_exceeds);
177}
178
179static void damon_sysfs_stats_release(struct kobject *kobj)
180{
181 kfree(container_of(kobj, struct damon_sysfs_stats, kobj));
182}
183
184static struct kobj_attribute damon_sysfs_stats_nr_tried_attr =
185 __ATTR_RO_MODE(nr_tried, 0400);
186
187static struct kobj_attribute damon_sysfs_stats_sz_tried_attr =
188 __ATTR_RO_MODE(sz_tried, 0400);
189
190static struct kobj_attribute damon_sysfs_stats_nr_applied_attr =
191 __ATTR_RO_MODE(nr_applied, 0400);
192
193static struct kobj_attribute damon_sysfs_stats_sz_applied_attr =
194 __ATTR_RO_MODE(sz_applied, 0400);
195
196static struct kobj_attribute damon_sysfs_stats_qt_exceeds_attr =
197 __ATTR_RO_MODE(qt_exceeds, 0400);
198
199static struct attribute *damon_sysfs_stats_attrs[] = {
200 &damon_sysfs_stats_nr_tried_attr.attr,
201 &damon_sysfs_stats_sz_tried_attr.attr,
202 &damon_sysfs_stats_nr_applied_attr.attr,
203 &damon_sysfs_stats_sz_applied_attr.attr,
204 &damon_sysfs_stats_qt_exceeds_attr.attr,
205 NULL,
206};
207ATTRIBUTE_GROUPS(damon_sysfs_stats);
208
209static struct kobj_type damon_sysfs_stats_ktype = {
210 .release = damon_sysfs_stats_release,
211 .sysfs_ops = &kobj_sysfs_ops,
212 .default_groups = damon_sysfs_stats_groups,
213};
214
215/*
SeongJae Park1b322342022-03-22 14:49:46 -0700216 * watermarks directory
217 */
218
219struct damon_sysfs_watermarks {
220 struct kobject kobj;
221 enum damos_wmark_metric metric;
222 unsigned long interval_us;
223 unsigned long high;
224 unsigned long mid;
225 unsigned long low;
226};
227
228static struct damon_sysfs_watermarks *damon_sysfs_watermarks_alloc(
229 enum damos_wmark_metric metric, unsigned long interval_us,
230 unsigned long high, unsigned long mid, unsigned long low)
231{
232 struct damon_sysfs_watermarks *watermarks = kmalloc(
233 sizeof(*watermarks), GFP_KERNEL);
234
235 if (!watermarks)
236 return NULL;
237 watermarks->kobj = (struct kobject){};
238 watermarks->metric = metric;
239 watermarks->interval_us = interval_us;
240 watermarks->high = high;
241 watermarks->mid = mid;
242 watermarks->low = low;
243 return watermarks;
244}
245
246/* Should match with enum damos_wmark_metric */
247static const char * const damon_sysfs_wmark_metric_strs[] = {
248 "none",
249 "free_mem_rate",
250};
251
252static ssize_t metric_show(struct kobject *kobj, struct kobj_attribute *attr,
253 char *buf)
254{
255 struct damon_sysfs_watermarks *watermarks = container_of(kobj,
256 struct damon_sysfs_watermarks, kobj);
257
258 return sysfs_emit(buf, "%s\n",
259 damon_sysfs_wmark_metric_strs[watermarks->metric]);
260}
261
262static ssize_t metric_store(struct kobject *kobj, struct kobj_attribute *attr,
263 const char *buf, size_t count)
264{
265 struct damon_sysfs_watermarks *watermarks = container_of(kobj,
266 struct damon_sysfs_watermarks, kobj);
267 enum damos_wmark_metric metric;
268
269 for (metric = 0; metric < NR_DAMOS_WMARK_METRICS; metric++) {
270 if (sysfs_streq(buf, damon_sysfs_wmark_metric_strs[metric])) {
271 watermarks->metric = metric;
272 return count;
273 }
274 }
275 return -EINVAL;
276}
277
278static ssize_t interval_us_show(struct kobject *kobj,
279 struct kobj_attribute *attr, char *buf)
280{
281 struct damon_sysfs_watermarks *watermarks = container_of(kobj,
282 struct damon_sysfs_watermarks, kobj);
283
284 return sysfs_emit(buf, "%lu\n", watermarks->interval_us);
285}
286
287static ssize_t interval_us_store(struct kobject *kobj,
288 struct kobj_attribute *attr, const char *buf, size_t count)
289{
290 struct damon_sysfs_watermarks *watermarks = container_of(kobj,
291 struct damon_sysfs_watermarks, kobj);
292 int err = kstrtoul(buf, 0, &watermarks->interval_us);
293
294 if (err)
295 return -EINVAL;
296 return count;
297}
298
299static ssize_t high_show(struct kobject *kobj,
300 struct kobj_attribute *attr, char *buf)
301{
302 struct damon_sysfs_watermarks *watermarks = container_of(kobj,
303 struct damon_sysfs_watermarks, kobj);
304
305 return sysfs_emit(buf, "%lu\n", watermarks->high);
306}
307
308static ssize_t high_store(struct kobject *kobj,
309 struct kobj_attribute *attr, const char *buf, size_t count)
310{
311 struct damon_sysfs_watermarks *watermarks = container_of(kobj,
312 struct damon_sysfs_watermarks, kobj);
313 int err = kstrtoul(buf, 0, &watermarks->high);
314
315 if (err)
316 return -EINVAL;
317 return count;
318}
319
320static ssize_t mid_show(struct kobject *kobj,
321 struct kobj_attribute *attr, char *buf)
322{
323 struct damon_sysfs_watermarks *watermarks = container_of(kobj,
324 struct damon_sysfs_watermarks, kobj);
325
326 return sysfs_emit(buf, "%lu\n", watermarks->mid);
327}
328
329static ssize_t mid_store(struct kobject *kobj,
330 struct kobj_attribute *attr, const char *buf, size_t count)
331{
332 struct damon_sysfs_watermarks *watermarks = container_of(kobj,
333 struct damon_sysfs_watermarks, kobj);
334 int err = kstrtoul(buf, 0, &watermarks->mid);
335
336 if (err)
337 return -EINVAL;
338 return count;
339}
340
341static ssize_t low_show(struct kobject *kobj,
342 struct kobj_attribute *attr, char *buf)
343{
344 struct damon_sysfs_watermarks *watermarks = container_of(kobj,
345 struct damon_sysfs_watermarks, kobj);
346
347 return sysfs_emit(buf, "%lu\n", watermarks->low);
348}
349
350static ssize_t low_store(struct kobject *kobj,
351 struct kobj_attribute *attr, const char *buf, size_t count)
352{
353 struct damon_sysfs_watermarks *watermarks = container_of(kobj,
354 struct damon_sysfs_watermarks, kobj);
355 int err = kstrtoul(buf, 0, &watermarks->low);
356
357 if (err)
358 return -EINVAL;
359 return count;
360}
361
362static void damon_sysfs_watermarks_release(struct kobject *kobj)
363{
364 kfree(container_of(kobj, struct damon_sysfs_watermarks, kobj));
365}
366
367static struct kobj_attribute damon_sysfs_watermarks_metric_attr =
368 __ATTR_RW_MODE(metric, 0600);
369
370static struct kobj_attribute damon_sysfs_watermarks_interval_us_attr =
371 __ATTR_RW_MODE(interval_us, 0600);
372
373static struct kobj_attribute damon_sysfs_watermarks_high_attr =
374 __ATTR_RW_MODE(high, 0600);
375
376static struct kobj_attribute damon_sysfs_watermarks_mid_attr =
377 __ATTR_RW_MODE(mid, 0600);
378
379static struct kobj_attribute damon_sysfs_watermarks_low_attr =
380 __ATTR_RW_MODE(low, 0600);
381
382static struct attribute *damon_sysfs_watermarks_attrs[] = {
383 &damon_sysfs_watermarks_metric_attr.attr,
384 &damon_sysfs_watermarks_interval_us_attr.attr,
385 &damon_sysfs_watermarks_high_attr.attr,
386 &damon_sysfs_watermarks_mid_attr.attr,
387 &damon_sysfs_watermarks_low_attr.attr,
388 NULL,
389};
390ATTRIBUTE_GROUPS(damon_sysfs_watermarks);
391
392static struct kobj_type damon_sysfs_watermarks_ktype = {
393 .release = damon_sysfs_watermarks_release,
394 .sysfs_ops = &kobj_sysfs_ops,
395 .default_groups = damon_sysfs_watermarks_groups,
396};
397
398/*
SeongJae Park1c78b2b2022-03-22 14:49:43 -0700399 * scheme/weights directory
400 */
401
402struct damon_sysfs_weights {
403 struct kobject kobj;
404 unsigned int sz;
405 unsigned int nr_accesses;
406 unsigned int age;
407};
408
409static struct damon_sysfs_weights *damon_sysfs_weights_alloc(unsigned int sz,
410 unsigned int nr_accesses, unsigned int age)
411{
412 struct damon_sysfs_weights *weights = kmalloc(sizeof(*weights),
413 GFP_KERNEL);
414
415 if (!weights)
416 return NULL;
417 weights->kobj = (struct kobject){};
418 weights->sz = sz;
419 weights->nr_accesses = nr_accesses;
420 weights->age = age;
421 return weights;
422}
423
424static ssize_t sz_permil_show(struct kobject *kobj,
425 struct kobj_attribute *attr, char *buf)
426{
427 struct damon_sysfs_weights *weights = container_of(kobj,
428 struct damon_sysfs_weights, kobj);
429
430 return sysfs_emit(buf, "%u\n", weights->sz);
431}
432
433static ssize_t sz_permil_store(struct kobject *kobj,
434 struct kobj_attribute *attr, const char *buf, size_t count)
435{
436 struct damon_sysfs_weights *weights = container_of(kobj,
437 struct damon_sysfs_weights, kobj);
438 int err = kstrtouint(buf, 0, &weights->sz);
439
440 if (err)
441 return -EINVAL;
442 return count;
443}
444
445static ssize_t nr_accesses_permil_show(struct kobject *kobj,
446 struct kobj_attribute *attr, char *buf)
447{
448 struct damon_sysfs_weights *weights = container_of(kobj,
449 struct damon_sysfs_weights, kobj);
450
451 return sysfs_emit(buf, "%u\n", weights->nr_accesses);
452}
453
454static ssize_t nr_accesses_permil_store(struct kobject *kobj,
455 struct kobj_attribute *attr, const char *buf, size_t count)
456{
457 struct damon_sysfs_weights *weights = container_of(kobj,
458 struct damon_sysfs_weights, kobj);
459 int err = kstrtouint(buf, 0, &weights->nr_accesses);
460
461 if (err)
462 return -EINVAL;
463 return count;
464}
465
466static ssize_t age_permil_show(struct kobject *kobj,
467 struct kobj_attribute *attr, char *buf)
468{
469 struct damon_sysfs_weights *weights = container_of(kobj,
470 struct damon_sysfs_weights, kobj);
471
472 return sysfs_emit(buf, "%u\n", weights->age);
473}
474
475static ssize_t age_permil_store(struct kobject *kobj,
476 struct kobj_attribute *attr, const char *buf, size_t count)
477{
478 struct damon_sysfs_weights *weights = container_of(kobj,
479 struct damon_sysfs_weights, kobj);
480 int err = kstrtouint(buf, 0, &weights->age);
481
482 if (err)
483 return -EINVAL;
484 return count;
485}
486
487static void damon_sysfs_weights_release(struct kobject *kobj)
488{
489 kfree(container_of(kobj, struct damon_sysfs_weights, kobj));
490}
491
492static struct kobj_attribute damon_sysfs_weights_sz_attr =
493 __ATTR_RW_MODE(sz_permil, 0600);
494
495static struct kobj_attribute damon_sysfs_weights_nr_accesses_attr =
496 __ATTR_RW_MODE(nr_accesses_permil, 0600);
497
498static struct kobj_attribute damon_sysfs_weights_age_attr =
499 __ATTR_RW_MODE(age_permil, 0600);
500
501static struct attribute *damon_sysfs_weights_attrs[] = {
502 &damon_sysfs_weights_sz_attr.attr,
503 &damon_sysfs_weights_nr_accesses_attr.attr,
504 &damon_sysfs_weights_age_attr.attr,
505 NULL,
506};
507ATTRIBUTE_GROUPS(damon_sysfs_weights);
508
509static struct kobj_type damon_sysfs_weights_ktype = {
510 .release = damon_sysfs_weights_release,
511 .sysfs_ops = &kobj_sysfs_ops,
512 .default_groups = damon_sysfs_weights_groups,
513};
514
515/*
SeongJae Park9bbb8202022-03-22 14:49:40 -0700516 * quotas directory
517 */
518
519struct damon_sysfs_quotas {
520 struct kobject kobj;
SeongJae Park1c78b2b2022-03-22 14:49:43 -0700521 struct damon_sysfs_weights *weights;
SeongJae Park9bbb8202022-03-22 14:49:40 -0700522 unsigned long ms;
523 unsigned long sz;
524 unsigned long reset_interval_ms;
525};
526
527static struct damon_sysfs_quotas *damon_sysfs_quotas_alloc(void)
528{
529 return kzalloc(sizeof(struct damon_sysfs_quotas), GFP_KERNEL);
530}
531
SeongJae Park1c78b2b2022-03-22 14:49:43 -0700532static int damon_sysfs_quotas_add_dirs(struct damon_sysfs_quotas *quotas)
533{
534 struct damon_sysfs_weights *weights;
535 int err;
536
537 weights = damon_sysfs_weights_alloc(0, 0, 0);
538 if (!weights)
539 return -ENOMEM;
540
541 err = kobject_init_and_add(&weights->kobj, &damon_sysfs_weights_ktype,
542 &quotas->kobj, "weights");
543 if (err)
544 kobject_put(&weights->kobj);
545 else
546 quotas->weights = weights;
547 return err;
548}
549
550static void damon_sysfs_quotas_rm_dirs(struct damon_sysfs_quotas *quotas)
551{
552 kobject_put(&quotas->weights->kobj);
553}
554
SeongJae Park9bbb8202022-03-22 14:49:40 -0700555static ssize_t ms_show(struct kobject *kobj, struct kobj_attribute *attr,
556 char *buf)
557{
558 struct damon_sysfs_quotas *quotas = container_of(kobj,
559 struct damon_sysfs_quotas, kobj);
560
561 return sysfs_emit(buf, "%lu\n", quotas->ms);
562}
563
564static ssize_t ms_store(struct kobject *kobj, struct kobj_attribute *attr,
565 const char *buf, size_t count)
566{
567 struct damon_sysfs_quotas *quotas = container_of(kobj,
568 struct damon_sysfs_quotas, kobj);
569 int err = kstrtoul(buf, 0, &quotas->ms);
570
571 if (err)
572 return -EINVAL;
573 return count;
574}
575
576static ssize_t bytes_show(struct kobject *kobj, struct kobj_attribute *attr,
577 char *buf)
578{
579 struct damon_sysfs_quotas *quotas = container_of(kobj,
580 struct damon_sysfs_quotas, kobj);
581
582 return sysfs_emit(buf, "%lu\n", quotas->sz);
583}
584
585static ssize_t bytes_store(struct kobject *kobj,
586 struct kobj_attribute *attr, const char *buf, size_t count)
587{
588 struct damon_sysfs_quotas *quotas = container_of(kobj,
589 struct damon_sysfs_quotas, kobj);
590 int err = kstrtoul(buf, 0, &quotas->sz);
591
592 if (err)
593 return -EINVAL;
594 return count;
595}
596
597static ssize_t reset_interval_ms_show(struct kobject *kobj,
598 struct kobj_attribute *attr, char *buf)
599{
600 struct damon_sysfs_quotas *quotas = container_of(kobj,
601 struct damon_sysfs_quotas, kobj);
602
603 return sysfs_emit(buf, "%lu\n", quotas->reset_interval_ms);
604}
605
606static ssize_t reset_interval_ms_store(struct kobject *kobj,
607 struct kobj_attribute *attr, const char *buf, size_t count)
608{
609 struct damon_sysfs_quotas *quotas = container_of(kobj,
610 struct damon_sysfs_quotas, kobj);
611 int err = kstrtoul(buf, 0, &quotas->reset_interval_ms);
612
613 if (err)
614 return -EINVAL;
615 return count;
616}
617
618static void damon_sysfs_quotas_release(struct kobject *kobj)
619{
620 kfree(container_of(kobj, struct damon_sysfs_quotas, kobj));
621}
622
623static struct kobj_attribute damon_sysfs_quotas_ms_attr =
624 __ATTR_RW_MODE(ms, 0600);
625
626static struct kobj_attribute damon_sysfs_quotas_sz_attr =
627 __ATTR_RW_MODE(bytes, 0600);
628
629static struct kobj_attribute damon_sysfs_quotas_reset_interval_ms_attr =
630 __ATTR_RW_MODE(reset_interval_ms, 0600);
631
632static struct attribute *damon_sysfs_quotas_attrs[] = {
633 &damon_sysfs_quotas_ms_attr.attr,
634 &damon_sysfs_quotas_sz_attr.attr,
635 &damon_sysfs_quotas_reset_interval_ms_attr.attr,
636 NULL,
637};
638ATTRIBUTE_GROUPS(damon_sysfs_quotas);
639
640static struct kobj_type damon_sysfs_quotas_ktype = {
641 .release = damon_sysfs_quotas_release,
642 .sysfs_ops = &kobj_sysfs_ops,
643 .default_groups = damon_sysfs_quotas_groups,
644};
645
646/*
SeongJae Park7e84b1f2022-03-22 14:49:37 -0700647 * access_pattern directory
648 */
649
650struct damon_sysfs_access_pattern {
651 struct kobject kobj;
652 struct damon_sysfs_ul_range *sz;
653 struct damon_sysfs_ul_range *nr_accesses;
654 struct damon_sysfs_ul_range *age;
655};
656
657static
658struct damon_sysfs_access_pattern *damon_sysfs_access_pattern_alloc(void)
659{
660 struct damon_sysfs_access_pattern *access_pattern =
661 kmalloc(sizeof(*access_pattern), GFP_KERNEL);
662
663 if (!access_pattern)
664 return NULL;
665 access_pattern->kobj = (struct kobject){};
666 return access_pattern;
667}
668
669static int damon_sysfs_access_pattern_add_range_dir(
670 struct damon_sysfs_access_pattern *access_pattern,
671 struct damon_sysfs_ul_range **range_dir_ptr,
672 char *name)
673{
674 struct damon_sysfs_ul_range *range = damon_sysfs_ul_range_alloc(0, 0);
675 int err;
676
677 if (!range)
678 return -ENOMEM;
679 err = kobject_init_and_add(&range->kobj, &damon_sysfs_ul_range_ktype,
680 &access_pattern->kobj, name);
681 if (err)
682 kobject_put(&range->kobj);
683 else
684 *range_dir_ptr = range;
685 return err;
686}
687
688static int damon_sysfs_access_pattern_add_dirs(
689 struct damon_sysfs_access_pattern *access_pattern)
690{
691 int err;
692
693 err = damon_sysfs_access_pattern_add_range_dir(access_pattern,
694 &access_pattern->sz, "sz");
695 if (err)
696 goto put_sz_out;
697
698 err = damon_sysfs_access_pattern_add_range_dir(access_pattern,
699 &access_pattern->nr_accesses, "nr_accesses");
700 if (err)
701 goto put_nr_accesses_sz_out;
702
703 err = damon_sysfs_access_pattern_add_range_dir(access_pattern,
704 &access_pattern->age, "age");
705 if (err)
706 goto put_age_nr_accesses_sz_out;
707 return 0;
708
709put_age_nr_accesses_sz_out:
710 kobject_put(&access_pattern->age->kobj);
711 access_pattern->age = NULL;
712put_nr_accesses_sz_out:
713 kobject_put(&access_pattern->nr_accesses->kobj);
714 access_pattern->nr_accesses = NULL;
715put_sz_out:
716 kobject_put(&access_pattern->sz->kobj);
717 access_pattern->sz = NULL;
718 return err;
719}
720
721static void damon_sysfs_access_pattern_rm_dirs(
722 struct damon_sysfs_access_pattern *access_pattern)
723{
724 kobject_put(&access_pattern->sz->kobj);
725 kobject_put(&access_pattern->nr_accesses->kobj);
726 kobject_put(&access_pattern->age->kobj);
727}
728
729static void damon_sysfs_access_pattern_release(struct kobject *kobj)
730{
731 kfree(container_of(kobj, struct damon_sysfs_access_pattern, kobj));
732}
733
734static struct attribute *damon_sysfs_access_pattern_attrs[] = {
735 NULL,
736};
737ATTRIBUTE_GROUPS(damon_sysfs_access_pattern);
738
739static struct kobj_type damon_sysfs_access_pattern_ktype = {
740 .release = damon_sysfs_access_pattern_release,
741 .sysfs_ops = &kobj_sysfs_ops,
742 .default_groups = damon_sysfs_access_pattern_groups,
743};
744
745/*
746 * scheme directory
747 */
748
749struct damon_sysfs_scheme {
750 struct kobject kobj;
751 enum damos_action action;
752 struct damon_sysfs_access_pattern *access_pattern;
SeongJae Park9bbb8202022-03-22 14:49:40 -0700753 struct damon_sysfs_quotas *quotas;
SeongJae Park1b322342022-03-22 14:49:46 -0700754 struct damon_sysfs_watermarks *watermarks;
SeongJae Park0ac32b82022-03-22 14:49:49 -0700755 struct damon_sysfs_stats *stats;
SeongJae Park7e84b1f2022-03-22 14:49:37 -0700756};
757
758/* This should match with enum damos_action */
759static const char * const damon_sysfs_damos_action_strs[] = {
760 "willneed",
761 "cold",
762 "pageout",
763 "hugepage",
764 "nohugepage",
765 "stat",
766};
767
768static struct damon_sysfs_scheme *damon_sysfs_scheme_alloc(
769 enum damos_action action)
770{
771 struct damon_sysfs_scheme *scheme = kmalloc(sizeof(*scheme),
772 GFP_KERNEL);
773
774 if (!scheme)
775 return NULL;
776 scheme->kobj = (struct kobject){};
777 scheme->action = action;
778 return scheme;
779}
780
781static int damon_sysfs_scheme_set_access_pattern(
782 struct damon_sysfs_scheme *scheme)
783{
784 struct damon_sysfs_access_pattern *access_pattern;
785 int err;
786
787 access_pattern = damon_sysfs_access_pattern_alloc();
788 if (!access_pattern)
789 return -ENOMEM;
790 err = kobject_init_and_add(&access_pattern->kobj,
791 &damon_sysfs_access_pattern_ktype, &scheme->kobj,
792 "access_pattern");
793 if (err)
794 goto out;
795 err = damon_sysfs_access_pattern_add_dirs(access_pattern);
796 if (err)
797 goto out;
798 scheme->access_pattern = access_pattern;
799 return 0;
800
801out:
802 kobject_put(&access_pattern->kobj);
803 return err;
804}
805
SeongJae Park9bbb8202022-03-22 14:49:40 -0700806static int damon_sysfs_scheme_set_quotas(struct damon_sysfs_scheme *scheme)
807{
808 struct damon_sysfs_quotas *quotas = damon_sysfs_quotas_alloc();
809 int err;
810
811 if (!quotas)
812 return -ENOMEM;
813 err = kobject_init_and_add(&quotas->kobj, &damon_sysfs_quotas_ktype,
814 &scheme->kobj, "quotas");
815 if (err)
816 goto out;
SeongJae Park1c78b2b2022-03-22 14:49:43 -0700817 err = damon_sysfs_quotas_add_dirs(quotas);
818 if (err)
819 goto out;
SeongJae Park9bbb8202022-03-22 14:49:40 -0700820 scheme->quotas = quotas;
821 return 0;
822
823out:
824 kobject_put(&quotas->kobj);
825 return err;
826}
827
SeongJae Park1b322342022-03-22 14:49:46 -0700828static int damon_sysfs_scheme_set_watermarks(struct damon_sysfs_scheme *scheme)
829{
830 struct damon_sysfs_watermarks *watermarks =
831 damon_sysfs_watermarks_alloc(DAMOS_WMARK_NONE, 0, 0, 0, 0);
832 int err;
833
834 if (!watermarks)
835 return -ENOMEM;
836 err = kobject_init_and_add(&watermarks->kobj,
837 &damon_sysfs_watermarks_ktype, &scheme->kobj,
838 "watermarks");
839 if (err)
840 kobject_put(&watermarks->kobj);
841 else
842 scheme->watermarks = watermarks;
843 return err;
844}
845
SeongJae Park0ac32b82022-03-22 14:49:49 -0700846static int damon_sysfs_scheme_set_stats(struct damon_sysfs_scheme *scheme)
847{
848 struct damon_sysfs_stats *stats = damon_sysfs_stats_alloc();
849 int err;
850
851 if (!stats)
852 return -ENOMEM;
853 err = kobject_init_and_add(&stats->kobj, &damon_sysfs_stats_ktype,
854 &scheme->kobj, "stats");
855 if (err)
856 kobject_put(&stats->kobj);
857 else
858 scheme->stats = stats;
859 return err;
860}
861
SeongJae Park7e84b1f2022-03-22 14:49:37 -0700862static int damon_sysfs_scheme_add_dirs(struct damon_sysfs_scheme *scheme)
863{
864 int err;
865
866 err = damon_sysfs_scheme_set_access_pattern(scheme);
867 if (err)
868 return err;
SeongJae Park9bbb8202022-03-22 14:49:40 -0700869 err = damon_sysfs_scheme_set_quotas(scheme);
870 if (err)
871 goto put_access_pattern_out;
SeongJae Park1b322342022-03-22 14:49:46 -0700872 err = damon_sysfs_scheme_set_watermarks(scheme);
873 if (err)
874 goto put_quotas_access_pattern_out;
SeongJae Park0ac32b82022-03-22 14:49:49 -0700875 err = damon_sysfs_scheme_set_stats(scheme);
876 if (err)
877 goto put_watermarks_quotas_access_pattern_out;
SeongJae Park7e84b1f2022-03-22 14:49:37 -0700878 return 0;
SeongJae Park9bbb8202022-03-22 14:49:40 -0700879
SeongJae Park0ac32b82022-03-22 14:49:49 -0700880put_watermarks_quotas_access_pattern_out:
881 kobject_put(&scheme->watermarks->kobj);
882 scheme->watermarks = NULL;
SeongJae Park1b322342022-03-22 14:49:46 -0700883put_quotas_access_pattern_out:
884 kobject_put(&scheme->quotas->kobj);
885 scheme->quotas = NULL;
SeongJae Park9bbb8202022-03-22 14:49:40 -0700886put_access_pattern_out:
887 kobject_put(&scheme->access_pattern->kobj);
888 scheme->access_pattern = NULL;
889 return err;
SeongJae Park7e84b1f2022-03-22 14:49:37 -0700890}
891
892static void damon_sysfs_scheme_rm_dirs(struct damon_sysfs_scheme *scheme)
893{
894 damon_sysfs_access_pattern_rm_dirs(scheme->access_pattern);
895 kobject_put(&scheme->access_pattern->kobj);
SeongJae Park1c78b2b2022-03-22 14:49:43 -0700896 damon_sysfs_quotas_rm_dirs(scheme->quotas);
SeongJae Park9bbb8202022-03-22 14:49:40 -0700897 kobject_put(&scheme->quotas->kobj);
SeongJae Park1b322342022-03-22 14:49:46 -0700898 kobject_put(&scheme->watermarks->kobj);
SeongJae Park0ac32b82022-03-22 14:49:49 -0700899 kobject_put(&scheme->stats->kobj);
SeongJae Park7e84b1f2022-03-22 14:49:37 -0700900}
901
902static ssize_t action_show(struct kobject *kobj, struct kobj_attribute *attr,
903 char *buf)
904{
905 struct damon_sysfs_scheme *scheme = container_of(kobj,
906 struct damon_sysfs_scheme, kobj);
907
908 return sysfs_emit(buf, "%s\n",
909 damon_sysfs_damos_action_strs[scheme->action]);
910}
911
912static ssize_t action_store(struct kobject *kobj, struct kobj_attribute *attr,
913 const char *buf, size_t count)
914{
915 struct damon_sysfs_scheme *scheme = container_of(kobj,
916 struct damon_sysfs_scheme, kobj);
917 enum damos_action action;
918
919 for (action = 0; action < NR_DAMOS_ACTIONS; action++) {
920 if (sysfs_streq(buf, damon_sysfs_damos_action_strs[action])) {
921 scheme->action = action;
922 return count;
923 }
924 }
925 return -EINVAL;
926}
927
928static void damon_sysfs_scheme_release(struct kobject *kobj)
929{
930 kfree(container_of(kobj, struct damon_sysfs_scheme, kobj));
931}
932
933static struct kobj_attribute damon_sysfs_scheme_action_attr =
934 __ATTR_RW_MODE(action, 0600);
935
936static struct attribute *damon_sysfs_scheme_attrs[] = {
937 &damon_sysfs_scheme_action_attr.attr,
938 NULL,
939};
940ATTRIBUTE_GROUPS(damon_sysfs_scheme);
941
942static struct kobj_type damon_sysfs_scheme_ktype = {
943 .release = damon_sysfs_scheme_release,
944 .sysfs_ops = &kobj_sysfs_ops,
945 .default_groups = damon_sysfs_scheme_groups,
946};
947
948/*
949 * schemes directory
950 */
951
952struct damon_sysfs_schemes {
953 struct kobject kobj;
954 struct damon_sysfs_scheme **schemes_arr;
955 int nr;
956};
957
958static struct damon_sysfs_schemes *damon_sysfs_schemes_alloc(void)
959{
960 return kzalloc(sizeof(struct damon_sysfs_schemes), GFP_KERNEL);
961}
962
963static void damon_sysfs_schemes_rm_dirs(struct damon_sysfs_schemes *schemes)
964{
965 struct damon_sysfs_scheme **schemes_arr = schemes->schemes_arr;
966 int i;
967
968 for (i = 0; i < schemes->nr; i++) {
969 damon_sysfs_scheme_rm_dirs(schemes_arr[i]);
970 kobject_put(&schemes_arr[i]->kobj);
971 }
972 schemes->nr = 0;
973 kfree(schemes_arr);
974 schemes->schemes_arr = NULL;
975}
976
977static int damon_sysfs_schemes_add_dirs(struct damon_sysfs_schemes *schemes,
978 int nr_schemes)
979{
980 struct damon_sysfs_scheme **schemes_arr, *scheme;
981 int err, i;
982
983 damon_sysfs_schemes_rm_dirs(schemes);
984 if (!nr_schemes)
985 return 0;
986
987 schemes_arr = kmalloc_array(nr_schemes, sizeof(*schemes_arr),
988 GFP_KERNEL | __GFP_NOWARN);
989 if (!schemes_arr)
990 return -ENOMEM;
991 schemes->schemes_arr = schemes_arr;
992
993 for (i = 0; i < nr_schemes; i++) {
994 scheme = damon_sysfs_scheme_alloc(DAMOS_STAT);
995 if (!scheme) {
996 damon_sysfs_schemes_rm_dirs(schemes);
997 return -ENOMEM;
998 }
999
1000 err = kobject_init_and_add(&scheme->kobj,
1001 &damon_sysfs_scheme_ktype, &schemes->kobj,
1002 "%d", i);
1003 if (err)
1004 goto out;
1005 err = damon_sysfs_scheme_add_dirs(scheme);
1006 if (err)
1007 goto out;
1008
1009 schemes_arr[i] = scheme;
1010 schemes->nr++;
1011 }
1012 return 0;
1013
1014out:
1015 damon_sysfs_schemes_rm_dirs(schemes);
1016 kobject_put(&scheme->kobj);
1017 return err;
1018}
1019
1020static ssize_t nr_schemes_show(struct kobject *kobj,
1021 struct kobj_attribute *attr, char *buf)
1022{
1023 struct damon_sysfs_schemes *schemes = container_of(kobj,
1024 struct damon_sysfs_schemes, kobj);
1025
1026 return sysfs_emit(buf, "%d\n", schemes->nr);
1027}
1028
1029static ssize_t nr_schemes_store(struct kobject *kobj,
1030 struct kobj_attribute *attr, const char *buf, size_t count)
1031{
1032 struct damon_sysfs_schemes *schemes = container_of(kobj,
1033 struct damon_sysfs_schemes, kobj);
1034 int nr, err = kstrtoint(buf, 0, &nr);
1035
1036 if (err)
1037 return err;
1038 if (nr < 0)
1039 return -EINVAL;
1040
1041 if (!mutex_trylock(&damon_sysfs_lock))
1042 return -EBUSY;
1043 err = damon_sysfs_schemes_add_dirs(schemes, nr);
1044 mutex_unlock(&damon_sysfs_lock);
1045 if (err)
1046 return err;
1047 return count;
1048}
1049
1050static void damon_sysfs_schemes_release(struct kobject *kobj)
1051{
1052 kfree(container_of(kobj, struct damon_sysfs_schemes, kobj));
1053}
1054
1055static struct kobj_attribute damon_sysfs_schemes_nr_attr =
1056 __ATTR_RW_MODE(nr_schemes, 0600);
1057
1058static struct attribute *damon_sysfs_schemes_attrs[] = {
1059 &damon_sysfs_schemes_nr_attr.attr,
1060 NULL,
1061};
1062ATTRIBUTE_GROUPS(damon_sysfs_schemes);
1063
1064static struct kobj_type damon_sysfs_schemes_ktype = {
1065 .release = damon_sysfs_schemes_release,
1066 .sysfs_ops = &kobj_sysfs_ops,
1067 .default_groups = damon_sysfs_schemes_groups,
1068};
1069
1070/*
SeongJae Park2031b142022-03-22 14:49:34 -07001071 * init region directory
1072 */
1073
1074struct damon_sysfs_region {
1075 struct kobject kobj;
1076 unsigned long start;
1077 unsigned long end;
1078};
1079
1080static struct damon_sysfs_region *damon_sysfs_region_alloc(
1081 unsigned long start,
1082 unsigned long end)
1083{
1084 struct damon_sysfs_region *region = kmalloc(sizeof(*region),
1085 GFP_KERNEL);
1086
1087 if (!region)
1088 return NULL;
1089 region->kobj = (struct kobject){};
1090 region->start = start;
1091 region->end = end;
1092 return region;
1093}
1094
1095static ssize_t start_show(struct kobject *kobj, struct kobj_attribute *attr,
1096 char *buf)
1097{
1098 struct damon_sysfs_region *region = container_of(kobj,
1099 struct damon_sysfs_region, kobj);
1100
1101 return sysfs_emit(buf, "%lu\n", region->start);
1102}
1103
1104static ssize_t start_store(struct kobject *kobj, struct kobj_attribute *attr,
1105 const char *buf, size_t count)
1106{
1107 struct damon_sysfs_region *region = container_of(kobj,
1108 struct damon_sysfs_region, kobj);
1109 int err = kstrtoul(buf, 0, &region->start);
1110
1111 if (err)
1112 return -EINVAL;
1113 return count;
1114}
1115
1116static ssize_t end_show(struct kobject *kobj, struct kobj_attribute *attr,
1117 char *buf)
1118{
1119 struct damon_sysfs_region *region = container_of(kobj,
1120 struct damon_sysfs_region, kobj);
1121
1122 return sysfs_emit(buf, "%lu\n", region->end);
1123}
1124
1125static ssize_t end_store(struct kobject *kobj, struct kobj_attribute *attr,
1126 const char *buf, size_t count)
1127{
1128 struct damon_sysfs_region *region = container_of(kobj,
1129 struct damon_sysfs_region, kobj);
1130 int err = kstrtoul(buf, 0, &region->end);
1131
1132 if (err)
1133 return -EINVAL;
1134 return count;
1135}
1136
1137static void damon_sysfs_region_release(struct kobject *kobj)
1138{
1139 kfree(container_of(kobj, struct damon_sysfs_region, kobj));
1140}
1141
1142static struct kobj_attribute damon_sysfs_region_start_attr =
1143 __ATTR_RW_MODE(start, 0600);
1144
1145static struct kobj_attribute damon_sysfs_region_end_attr =
1146 __ATTR_RW_MODE(end, 0600);
1147
1148static struct attribute *damon_sysfs_region_attrs[] = {
1149 &damon_sysfs_region_start_attr.attr,
1150 &damon_sysfs_region_end_attr.attr,
1151 NULL,
1152};
1153ATTRIBUTE_GROUPS(damon_sysfs_region);
1154
1155static struct kobj_type damon_sysfs_region_ktype = {
1156 .release = damon_sysfs_region_release,
1157 .sysfs_ops = &kobj_sysfs_ops,
1158 .default_groups = damon_sysfs_region_groups,
1159};
1160
1161/*
1162 * init_regions directory
1163 */
1164
1165struct damon_sysfs_regions {
1166 struct kobject kobj;
1167 struct damon_sysfs_region **regions_arr;
1168 int nr;
1169};
1170
1171static struct damon_sysfs_regions *damon_sysfs_regions_alloc(void)
1172{
1173 return kzalloc(sizeof(struct damon_sysfs_regions), GFP_KERNEL);
1174}
1175
1176static void damon_sysfs_regions_rm_dirs(struct damon_sysfs_regions *regions)
1177{
1178 struct damon_sysfs_region **regions_arr = regions->regions_arr;
1179 int i;
1180
1181 for (i = 0; i < regions->nr; i++)
1182 kobject_put(&regions_arr[i]->kobj);
1183 regions->nr = 0;
1184 kfree(regions_arr);
1185 regions->regions_arr = NULL;
1186}
1187
1188static int damon_sysfs_regions_add_dirs(struct damon_sysfs_regions *regions,
1189 int nr_regions)
1190{
1191 struct damon_sysfs_region **regions_arr, *region;
1192 int err, i;
1193
1194 damon_sysfs_regions_rm_dirs(regions);
1195 if (!nr_regions)
1196 return 0;
1197
1198 regions_arr = kmalloc_array(nr_regions, sizeof(*regions_arr),
1199 GFP_KERNEL | __GFP_NOWARN);
1200 if (!regions_arr)
1201 return -ENOMEM;
1202 regions->regions_arr = regions_arr;
1203
1204 for (i = 0; i < nr_regions; i++) {
1205 region = damon_sysfs_region_alloc(0, 0);
1206 if (!region) {
1207 damon_sysfs_regions_rm_dirs(regions);
1208 return -ENOMEM;
1209 }
1210
1211 err = kobject_init_and_add(&region->kobj,
1212 &damon_sysfs_region_ktype, &regions->kobj,
1213 "%d", i);
1214 if (err) {
1215 kobject_put(&region->kobj);
1216 damon_sysfs_regions_rm_dirs(regions);
1217 return err;
1218 }
1219
1220 regions_arr[i] = region;
1221 regions->nr++;
1222 }
1223 return 0;
1224}
1225
1226static ssize_t nr_regions_show(struct kobject *kobj,
1227 struct kobj_attribute *attr, char *buf)
1228{
1229 struct damon_sysfs_regions *regions = container_of(kobj,
1230 struct damon_sysfs_regions, kobj);
1231
1232 return sysfs_emit(buf, "%d\n", regions->nr);
1233}
1234
1235static ssize_t nr_regions_store(struct kobject *kobj,
1236 struct kobj_attribute *attr, const char *buf, size_t count)
1237{
1238 struct damon_sysfs_regions *regions = container_of(kobj,
1239 struct damon_sysfs_regions, kobj);
1240 int nr, err = kstrtoint(buf, 0, &nr);
1241
1242 if (err)
1243 return err;
1244 if (nr < 0)
1245 return -EINVAL;
1246
1247 if (!mutex_trylock(&damon_sysfs_lock))
1248 return -EBUSY;
1249 err = damon_sysfs_regions_add_dirs(regions, nr);
1250 mutex_unlock(&damon_sysfs_lock);
1251 if (err)
1252 return err;
1253
1254 return count;
1255}
1256
1257static void damon_sysfs_regions_release(struct kobject *kobj)
1258{
1259 kfree(container_of(kobj, struct damon_sysfs_regions, kobj));
1260}
1261
1262static struct kobj_attribute damon_sysfs_regions_nr_attr =
1263 __ATTR_RW_MODE(nr_regions, 0600);
1264
1265static struct attribute *damon_sysfs_regions_attrs[] = {
1266 &damon_sysfs_regions_nr_attr.attr,
1267 NULL,
1268};
1269ATTRIBUTE_GROUPS(damon_sysfs_regions);
1270
1271static struct kobj_type damon_sysfs_regions_ktype = {
1272 .release = damon_sysfs_regions_release,
1273 .sysfs_ops = &kobj_sysfs_ops,
1274 .default_groups = damon_sysfs_regions_groups,
1275};
1276
1277/*
SeongJae Parkc951cd32022-03-22 14:49:27 -07001278 * target directory
1279 */
1280
1281struct damon_sysfs_target {
1282 struct kobject kobj;
SeongJae Park2031b142022-03-22 14:49:34 -07001283 struct damon_sysfs_regions *regions;
SeongJae Parkc951cd32022-03-22 14:49:27 -07001284 int pid;
1285};
1286
1287static struct damon_sysfs_target *damon_sysfs_target_alloc(void)
1288{
1289 return kzalloc(sizeof(struct damon_sysfs_target), GFP_KERNEL);
1290}
1291
SeongJae Park2031b142022-03-22 14:49:34 -07001292static int damon_sysfs_target_add_dirs(struct damon_sysfs_target *target)
1293{
1294 struct damon_sysfs_regions *regions = damon_sysfs_regions_alloc();
1295 int err;
1296
1297 if (!regions)
1298 return -ENOMEM;
1299
1300 err = kobject_init_and_add(&regions->kobj, &damon_sysfs_regions_ktype,
1301 &target->kobj, "regions");
1302 if (err)
1303 kobject_put(&regions->kobj);
1304 else
1305 target->regions = regions;
1306 return err;
1307}
1308
1309static void damon_sysfs_target_rm_dirs(struct damon_sysfs_target *target)
1310{
1311 damon_sysfs_regions_rm_dirs(target->regions);
1312 kobject_put(&target->regions->kobj);
1313}
1314
SeongJae Parkc951cd32022-03-22 14:49:27 -07001315static ssize_t pid_target_show(struct kobject *kobj,
1316 struct kobj_attribute *attr, char *buf)
1317{
1318 struct damon_sysfs_target *target = container_of(kobj,
1319 struct damon_sysfs_target, kobj);
1320
1321 return sysfs_emit(buf, "%d\n", target->pid);
1322}
1323
1324static ssize_t pid_target_store(struct kobject *kobj,
1325 struct kobj_attribute *attr, const char *buf, size_t count)
1326{
1327 struct damon_sysfs_target *target = container_of(kobj,
1328 struct damon_sysfs_target, kobj);
1329 int err = kstrtoint(buf, 0, &target->pid);
1330
1331 if (err)
1332 return -EINVAL;
1333 return count;
1334}
1335
1336static void damon_sysfs_target_release(struct kobject *kobj)
1337{
1338 kfree(container_of(kobj, struct damon_sysfs_target, kobj));
1339}
1340
1341static struct kobj_attribute damon_sysfs_target_pid_attr =
1342 __ATTR_RW_MODE(pid_target, 0600);
1343
1344static struct attribute *damon_sysfs_target_attrs[] = {
1345 &damon_sysfs_target_pid_attr.attr,
1346 NULL,
1347};
1348ATTRIBUTE_GROUPS(damon_sysfs_target);
1349
1350static struct kobj_type damon_sysfs_target_ktype = {
1351 .release = damon_sysfs_target_release,
1352 .sysfs_ops = &kobj_sysfs_ops,
1353 .default_groups = damon_sysfs_target_groups,
1354};
1355
1356/*
1357 * targets directory
1358 */
1359
1360struct damon_sysfs_targets {
1361 struct kobject kobj;
1362 struct damon_sysfs_target **targets_arr;
1363 int nr;
1364};
1365
1366static struct damon_sysfs_targets *damon_sysfs_targets_alloc(void)
1367{
1368 return kzalloc(sizeof(struct damon_sysfs_targets), GFP_KERNEL);
1369}
1370
1371static void damon_sysfs_targets_rm_dirs(struct damon_sysfs_targets *targets)
1372{
1373 struct damon_sysfs_target **targets_arr = targets->targets_arr;
1374 int i;
1375
SeongJae Park2031b142022-03-22 14:49:34 -07001376 for (i = 0; i < targets->nr; i++) {
1377 damon_sysfs_target_rm_dirs(targets_arr[i]);
SeongJae Parkc951cd32022-03-22 14:49:27 -07001378 kobject_put(&targets_arr[i]->kobj);
SeongJae Park2031b142022-03-22 14:49:34 -07001379 }
SeongJae Parkc951cd32022-03-22 14:49:27 -07001380 targets->nr = 0;
1381 kfree(targets_arr);
1382 targets->targets_arr = NULL;
1383}
1384
1385static int damon_sysfs_targets_add_dirs(struct damon_sysfs_targets *targets,
1386 int nr_targets)
1387{
1388 struct damon_sysfs_target **targets_arr, *target;
1389 int err, i;
1390
1391 damon_sysfs_targets_rm_dirs(targets);
1392 if (!nr_targets)
1393 return 0;
1394
1395 targets_arr = kmalloc_array(nr_targets, sizeof(*targets_arr),
1396 GFP_KERNEL | __GFP_NOWARN);
1397 if (!targets_arr)
1398 return -ENOMEM;
1399 targets->targets_arr = targets_arr;
1400
1401 for (i = 0; i < nr_targets; i++) {
1402 target = damon_sysfs_target_alloc();
1403 if (!target) {
1404 damon_sysfs_targets_rm_dirs(targets);
1405 return -ENOMEM;
1406 }
1407
1408 err = kobject_init_and_add(&target->kobj,
1409 &damon_sysfs_target_ktype, &targets->kobj,
1410 "%d", i);
1411 if (err)
1412 goto out;
1413
SeongJae Park2031b142022-03-22 14:49:34 -07001414 err = damon_sysfs_target_add_dirs(target);
1415 if (err)
1416 goto out;
1417
SeongJae Parkc951cd32022-03-22 14:49:27 -07001418 targets_arr[i] = target;
1419 targets->nr++;
1420 }
1421 return 0;
1422
1423out:
1424 damon_sysfs_targets_rm_dirs(targets);
1425 kobject_put(&target->kobj);
1426 return err;
1427}
1428
1429static ssize_t nr_targets_show(struct kobject *kobj,
1430 struct kobj_attribute *attr, char *buf)
1431{
1432 struct damon_sysfs_targets *targets = container_of(kobj,
1433 struct damon_sysfs_targets, kobj);
1434
1435 return sysfs_emit(buf, "%d\n", targets->nr);
1436}
1437
1438static ssize_t nr_targets_store(struct kobject *kobj,
1439 struct kobj_attribute *attr, const char *buf, size_t count)
1440{
1441 struct damon_sysfs_targets *targets = container_of(kobj,
1442 struct damon_sysfs_targets, kobj);
1443 int nr, err = kstrtoint(buf, 0, &nr);
1444
1445 if (err)
1446 return err;
1447 if (nr < 0)
1448 return -EINVAL;
1449
1450 if (!mutex_trylock(&damon_sysfs_lock))
1451 return -EBUSY;
1452 err = damon_sysfs_targets_add_dirs(targets, nr);
1453 mutex_unlock(&damon_sysfs_lock);
1454 if (err)
1455 return err;
1456
1457 return count;
1458}
1459
1460static void damon_sysfs_targets_release(struct kobject *kobj)
1461{
1462 kfree(container_of(kobj, struct damon_sysfs_targets, kobj));
1463}
1464
1465static struct kobj_attribute damon_sysfs_targets_nr_attr =
1466 __ATTR_RW_MODE(nr_targets, 0600);
1467
1468static struct attribute *damon_sysfs_targets_attrs[] = {
1469 &damon_sysfs_targets_nr_attr.attr,
1470 NULL,
1471};
1472ATTRIBUTE_GROUPS(damon_sysfs_targets);
1473
1474static struct kobj_type damon_sysfs_targets_ktype = {
1475 .release = damon_sysfs_targets_release,
1476 .sysfs_ops = &kobj_sysfs_ops,
1477 .default_groups = damon_sysfs_targets_groups,
1478};
1479
1480/*
1481 * intervals directory
1482 */
1483
1484struct damon_sysfs_intervals {
1485 struct kobject kobj;
1486 unsigned long sample_us;
1487 unsigned long aggr_us;
1488 unsigned long update_us;
1489};
1490
1491static struct damon_sysfs_intervals *damon_sysfs_intervals_alloc(
1492 unsigned long sample_us, unsigned long aggr_us,
1493 unsigned long update_us)
1494{
1495 struct damon_sysfs_intervals *intervals = kmalloc(sizeof(*intervals),
1496 GFP_KERNEL);
1497
1498 if (!intervals)
1499 return NULL;
1500
1501 intervals->kobj = (struct kobject){};
1502 intervals->sample_us = sample_us;
1503 intervals->aggr_us = aggr_us;
1504 intervals->update_us = update_us;
1505 return intervals;
1506}
1507
1508static ssize_t sample_us_show(struct kobject *kobj,
1509 struct kobj_attribute *attr, char *buf)
1510{
1511 struct damon_sysfs_intervals *intervals = container_of(kobj,
1512 struct damon_sysfs_intervals, kobj);
1513
1514 return sysfs_emit(buf, "%lu\n", intervals->sample_us);
1515}
1516
1517static ssize_t sample_us_store(struct kobject *kobj,
1518 struct kobj_attribute *attr, const char *buf, size_t count)
1519{
1520 struct damon_sysfs_intervals *intervals = container_of(kobj,
1521 struct damon_sysfs_intervals, kobj);
1522 unsigned long us;
1523 int err = kstrtoul(buf, 0, &us);
1524
1525 if (err)
1526 return -EINVAL;
1527
1528 intervals->sample_us = us;
1529 return count;
1530}
1531
1532static ssize_t aggr_us_show(struct kobject *kobj, struct kobj_attribute *attr,
1533 char *buf)
1534{
1535 struct damon_sysfs_intervals *intervals = container_of(kobj,
1536 struct damon_sysfs_intervals, kobj);
1537
1538 return sysfs_emit(buf, "%lu\n", intervals->aggr_us);
1539}
1540
1541static ssize_t aggr_us_store(struct kobject *kobj, struct kobj_attribute *attr,
1542 const char *buf, size_t count)
1543{
1544 struct damon_sysfs_intervals *intervals = container_of(kobj,
1545 struct damon_sysfs_intervals, kobj);
1546 unsigned long us;
1547 int err = kstrtoul(buf, 0, &us);
1548
1549 if (err)
1550 return -EINVAL;
1551
1552 intervals->aggr_us = us;
1553 return count;
1554}
1555
1556static ssize_t update_us_show(struct kobject *kobj,
1557 struct kobj_attribute *attr, char *buf)
1558{
1559 struct damon_sysfs_intervals *intervals = container_of(kobj,
1560 struct damon_sysfs_intervals, kobj);
1561
1562 return sysfs_emit(buf, "%lu\n", intervals->update_us);
1563}
1564
1565static ssize_t update_us_store(struct kobject *kobj,
1566 struct kobj_attribute *attr, const char *buf, size_t count)
1567{
1568 struct damon_sysfs_intervals *intervals = container_of(kobj,
1569 struct damon_sysfs_intervals, kobj);
1570 unsigned long us;
1571 int err = kstrtoul(buf, 0, &us);
1572
1573 if (err)
1574 return -EINVAL;
1575
1576 intervals->update_us = us;
1577 return count;
1578}
1579
1580static void damon_sysfs_intervals_release(struct kobject *kobj)
1581{
1582 kfree(container_of(kobj, struct damon_sysfs_intervals, kobj));
1583}
1584
1585static struct kobj_attribute damon_sysfs_intervals_sample_us_attr =
1586 __ATTR_RW_MODE(sample_us, 0600);
1587
1588static struct kobj_attribute damon_sysfs_intervals_aggr_us_attr =
1589 __ATTR_RW_MODE(aggr_us, 0600);
1590
1591static struct kobj_attribute damon_sysfs_intervals_update_us_attr =
1592 __ATTR_RW_MODE(update_us, 0600);
1593
1594static struct attribute *damon_sysfs_intervals_attrs[] = {
1595 &damon_sysfs_intervals_sample_us_attr.attr,
1596 &damon_sysfs_intervals_aggr_us_attr.attr,
1597 &damon_sysfs_intervals_update_us_attr.attr,
1598 NULL,
1599};
1600ATTRIBUTE_GROUPS(damon_sysfs_intervals);
1601
1602static struct kobj_type damon_sysfs_intervals_ktype = {
1603 .release = damon_sysfs_intervals_release,
1604 .sysfs_ops = &kobj_sysfs_ops,
1605 .default_groups = damon_sysfs_intervals_groups,
1606};
1607
1608/*
1609 * monitoring_attrs directory
1610 */
1611
1612struct damon_sysfs_attrs {
1613 struct kobject kobj;
1614 struct damon_sysfs_intervals *intervals;
1615 struct damon_sysfs_ul_range *nr_regions_range;
1616};
1617
1618static struct damon_sysfs_attrs *damon_sysfs_attrs_alloc(void)
1619{
1620 struct damon_sysfs_attrs *attrs = kmalloc(sizeof(*attrs), GFP_KERNEL);
1621
1622 if (!attrs)
1623 return NULL;
1624 attrs->kobj = (struct kobject){};
1625 return attrs;
1626}
1627
1628static int damon_sysfs_attrs_add_dirs(struct damon_sysfs_attrs *attrs)
1629{
1630 struct damon_sysfs_intervals *intervals;
1631 struct damon_sysfs_ul_range *nr_regions_range;
1632 int err;
1633
1634 intervals = damon_sysfs_intervals_alloc(5000, 100000, 60000000);
1635 if (!intervals)
1636 return -ENOMEM;
1637
1638 err = kobject_init_and_add(&intervals->kobj,
1639 &damon_sysfs_intervals_ktype, &attrs->kobj,
1640 "intervals");
1641 if (err)
1642 goto put_intervals_out;
1643 attrs->intervals = intervals;
1644
1645 nr_regions_range = damon_sysfs_ul_range_alloc(10, 1000);
1646 if (!nr_regions_range) {
1647 err = -ENOMEM;
1648 goto put_intervals_out;
1649 }
1650
1651 err = kobject_init_and_add(&nr_regions_range->kobj,
1652 &damon_sysfs_ul_range_ktype, &attrs->kobj,
1653 "nr_regions");
1654 if (err)
1655 goto put_nr_regions_intervals_out;
1656 attrs->nr_regions_range = nr_regions_range;
1657 return 0;
1658
1659put_nr_regions_intervals_out:
1660 kobject_put(&nr_regions_range->kobj);
1661 attrs->nr_regions_range = NULL;
1662put_intervals_out:
1663 kobject_put(&intervals->kobj);
1664 attrs->intervals = NULL;
1665 return err;
1666}
1667
1668static void damon_sysfs_attrs_rm_dirs(struct damon_sysfs_attrs *attrs)
1669{
1670 kobject_put(&attrs->nr_regions_range->kobj);
1671 kobject_put(&attrs->intervals->kobj);
1672}
1673
1674static void damon_sysfs_attrs_release(struct kobject *kobj)
1675{
1676 kfree(container_of(kobj, struct damon_sysfs_attrs, kobj));
1677}
1678
1679static struct attribute *damon_sysfs_attrs_attrs[] = {
1680 NULL,
1681};
1682ATTRIBUTE_GROUPS(damon_sysfs_attrs);
1683
1684static struct kobj_type damon_sysfs_attrs_ktype = {
1685 .release = damon_sysfs_attrs_release,
1686 .sysfs_ops = &kobj_sysfs_ops,
1687 .default_groups = damon_sysfs_attrs_groups,
1688};
1689
1690/*
1691 * context directory
1692 */
1693
1694/* This should match with enum damon_ops_id */
1695static const char * const damon_sysfs_ops_strs[] = {
1696 "vaddr",
1697 "paddr",
1698};
1699
1700struct damon_sysfs_context {
1701 struct kobject kobj;
1702 enum damon_ops_id ops_id;
1703 struct damon_sysfs_attrs *attrs;
1704 struct damon_sysfs_targets *targets;
SeongJae Park7e84b1f2022-03-22 14:49:37 -07001705 struct damon_sysfs_schemes *schemes;
SeongJae Parkc951cd32022-03-22 14:49:27 -07001706};
1707
1708static struct damon_sysfs_context *damon_sysfs_context_alloc(
1709 enum damon_ops_id ops_id)
1710{
1711 struct damon_sysfs_context *context = kmalloc(sizeof(*context),
1712 GFP_KERNEL);
1713
1714 if (!context)
1715 return NULL;
1716 context->kobj = (struct kobject){};
1717 context->ops_id = ops_id;
1718 return context;
1719}
1720
1721static int damon_sysfs_context_set_attrs(struct damon_sysfs_context *context)
1722{
1723 struct damon_sysfs_attrs *attrs = damon_sysfs_attrs_alloc();
1724 int err;
1725
1726 if (!attrs)
1727 return -ENOMEM;
1728 err = kobject_init_and_add(&attrs->kobj, &damon_sysfs_attrs_ktype,
1729 &context->kobj, "monitoring_attrs");
1730 if (err)
1731 goto out;
1732 err = damon_sysfs_attrs_add_dirs(attrs);
1733 if (err)
1734 goto out;
1735 context->attrs = attrs;
1736 return 0;
1737
1738out:
1739 kobject_put(&attrs->kobj);
1740 return err;
1741}
1742
1743static int damon_sysfs_context_set_targets(struct damon_sysfs_context *context)
1744{
1745 struct damon_sysfs_targets *targets = damon_sysfs_targets_alloc();
1746 int err;
1747
1748 if (!targets)
1749 return -ENOMEM;
1750 err = kobject_init_and_add(&targets->kobj, &damon_sysfs_targets_ktype,
1751 &context->kobj, "targets");
1752 if (err) {
1753 kobject_put(&targets->kobj);
1754 return err;
1755 }
1756 context->targets = targets;
1757 return 0;
1758}
1759
SeongJae Park7e84b1f2022-03-22 14:49:37 -07001760static int damon_sysfs_context_set_schemes(struct damon_sysfs_context *context)
1761{
1762 struct damon_sysfs_schemes *schemes = damon_sysfs_schemes_alloc();
1763 int err;
1764
1765 if (!schemes)
1766 return -ENOMEM;
1767 err = kobject_init_and_add(&schemes->kobj, &damon_sysfs_schemes_ktype,
1768 &context->kobj, "schemes");
1769 if (err) {
1770 kobject_put(&schemes->kobj);
1771 return err;
1772 }
1773 context->schemes = schemes;
1774 return 0;
1775}
1776
SeongJae Parkc951cd32022-03-22 14:49:27 -07001777static int damon_sysfs_context_add_dirs(struct damon_sysfs_context *context)
1778{
1779 int err;
1780
1781 err = damon_sysfs_context_set_attrs(context);
1782 if (err)
1783 return err;
1784
1785 err = damon_sysfs_context_set_targets(context);
1786 if (err)
1787 goto put_attrs_out;
SeongJae Park7e84b1f2022-03-22 14:49:37 -07001788
1789 err = damon_sysfs_context_set_schemes(context);
1790 if (err)
1791 goto put_targets_attrs_out;
SeongJae Parkc951cd32022-03-22 14:49:27 -07001792 return 0;
1793
SeongJae Park7e84b1f2022-03-22 14:49:37 -07001794put_targets_attrs_out:
1795 kobject_put(&context->targets->kobj);
1796 context->targets = NULL;
SeongJae Parkc951cd32022-03-22 14:49:27 -07001797put_attrs_out:
1798 kobject_put(&context->attrs->kobj);
1799 context->attrs = NULL;
1800 return err;
1801}
1802
1803static void damon_sysfs_context_rm_dirs(struct damon_sysfs_context *context)
1804{
1805 damon_sysfs_attrs_rm_dirs(context->attrs);
1806 kobject_put(&context->attrs->kobj);
1807 damon_sysfs_targets_rm_dirs(context->targets);
1808 kobject_put(&context->targets->kobj);
SeongJae Park7e84b1f2022-03-22 14:49:37 -07001809 damon_sysfs_schemes_rm_dirs(context->schemes);
1810 kobject_put(&context->schemes->kobj);
SeongJae Parkc951cd32022-03-22 14:49:27 -07001811}
1812
1813static ssize_t operations_show(struct kobject *kobj,
1814 struct kobj_attribute *attr, char *buf)
1815{
1816 struct damon_sysfs_context *context = container_of(kobj,
1817 struct damon_sysfs_context, kobj);
1818
1819 return sysfs_emit(buf, "%s\n", damon_sysfs_ops_strs[context->ops_id]);
1820}
1821
1822static ssize_t operations_store(struct kobject *kobj,
1823 struct kobj_attribute *attr, const char *buf, size_t count)
1824{
1825 struct damon_sysfs_context *context = container_of(kobj,
1826 struct damon_sysfs_context, kobj);
1827 enum damon_ops_id id;
1828
1829 for (id = 0; id < NR_DAMON_OPS; id++) {
1830 if (sysfs_streq(buf, damon_sysfs_ops_strs[id])) {
SeongJae Parkc951cd32022-03-22 14:49:27 -07001831 context->ops_id = id;
1832 return count;
1833 }
1834 }
1835 return -EINVAL;
1836}
1837
1838static void damon_sysfs_context_release(struct kobject *kobj)
1839{
1840 kfree(container_of(kobj, struct damon_sysfs_context, kobj));
1841}
1842
1843static struct kobj_attribute damon_sysfs_context_operations_attr =
1844 __ATTR_RW_MODE(operations, 0600);
1845
1846static struct attribute *damon_sysfs_context_attrs[] = {
1847 &damon_sysfs_context_operations_attr.attr,
1848 NULL,
1849};
1850ATTRIBUTE_GROUPS(damon_sysfs_context);
1851
1852static struct kobj_type damon_sysfs_context_ktype = {
1853 .release = damon_sysfs_context_release,
1854 .sysfs_ops = &kobj_sysfs_ops,
1855 .default_groups = damon_sysfs_context_groups,
1856};
1857
1858/*
1859 * contexts directory
1860 */
1861
1862struct damon_sysfs_contexts {
1863 struct kobject kobj;
1864 struct damon_sysfs_context **contexts_arr;
1865 int nr;
1866};
1867
1868static struct damon_sysfs_contexts *damon_sysfs_contexts_alloc(void)
1869{
1870 return kzalloc(sizeof(struct damon_sysfs_contexts), GFP_KERNEL);
1871}
1872
1873static void damon_sysfs_contexts_rm_dirs(struct damon_sysfs_contexts *contexts)
1874{
1875 struct damon_sysfs_context **contexts_arr = contexts->contexts_arr;
1876 int i;
1877
1878 for (i = 0; i < contexts->nr; i++) {
1879 damon_sysfs_context_rm_dirs(contexts_arr[i]);
1880 kobject_put(&contexts_arr[i]->kobj);
1881 }
1882 contexts->nr = 0;
1883 kfree(contexts_arr);
1884 contexts->contexts_arr = NULL;
1885}
1886
1887static int damon_sysfs_contexts_add_dirs(struct damon_sysfs_contexts *contexts,
1888 int nr_contexts)
1889{
1890 struct damon_sysfs_context **contexts_arr, *context;
1891 int err, i;
1892
1893 damon_sysfs_contexts_rm_dirs(contexts);
1894 if (!nr_contexts)
1895 return 0;
1896
1897 contexts_arr = kmalloc_array(nr_contexts, sizeof(*contexts_arr),
1898 GFP_KERNEL | __GFP_NOWARN);
1899 if (!contexts_arr)
1900 return -ENOMEM;
1901 contexts->contexts_arr = contexts_arr;
1902
1903 for (i = 0; i < nr_contexts; i++) {
1904 context = damon_sysfs_context_alloc(DAMON_OPS_VADDR);
1905 if (!context) {
1906 damon_sysfs_contexts_rm_dirs(contexts);
1907 return -ENOMEM;
1908 }
1909
1910 err = kobject_init_and_add(&context->kobj,
1911 &damon_sysfs_context_ktype, &contexts->kobj,
1912 "%d", i);
1913 if (err)
1914 goto out;
1915
1916 err = damon_sysfs_context_add_dirs(context);
1917 if (err)
1918 goto out;
1919
1920 contexts_arr[i] = context;
1921 contexts->nr++;
1922 }
1923 return 0;
1924
1925out:
1926 damon_sysfs_contexts_rm_dirs(contexts);
1927 kobject_put(&context->kobj);
1928 return err;
1929}
1930
1931static ssize_t nr_contexts_show(struct kobject *kobj,
1932 struct kobj_attribute *attr, char *buf)
1933{
1934 struct damon_sysfs_contexts *contexts = container_of(kobj,
1935 struct damon_sysfs_contexts, kobj);
1936
1937 return sysfs_emit(buf, "%d\n", contexts->nr);
1938}
1939
1940static ssize_t nr_contexts_store(struct kobject *kobj,
1941 struct kobj_attribute *attr, const char *buf, size_t count)
1942{
1943 struct damon_sysfs_contexts *contexts = container_of(kobj,
1944 struct damon_sysfs_contexts, kobj);
1945 int nr, err;
1946
1947 err = kstrtoint(buf, 0, &nr);
1948 if (err)
1949 return err;
1950 /* TODO: support multiple contexts per kdamond */
1951 if (nr < 0 || 1 < nr)
1952 return -EINVAL;
1953
1954 if (!mutex_trylock(&damon_sysfs_lock))
1955 return -EBUSY;
1956 err = damon_sysfs_contexts_add_dirs(contexts, nr);
1957 mutex_unlock(&damon_sysfs_lock);
1958 if (err)
1959 return err;
1960
1961 return count;
1962}
1963
1964static void damon_sysfs_contexts_release(struct kobject *kobj)
1965{
1966 kfree(container_of(kobj, struct damon_sysfs_contexts, kobj));
1967}
1968
1969static struct kobj_attribute damon_sysfs_contexts_nr_attr
1970 = __ATTR_RW_MODE(nr_contexts, 0600);
1971
1972static struct attribute *damon_sysfs_contexts_attrs[] = {
1973 &damon_sysfs_contexts_nr_attr.attr,
1974 NULL,
1975};
1976ATTRIBUTE_GROUPS(damon_sysfs_contexts);
1977
1978static struct kobj_type damon_sysfs_contexts_ktype = {
1979 .release = damon_sysfs_contexts_release,
1980 .sysfs_ops = &kobj_sysfs_ops,
1981 .default_groups = damon_sysfs_contexts_groups,
1982};
1983
1984/*
1985 * kdamond directory
1986 */
1987
1988struct damon_sysfs_kdamond {
1989 struct kobject kobj;
1990 struct damon_sysfs_contexts *contexts;
1991 struct damon_ctx *damon_ctx;
1992};
1993
1994static struct damon_sysfs_kdamond *damon_sysfs_kdamond_alloc(void)
1995{
1996 return kzalloc(sizeof(struct damon_sysfs_kdamond), GFP_KERNEL);
1997}
1998
1999static int damon_sysfs_kdamond_add_dirs(struct damon_sysfs_kdamond *kdamond)
2000{
2001 struct damon_sysfs_contexts *contexts;
2002 int err;
2003
2004 contexts = damon_sysfs_contexts_alloc();
2005 if (!contexts)
2006 return -ENOMEM;
2007
2008 err = kobject_init_and_add(&contexts->kobj,
2009 &damon_sysfs_contexts_ktype, &kdamond->kobj,
2010 "contexts");
2011 if (err) {
2012 kobject_put(&contexts->kobj);
2013 return err;
2014 }
2015 kdamond->contexts = contexts;
2016
2017 return err;
2018}
2019
2020static void damon_sysfs_kdamond_rm_dirs(struct damon_sysfs_kdamond *kdamond)
2021{
2022 damon_sysfs_contexts_rm_dirs(kdamond->contexts);
2023 kobject_put(&kdamond->contexts->kobj);
2024}
2025
SeongJae Parka61ea562022-03-22 14:49:30 -07002026static bool damon_sysfs_ctx_running(struct damon_ctx *ctx)
2027{
2028 bool running;
2029
2030 mutex_lock(&ctx->kdamond_lock);
2031 running = ctx->kdamond != NULL;
2032 mutex_unlock(&ctx->kdamond_lock);
2033 return running;
2034}
2035
SeongJae Parkc951cd32022-03-22 14:49:27 -07002036static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr,
2037 char *buf)
2038{
SeongJae Parka61ea562022-03-22 14:49:30 -07002039 struct damon_sysfs_kdamond *kdamond = container_of(kobj,
2040 struct damon_sysfs_kdamond, kobj);
2041 struct damon_ctx *ctx = kdamond->damon_ctx;
2042 bool running;
2043
2044 if (!ctx)
2045 running = false;
2046 else
2047 running = damon_sysfs_ctx_running(ctx);
2048
2049 return sysfs_emit(buf, "%s\n", running ? "on" : "off");
2050}
2051
2052static int damon_sysfs_set_attrs(struct damon_ctx *ctx,
2053 struct damon_sysfs_attrs *sys_attrs)
2054{
2055 struct damon_sysfs_intervals *sys_intervals = sys_attrs->intervals;
2056 struct damon_sysfs_ul_range *sys_nr_regions =
2057 sys_attrs->nr_regions_range;
2058
2059 return damon_set_attrs(ctx, sys_intervals->sample_us,
2060 sys_intervals->aggr_us, sys_intervals->update_us,
2061 sys_nr_regions->min, sys_nr_regions->max);
2062}
2063
2064static void damon_sysfs_destroy_targets(struct damon_ctx *ctx)
2065{
2066 struct damon_target *t, *next;
2067
2068 damon_for_each_target_safe(t, next, ctx) {
2069 if (ctx->ops.id == DAMON_OPS_VADDR)
2070 put_pid(t->pid);
2071 damon_destroy_target(t);
2072 }
2073}
2074
SeongJae Park2031b142022-03-22 14:49:34 -07002075static int damon_sysfs_set_regions(struct damon_target *t,
2076 struct damon_sysfs_regions *sysfs_regions)
2077{
2078 int i;
2079
2080 for (i = 0; i < sysfs_regions->nr; i++) {
2081 struct damon_sysfs_region *sys_region =
2082 sysfs_regions->regions_arr[i];
2083 struct damon_region *prev, *r;
2084
2085 if (sys_region->start > sys_region->end)
2086 return -EINVAL;
2087 r = damon_new_region(sys_region->start, sys_region->end);
2088 if (!r)
2089 return -ENOMEM;
2090 damon_add_region(r, t);
2091 if (damon_nr_regions(t) > 1) {
2092 prev = damon_prev_region(r);
2093 if (prev->ar.end > r->ar.start) {
2094 damon_destroy_region(r, t);
2095 return -EINVAL;
2096 }
2097 }
2098 }
2099 return 0;
2100}
2101
SeongJae Parka61ea562022-03-22 14:49:30 -07002102static int damon_sysfs_set_targets(struct damon_ctx *ctx,
2103 struct damon_sysfs_targets *sysfs_targets)
2104{
SeongJae Park2031b142022-03-22 14:49:34 -07002105 int i, err;
SeongJae Parka61ea562022-03-22 14:49:30 -07002106
2107 for (i = 0; i < sysfs_targets->nr; i++) {
2108 struct damon_sysfs_target *sys_target =
2109 sysfs_targets->targets_arr[i];
2110 struct damon_target *t = damon_new_target();
2111
2112 if (!t) {
2113 damon_sysfs_destroy_targets(ctx);
2114 return -ENOMEM;
2115 }
2116 if (ctx->ops.id == DAMON_OPS_VADDR) {
2117 t->pid = find_get_pid(sys_target->pid);
2118 if (!t->pid) {
2119 damon_sysfs_destroy_targets(ctx);
2120 return -EINVAL;
2121 }
2122 }
2123 damon_add_target(ctx, t);
SeongJae Park2031b142022-03-22 14:49:34 -07002124 err = damon_sysfs_set_regions(t, sys_target->regions);
2125 if (err) {
2126 damon_sysfs_destroy_targets(ctx);
2127 return err;
2128 }
SeongJae Parka61ea562022-03-22 14:49:30 -07002129 }
2130 return 0;
2131}
2132
SeongJae Park7e84b1f2022-03-22 14:49:37 -07002133static struct damos *damon_sysfs_mk_scheme(
2134 struct damon_sysfs_scheme *sysfs_scheme)
2135{
2136 struct damon_sysfs_access_pattern *pattern =
2137 sysfs_scheme->access_pattern;
SeongJae Park9bbb8202022-03-22 14:49:40 -07002138 struct damon_sysfs_quotas *sysfs_quotas = sysfs_scheme->quotas;
SeongJae Park1c78b2b2022-03-22 14:49:43 -07002139 struct damon_sysfs_weights *sysfs_weights = sysfs_quotas->weights;
SeongJae Park1b322342022-03-22 14:49:46 -07002140 struct damon_sysfs_watermarks *sysfs_wmarks = sysfs_scheme->watermarks;
SeongJae Park9bbb8202022-03-22 14:49:40 -07002141 struct damos_quota quota = {
2142 .ms = sysfs_quotas->ms,
2143 .sz = sysfs_quotas->sz,
2144 .reset_interval = sysfs_quotas->reset_interval_ms,
SeongJae Park1c78b2b2022-03-22 14:49:43 -07002145 .weight_sz = sysfs_weights->sz,
2146 .weight_nr_accesses = sysfs_weights->nr_accesses,
2147 .weight_age = sysfs_weights->age,
SeongJae Park9bbb8202022-03-22 14:49:40 -07002148 };
SeongJae Park7e84b1f2022-03-22 14:49:37 -07002149 struct damos_watermarks wmarks = {
SeongJae Park1b322342022-03-22 14:49:46 -07002150 .metric = sysfs_wmarks->metric,
2151 .interval = sysfs_wmarks->interval_us,
2152 .high = sysfs_wmarks->high,
2153 .mid = sysfs_wmarks->mid,
2154 .low = sysfs_wmarks->low,
SeongJae Park7e84b1f2022-03-22 14:49:37 -07002155 };
2156
2157 return damon_new_scheme(pattern->sz->min, pattern->sz->max,
2158 pattern->nr_accesses->min, pattern->nr_accesses->max,
2159 pattern->age->min, pattern->age->max,
2160 sysfs_scheme->action, &quota, &wmarks);
2161}
2162
2163static int damon_sysfs_set_schemes(struct damon_ctx *ctx,
2164 struct damon_sysfs_schemes *sysfs_schemes)
2165{
2166 int i;
2167
2168 for (i = 0; i < sysfs_schemes->nr; i++) {
2169 struct damos *scheme, *next;
2170
2171 scheme = damon_sysfs_mk_scheme(sysfs_schemes->schemes_arr[i]);
2172 if (!scheme) {
2173 damon_for_each_scheme_safe(scheme, next, ctx)
2174 damon_destroy_scheme(scheme);
2175 return -ENOMEM;
2176 }
2177 damon_add_scheme(ctx, scheme);
2178 }
2179 return 0;
2180}
2181
SeongJae Parka61ea562022-03-22 14:49:30 -07002182static void damon_sysfs_before_terminate(struct damon_ctx *ctx)
2183{
2184 struct damon_target *t, *next;
2185
2186 if (ctx->ops.id != DAMON_OPS_VADDR)
2187 return;
2188
2189 mutex_lock(&ctx->kdamond_lock);
2190 damon_for_each_target_safe(t, next, ctx) {
2191 put_pid(t->pid);
2192 damon_destroy_target(t);
2193 }
2194 mutex_unlock(&ctx->kdamond_lock);
2195}
2196
2197static struct damon_ctx *damon_sysfs_build_ctx(
2198 struct damon_sysfs_context *sys_ctx)
2199{
2200 struct damon_ctx *ctx = damon_new_ctx();
2201 int err;
2202
2203 if (!ctx)
2204 return ERR_PTR(-ENOMEM);
2205
2206 err = damon_select_ops(ctx, sys_ctx->ops_id);
2207 if (err)
2208 goto out;
2209 err = damon_sysfs_set_attrs(ctx, sys_ctx->attrs);
2210 if (err)
2211 goto out;
2212 err = damon_sysfs_set_targets(ctx, sys_ctx->targets);
2213 if (err)
2214 goto out;
SeongJae Park7e84b1f2022-03-22 14:49:37 -07002215 err = damon_sysfs_set_schemes(ctx, sys_ctx->schemes);
2216 if (err)
2217 goto out;
SeongJae Parka61ea562022-03-22 14:49:30 -07002218
2219 ctx->callback.before_terminate = damon_sysfs_before_terminate;
2220 return ctx;
2221
2222out:
2223 damon_destroy_ctx(ctx);
2224 return ERR_PTR(err);
2225}
2226
2227static int damon_sysfs_turn_damon_on(struct damon_sysfs_kdamond *kdamond)
2228{
2229 struct damon_ctx *ctx;
2230 int err;
2231
2232 if (kdamond->damon_ctx &&
2233 damon_sysfs_ctx_running(kdamond->damon_ctx))
2234 return -EBUSY;
2235 /* TODO: support multiple contexts per kdamond */
2236 if (kdamond->contexts->nr != 1)
2237 return -EINVAL;
2238
2239 if (kdamond->damon_ctx)
2240 damon_destroy_ctx(kdamond->damon_ctx);
2241 kdamond->damon_ctx = NULL;
2242
2243 ctx = damon_sysfs_build_ctx(kdamond->contexts->contexts_arr[0]);
2244 if (IS_ERR(ctx))
2245 return PTR_ERR(ctx);
2246 err = damon_start(&ctx, 1, false);
2247 if (err) {
2248 damon_destroy_ctx(ctx);
2249 return err;
2250 }
2251 kdamond->damon_ctx = ctx;
2252 return err;
2253}
2254
2255static int damon_sysfs_turn_damon_off(struct damon_sysfs_kdamond *kdamond)
2256{
2257 if (!kdamond->damon_ctx)
2258 return -EINVAL;
2259 return damon_stop(&kdamond->damon_ctx, 1);
2260 /*
2261 * To allow users show final monitoring results of already turned-off
2262 * DAMON, we free kdamond->damon_ctx in next
2263 * damon_sysfs_turn_damon_on(), or kdamonds_nr_store()
2264 */
SeongJae Parkc951cd32022-03-22 14:49:27 -07002265}
2266
SeongJae Park0ac32b82022-03-22 14:49:49 -07002267static int damon_sysfs_update_schemes_stats(struct damon_sysfs_kdamond *kdamond)
2268{
2269 struct damon_ctx *ctx = kdamond->damon_ctx;
2270 struct damos *scheme;
2271 int schemes_idx = 0;
2272
2273 if (!ctx)
2274 return -EINVAL;
2275 mutex_lock(&ctx->kdamond_lock);
2276 damon_for_each_scheme(scheme, ctx) {
2277 struct damon_sysfs_schemes *sysfs_schemes;
2278 struct damon_sysfs_stats *sysfs_stats;
2279
2280 sysfs_schemes = kdamond->contexts->contexts_arr[0]->schemes;
2281 sysfs_stats = sysfs_schemes->schemes_arr[schemes_idx++]->stats;
2282 sysfs_stats->nr_tried = scheme->stat.nr_tried;
2283 sysfs_stats->sz_tried = scheme->stat.sz_tried;
2284 sysfs_stats->nr_applied = scheme->stat.nr_applied;
2285 sysfs_stats->sz_applied = scheme->stat.sz_applied;
2286 sysfs_stats->qt_exceeds = scheme->stat.qt_exceeds;
2287 }
2288 mutex_unlock(&ctx->kdamond_lock);
2289 return 0;
2290}
2291
SeongJae Parkc951cd32022-03-22 14:49:27 -07002292static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
2293 const char *buf, size_t count)
2294{
SeongJae Parka61ea562022-03-22 14:49:30 -07002295 struct damon_sysfs_kdamond *kdamond = container_of(kobj,
2296 struct damon_sysfs_kdamond, kobj);
2297 ssize_t ret;
2298
2299 if (!mutex_trylock(&damon_sysfs_lock))
2300 return -EBUSY;
2301 if (sysfs_streq(buf, "on"))
2302 ret = damon_sysfs_turn_damon_on(kdamond);
2303 else if (sysfs_streq(buf, "off"))
2304 ret = damon_sysfs_turn_damon_off(kdamond);
SeongJae Park0ac32b82022-03-22 14:49:49 -07002305 else if (sysfs_streq(buf, "update_schemes_stats"))
2306 ret = damon_sysfs_update_schemes_stats(kdamond);
SeongJae Parka61ea562022-03-22 14:49:30 -07002307 else
2308 ret = -EINVAL;
2309 mutex_unlock(&damon_sysfs_lock);
2310 if (!ret)
2311 ret = count;
2312 return ret;
SeongJae Parkc951cd32022-03-22 14:49:27 -07002313}
2314
2315static ssize_t pid_show(struct kobject *kobj,
2316 struct kobj_attribute *attr, char *buf)
2317{
SeongJae Parka61ea562022-03-22 14:49:30 -07002318 struct damon_sysfs_kdamond *kdamond = container_of(kobj,
2319 struct damon_sysfs_kdamond, kobj);
2320 struct damon_ctx *ctx;
2321 int pid;
2322
2323 if (!mutex_trylock(&damon_sysfs_lock))
2324 return -EBUSY;
2325 ctx = kdamond->damon_ctx;
2326 if (!ctx) {
2327 pid = -1;
2328 goto out;
2329 }
2330 mutex_lock(&ctx->kdamond_lock);
2331 if (!ctx->kdamond)
2332 pid = -1;
2333 else
2334 pid = ctx->kdamond->pid;
2335 mutex_unlock(&ctx->kdamond_lock);
2336out:
2337 mutex_unlock(&damon_sysfs_lock);
2338 return sysfs_emit(buf, "%d\n", pid);
SeongJae Parkc951cd32022-03-22 14:49:27 -07002339}
2340
2341static void damon_sysfs_kdamond_release(struct kobject *kobj)
2342{
2343 struct damon_sysfs_kdamond *kdamond = container_of(kobj,
2344 struct damon_sysfs_kdamond, kobj);
2345
2346 if (kdamond->damon_ctx)
2347 damon_destroy_ctx(kdamond->damon_ctx);
Xin Hao15423a52022-03-22 14:50:00 -07002348 kfree(kdamond);
SeongJae Parkc951cd32022-03-22 14:49:27 -07002349}
2350
2351static struct kobj_attribute damon_sysfs_kdamond_state_attr =
2352 __ATTR_RW_MODE(state, 0600);
2353
2354static struct kobj_attribute damon_sysfs_kdamond_pid_attr =
2355 __ATTR_RO_MODE(pid, 0400);
2356
2357static struct attribute *damon_sysfs_kdamond_attrs[] = {
2358 &damon_sysfs_kdamond_state_attr.attr,
2359 &damon_sysfs_kdamond_pid_attr.attr,
2360 NULL,
2361};
2362ATTRIBUTE_GROUPS(damon_sysfs_kdamond);
2363
2364static struct kobj_type damon_sysfs_kdamond_ktype = {
2365 .release = damon_sysfs_kdamond_release,
2366 .sysfs_ops = &kobj_sysfs_ops,
2367 .default_groups = damon_sysfs_kdamond_groups,
2368};
2369
2370/*
2371 * kdamonds directory
2372 */
2373
2374struct damon_sysfs_kdamonds {
2375 struct kobject kobj;
2376 struct damon_sysfs_kdamond **kdamonds_arr;
2377 int nr;
2378};
2379
2380static struct damon_sysfs_kdamonds *damon_sysfs_kdamonds_alloc(void)
2381{
2382 return kzalloc(sizeof(struct damon_sysfs_kdamonds), GFP_KERNEL);
2383}
2384
2385static void damon_sysfs_kdamonds_rm_dirs(struct damon_sysfs_kdamonds *kdamonds)
2386{
2387 struct damon_sysfs_kdamond **kdamonds_arr = kdamonds->kdamonds_arr;
2388 int i;
2389
2390 for (i = 0; i < kdamonds->nr; i++) {
2391 damon_sysfs_kdamond_rm_dirs(kdamonds_arr[i]);
2392 kobject_put(&kdamonds_arr[i]->kobj);
2393 }
2394 kdamonds->nr = 0;
2395 kfree(kdamonds_arr);
2396 kdamonds->kdamonds_arr = NULL;
2397}
2398
2399static int damon_sysfs_nr_running_ctxs(struct damon_sysfs_kdamond **kdamonds,
2400 int nr_kdamonds)
2401{
2402 int nr_running_ctxs = 0;
2403 int i;
2404
2405 for (i = 0; i < nr_kdamonds; i++) {
2406 struct damon_ctx *ctx = kdamonds[i]->damon_ctx;
2407
2408 if (!ctx)
2409 continue;
2410 mutex_lock(&ctx->kdamond_lock);
2411 if (ctx->kdamond)
2412 nr_running_ctxs++;
2413 mutex_unlock(&ctx->kdamond_lock);
2414 }
2415 return nr_running_ctxs;
2416}
2417
2418static int damon_sysfs_kdamonds_add_dirs(struct damon_sysfs_kdamonds *kdamonds,
2419 int nr_kdamonds)
2420{
2421 struct damon_sysfs_kdamond **kdamonds_arr, *kdamond;
2422 int err, i;
2423
2424 if (damon_sysfs_nr_running_ctxs(kdamonds->kdamonds_arr, kdamonds->nr))
2425 return -EBUSY;
2426
2427 damon_sysfs_kdamonds_rm_dirs(kdamonds);
2428 if (!nr_kdamonds)
2429 return 0;
2430
2431 kdamonds_arr = kmalloc_array(nr_kdamonds, sizeof(*kdamonds_arr),
2432 GFP_KERNEL | __GFP_NOWARN);
2433 if (!kdamonds_arr)
2434 return -ENOMEM;
2435 kdamonds->kdamonds_arr = kdamonds_arr;
2436
2437 for (i = 0; i < nr_kdamonds; i++) {
2438 kdamond = damon_sysfs_kdamond_alloc();
2439 if (!kdamond) {
2440 damon_sysfs_kdamonds_rm_dirs(kdamonds);
2441 return -ENOMEM;
2442 }
2443
2444 err = kobject_init_and_add(&kdamond->kobj,
2445 &damon_sysfs_kdamond_ktype, &kdamonds->kobj,
2446 "%d", i);
2447 if (err)
2448 goto out;
2449
2450 err = damon_sysfs_kdamond_add_dirs(kdamond);
2451 if (err)
2452 goto out;
2453
2454 kdamonds_arr[i] = kdamond;
2455 kdamonds->nr++;
2456 }
2457 return 0;
2458
2459out:
2460 damon_sysfs_kdamonds_rm_dirs(kdamonds);
2461 kobject_put(&kdamond->kobj);
2462 return err;
2463}
2464
2465static ssize_t nr_kdamonds_show(struct kobject *kobj,
2466 struct kobj_attribute *attr, char *buf)
2467{
2468 struct damon_sysfs_kdamonds *kdamonds = container_of(kobj,
2469 struct damon_sysfs_kdamonds, kobj);
2470
2471 return sysfs_emit(buf, "%d\n", kdamonds->nr);
2472}
2473
2474static ssize_t nr_kdamonds_store(struct kobject *kobj,
2475 struct kobj_attribute *attr, const char *buf, size_t count)
2476{
2477 struct damon_sysfs_kdamonds *kdamonds = container_of(kobj,
2478 struct damon_sysfs_kdamonds, kobj);
2479 int nr, err;
2480
2481 err = kstrtoint(buf, 0, &nr);
2482 if (err)
2483 return err;
2484 if (nr < 0)
2485 return -EINVAL;
2486
2487 if (!mutex_trylock(&damon_sysfs_lock))
2488 return -EBUSY;
2489 err = damon_sysfs_kdamonds_add_dirs(kdamonds, nr);
2490 mutex_unlock(&damon_sysfs_lock);
2491 if (err)
2492 return err;
2493
2494 return count;
2495}
2496
2497static void damon_sysfs_kdamonds_release(struct kobject *kobj)
2498{
2499 kfree(container_of(kobj, struct damon_sysfs_kdamonds, kobj));
2500}
2501
2502static struct kobj_attribute damon_sysfs_kdamonds_nr_attr =
2503 __ATTR_RW_MODE(nr_kdamonds, 0600);
2504
2505static struct attribute *damon_sysfs_kdamonds_attrs[] = {
2506 &damon_sysfs_kdamonds_nr_attr.attr,
2507 NULL,
2508};
2509ATTRIBUTE_GROUPS(damon_sysfs_kdamonds);
2510
2511static struct kobj_type damon_sysfs_kdamonds_ktype = {
2512 .release = damon_sysfs_kdamonds_release,
2513 .sysfs_ops = &kobj_sysfs_ops,
2514 .default_groups = damon_sysfs_kdamonds_groups,
2515};
2516
2517/*
2518 * damon user interface directory
2519 */
2520
2521struct damon_sysfs_ui_dir {
2522 struct kobject kobj;
2523 struct damon_sysfs_kdamonds *kdamonds;
2524};
2525
2526static struct damon_sysfs_ui_dir *damon_sysfs_ui_dir_alloc(void)
2527{
2528 return kzalloc(sizeof(struct damon_sysfs_ui_dir), GFP_KERNEL);
2529}
2530
2531static int damon_sysfs_ui_dir_add_dirs(struct damon_sysfs_ui_dir *ui_dir)
2532{
2533 struct damon_sysfs_kdamonds *kdamonds;
2534 int err;
2535
2536 kdamonds = damon_sysfs_kdamonds_alloc();
2537 if (!kdamonds)
2538 return -ENOMEM;
2539
2540 err = kobject_init_and_add(&kdamonds->kobj,
2541 &damon_sysfs_kdamonds_ktype, &ui_dir->kobj,
2542 "kdamonds");
2543 if (err) {
2544 kobject_put(&kdamonds->kobj);
2545 return err;
2546 }
2547 ui_dir->kdamonds = kdamonds;
2548 return err;
2549}
2550
2551static void damon_sysfs_ui_dir_release(struct kobject *kobj)
2552{
2553 kfree(container_of(kobj, struct damon_sysfs_ui_dir, kobj));
2554}
2555
2556static struct attribute *damon_sysfs_ui_dir_attrs[] = {
2557 NULL,
2558};
2559ATTRIBUTE_GROUPS(damon_sysfs_ui_dir);
2560
2561static struct kobj_type damon_sysfs_ui_dir_ktype = {
2562 .release = damon_sysfs_ui_dir_release,
2563 .sysfs_ops = &kobj_sysfs_ops,
2564 .default_groups = damon_sysfs_ui_dir_groups,
2565};
2566
2567static int __init damon_sysfs_init(void)
2568{
2569 struct kobject *damon_sysfs_root;
2570 struct damon_sysfs_ui_dir *admin;
2571 int err;
2572
2573 damon_sysfs_root = kobject_create_and_add("damon", mm_kobj);
2574 if (!damon_sysfs_root)
2575 return -ENOMEM;
2576
2577 admin = damon_sysfs_ui_dir_alloc();
2578 if (!admin) {
2579 kobject_put(damon_sysfs_root);
2580 return -ENOMEM;
2581 }
2582 err = kobject_init_and_add(&admin->kobj, &damon_sysfs_ui_dir_ktype,
2583 damon_sysfs_root, "admin");
2584 if (err)
2585 goto out;
2586 err = damon_sysfs_ui_dir_add_dirs(admin);
2587 if (err)
2588 goto out;
2589 return 0;
2590
2591out:
2592 kobject_put(&admin->kobj);
2593 kobject_put(damon_sysfs_root);
2594 return err;
2595}
2596subsys_initcall(damon_sysfs_init);