xref: /openbmc/linux/mm/damon/sysfs.c (revision d0c44de2d8ffd2e4780d360b34ee6614aa4af080)
1c951cd3bSSeongJae Park // SPDX-License-Identifier: GPL-2.0
2c951cd3bSSeongJae Park /*
3c951cd3bSSeongJae Park  * DAMON sysfs Interface
4c951cd3bSSeongJae Park  *
5c951cd3bSSeongJae Park  * Copyright (c) 2022 SeongJae Park <sj@kernel.org>
6c951cd3bSSeongJae Park  */
7c951cd3bSSeongJae Park 
8c951cd3bSSeongJae Park #include <linux/pid.h>
9c951cd3bSSeongJae Park #include <linux/sched.h>
10c951cd3bSSeongJae Park #include <linux/slab.h>
11c951cd3bSSeongJae Park 
1239240595SSeongJae Park #include "sysfs-common.h"
13c951cd3bSSeongJae Park 
14c951cd3bSSeongJae Park /*
152031b14eSSeongJae Park  * init region directory
162031b14eSSeongJae Park  */
172031b14eSSeongJae Park 
182031b14eSSeongJae Park struct damon_sysfs_region {
192031b14eSSeongJae Park 	struct kobject kobj;
20789a2306SSeongJae Park 	struct damon_addr_range ar;
212031b14eSSeongJae Park };
222031b14eSSeongJae Park 
damon_sysfs_region_alloc(void)231f719814SSeongJae Park static struct damon_sysfs_region *damon_sysfs_region_alloc(void)
242031b14eSSeongJae Park {
251f719814SSeongJae Park 	return kzalloc(sizeof(struct damon_sysfs_region), GFP_KERNEL);
262031b14eSSeongJae Park }
272031b14eSSeongJae Park 
start_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)282031b14eSSeongJae Park static ssize_t start_show(struct kobject *kobj, struct kobj_attribute *attr,
292031b14eSSeongJae Park 		char *buf)
302031b14eSSeongJae Park {
312031b14eSSeongJae Park 	struct damon_sysfs_region *region = container_of(kobj,
322031b14eSSeongJae Park 			struct damon_sysfs_region, kobj);
332031b14eSSeongJae Park 
34789a2306SSeongJae Park 	return sysfs_emit(buf, "%lu\n", region->ar.start);
352031b14eSSeongJae Park }
362031b14eSSeongJae Park 
start_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)372031b14eSSeongJae Park static ssize_t start_store(struct kobject *kobj, struct kobj_attribute *attr,
382031b14eSSeongJae Park 		const char *buf, size_t count)
392031b14eSSeongJae Park {
402031b14eSSeongJae Park 	struct damon_sysfs_region *region = container_of(kobj,
412031b14eSSeongJae Park 			struct damon_sysfs_region, kobj);
42789a2306SSeongJae Park 	int err = kstrtoul(buf, 0, &region->ar.start);
432031b14eSSeongJae Park 
4430b6242cSXin Hao 	return err ? err : count;
452031b14eSSeongJae Park }
462031b14eSSeongJae Park 
end_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)472031b14eSSeongJae Park static ssize_t end_show(struct kobject *kobj, struct kobj_attribute *attr,
482031b14eSSeongJae Park 		char *buf)
492031b14eSSeongJae Park {
502031b14eSSeongJae Park 	struct damon_sysfs_region *region = container_of(kobj,
512031b14eSSeongJae Park 			struct damon_sysfs_region, kobj);
522031b14eSSeongJae Park 
53789a2306SSeongJae Park 	return sysfs_emit(buf, "%lu\n", region->ar.end);
542031b14eSSeongJae Park }
552031b14eSSeongJae Park 
end_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)562031b14eSSeongJae Park static ssize_t end_store(struct kobject *kobj, struct kobj_attribute *attr,
572031b14eSSeongJae Park 		const char *buf, size_t count)
582031b14eSSeongJae Park {
592031b14eSSeongJae Park 	struct damon_sysfs_region *region = container_of(kobj,
602031b14eSSeongJae Park 			struct damon_sysfs_region, kobj);
61789a2306SSeongJae Park 	int err = kstrtoul(buf, 0, &region->ar.end);
622031b14eSSeongJae Park 
6330b6242cSXin Hao 	return err ? err : count;
642031b14eSSeongJae Park }
652031b14eSSeongJae Park 
damon_sysfs_region_release(struct kobject * kobj)662031b14eSSeongJae Park static void damon_sysfs_region_release(struct kobject *kobj)
672031b14eSSeongJae Park {
682031b14eSSeongJae Park 	kfree(container_of(kobj, struct damon_sysfs_region, kobj));
692031b14eSSeongJae Park }
702031b14eSSeongJae Park 
712031b14eSSeongJae Park static struct kobj_attribute damon_sysfs_region_start_attr =
722031b14eSSeongJae Park 		__ATTR_RW_MODE(start, 0600);
732031b14eSSeongJae Park 
742031b14eSSeongJae Park static struct kobj_attribute damon_sysfs_region_end_attr =
752031b14eSSeongJae Park 		__ATTR_RW_MODE(end, 0600);
762031b14eSSeongJae Park 
772031b14eSSeongJae Park static struct attribute *damon_sysfs_region_attrs[] = {
782031b14eSSeongJae Park 	&damon_sysfs_region_start_attr.attr,
792031b14eSSeongJae Park 	&damon_sysfs_region_end_attr.attr,
802031b14eSSeongJae Park 	NULL,
812031b14eSSeongJae Park };
822031b14eSSeongJae Park ATTRIBUTE_GROUPS(damon_sysfs_region);
832031b14eSSeongJae Park 
84e56397e8SThomas Weißschuh static const struct kobj_type damon_sysfs_region_ktype = {
852031b14eSSeongJae Park 	.release = damon_sysfs_region_release,
862031b14eSSeongJae Park 	.sysfs_ops = &kobj_sysfs_ops,
872031b14eSSeongJae Park 	.default_groups = damon_sysfs_region_groups,
882031b14eSSeongJae Park };
892031b14eSSeongJae Park 
902031b14eSSeongJae Park /*
912031b14eSSeongJae Park  * init_regions directory
922031b14eSSeongJae Park  */
932031b14eSSeongJae Park 
942031b14eSSeongJae Park struct damon_sysfs_regions {
952031b14eSSeongJae Park 	struct kobject kobj;
962031b14eSSeongJae Park 	struct damon_sysfs_region **regions_arr;
972031b14eSSeongJae Park 	int nr;
982031b14eSSeongJae Park };
992031b14eSSeongJae Park 
damon_sysfs_regions_alloc(void)1002031b14eSSeongJae Park static struct damon_sysfs_regions *damon_sysfs_regions_alloc(void)
1012031b14eSSeongJae Park {
1022031b14eSSeongJae Park 	return kzalloc(sizeof(struct damon_sysfs_regions), GFP_KERNEL);
1032031b14eSSeongJae Park }
1042031b14eSSeongJae Park 
damon_sysfs_regions_rm_dirs(struct damon_sysfs_regions * regions)1052031b14eSSeongJae Park static void damon_sysfs_regions_rm_dirs(struct damon_sysfs_regions *regions)
1062031b14eSSeongJae Park {
1072031b14eSSeongJae Park 	struct damon_sysfs_region **regions_arr = regions->regions_arr;
1082031b14eSSeongJae Park 	int i;
1092031b14eSSeongJae Park 
1102031b14eSSeongJae Park 	for (i = 0; i < regions->nr; i++)
1112031b14eSSeongJae Park 		kobject_put(&regions_arr[i]->kobj);
1122031b14eSSeongJae Park 	regions->nr = 0;
1132031b14eSSeongJae Park 	kfree(regions_arr);
1142031b14eSSeongJae Park 	regions->regions_arr = NULL;
1152031b14eSSeongJae Park }
1162031b14eSSeongJae Park 
damon_sysfs_regions_add_dirs(struct damon_sysfs_regions * regions,int nr_regions)1172031b14eSSeongJae Park static int damon_sysfs_regions_add_dirs(struct damon_sysfs_regions *regions,
1182031b14eSSeongJae Park 		int nr_regions)
1192031b14eSSeongJae Park {
1202031b14eSSeongJae Park 	struct damon_sysfs_region **regions_arr, *region;
1212031b14eSSeongJae Park 	int err, i;
1222031b14eSSeongJae Park 
1232031b14eSSeongJae Park 	damon_sysfs_regions_rm_dirs(regions);
1242031b14eSSeongJae Park 	if (!nr_regions)
1252031b14eSSeongJae Park 		return 0;
1262031b14eSSeongJae Park 
1272031b14eSSeongJae Park 	regions_arr = kmalloc_array(nr_regions, sizeof(*regions_arr),
1282031b14eSSeongJae Park 			GFP_KERNEL | __GFP_NOWARN);
1292031b14eSSeongJae Park 	if (!regions_arr)
1302031b14eSSeongJae Park 		return -ENOMEM;
1312031b14eSSeongJae Park 	regions->regions_arr = regions_arr;
1322031b14eSSeongJae Park 
1332031b14eSSeongJae Park 	for (i = 0; i < nr_regions; i++) {
1341f719814SSeongJae Park 		region = damon_sysfs_region_alloc();
1352031b14eSSeongJae Park 		if (!region) {
1362031b14eSSeongJae Park 			damon_sysfs_regions_rm_dirs(regions);
1372031b14eSSeongJae Park 			return -ENOMEM;
1382031b14eSSeongJae Park 		}
1392031b14eSSeongJae Park 
1402031b14eSSeongJae Park 		err = kobject_init_and_add(&region->kobj,
1412031b14eSSeongJae Park 				&damon_sysfs_region_ktype, &regions->kobj,
1422031b14eSSeongJae Park 				"%d", i);
1432031b14eSSeongJae Park 		if (err) {
1442031b14eSSeongJae Park 			kobject_put(&region->kobj);
1452031b14eSSeongJae Park 			damon_sysfs_regions_rm_dirs(regions);
1462031b14eSSeongJae Park 			return err;
1472031b14eSSeongJae Park 		}
1482031b14eSSeongJae Park 
1492031b14eSSeongJae Park 		regions_arr[i] = region;
1502031b14eSSeongJae Park 		regions->nr++;
1512031b14eSSeongJae Park 	}
1522031b14eSSeongJae Park 	return 0;
1532031b14eSSeongJae Park }
1542031b14eSSeongJae Park 
nr_regions_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)1552031b14eSSeongJae Park static ssize_t nr_regions_show(struct kobject *kobj,
1562031b14eSSeongJae Park 		struct kobj_attribute *attr, char *buf)
1572031b14eSSeongJae Park {
1582031b14eSSeongJae Park 	struct damon_sysfs_regions *regions = container_of(kobj,
1592031b14eSSeongJae Park 			struct damon_sysfs_regions, kobj);
1602031b14eSSeongJae Park 
1612031b14eSSeongJae Park 	return sysfs_emit(buf, "%d\n", regions->nr);
1622031b14eSSeongJae Park }
1632031b14eSSeongJae Park 
nr_regions_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)1642031b14eSSeongJae Park static ssize_t nr_regions_store(struct kobject *kobj,
1652031b14eSSeongJae Park 		struct kobj_attribute *attr, const char *buf, size_t count)
1662031b14eSSeongJae Park {
167a17a8b3bSXin Hao 	struct damon_sysfs_regions *regions;
1682031b14eSSeongJae Park 	int nr, err = kstrtoint(buf, 0, &nr);
1692031b14eSSeongJae Park 
1702031b14eSSeongJae Park 	if (err)
1712031b14eSSeongJae Park 		return err;
1722031b14eSSeongJae Park 	if (nr < 0)
1732031b14eSSeongJae Park 		return -EINVAL;
1742031b14eSSeongJae Park 
175a17a8b3bSXin Hao 	regions = container_of(kobj, struct damon_sysfs_regions, kobj);
176a17a8b3bSXin Hao 
1772031b14eSSeongJae Park 	if (!mutex_trylock(&damon_sysfs_lock))
1782031b14eSSeongJae Park 		return -EBUSY;
1792031b14eSSeongJae Park 	err = damon_sysfs_regions_add_dirs(regions, nr);
1802031b14eSSeongJae Park 	mutex_unlock(&damon_sysfs_lock);
1812031b14eSSeongJae Park 	if (err)
1822031b14eSSeongJae Park 		return err;
1832031b14eSSeongJae Park 
1842031b14eSSeongJae Park 	return count;
1852031b14eSSeongJae Park }
1862031b14eSSeongJae Park 
damon_sysfs_regions_release(struct kobject * kobj)1872031b14eSSeongJae Park static void damon_sysfs_regions_release(struct kobject *kobj)
1882031b14eSSeongJae Park {
1892031b14eSSeongJae Park 	kfree(container_of(kobj, struct damon_sysfs_regions, kobj));
1902031b14eSSeongJae Park }
1912031b14eSSeongJae Park 
1922031b14eSSeongJae Park static struct kobj_attribute damon_sysfs_regions_nr_attr =
1932031b14eSSeongJae Park 		__ATTR_RW_MODE(nr_regions, 0600);
1942031b14eSSeongJae Park 
1952031b14eSSeongJae Park static struct attribute *damon_sysfs_regions_attrs[] = {
1962031b14eSSeongJae Park 	&damon_sysfs_regions_nr_attr.attr,
1972031b14eSSeongJae Park 	NULL,
1982031b14eSSeongJae Park };
1992031b14eSSeongJae Park ATTRIBUTE_GROUPS(damon_sysfs_regions);
2002031b14eSSeongJae Park 
201e56397e8SThomas Weißschuh static const struct kobj_type damon_sysfs_regions_ktype = {
2022031b14eSSeongJae Park 	.release = damon_sysfs_regions_release,
2032031b14eSSeongJae Park 	.sysfs_ops = &kobj_sysfs_ops,
2042031b14eSSeongJae Park 	.default_groups = damon_sysfs_regions_groups,
2052031b14eSSeongJae Park };
2062031b14eSSeongJae Park 
2072031b14eSSeongJae Park /*
208c951cd3bSSeongJae Park  * target directory
209c951cd3bSSeongJae Park  */
210c951cd3bSSeongJae Park 
211c951cd3bSSeongJae Park struct damon_sysfs_target {
212c951cd3bSSeongJae Park 	struct kobject kobj;
2132031b14eSSeongJae Park 	struct damon_sysfs_regions *regions;
214c951cd3bSSeongJae Park 	int pid;
215c951cd3bSSeongJae Park };
216c951cd3bSSeongJae Park 
damon_sysfs_target_alloc(void)217c951cd3bSSeongJae Park static struct damon_sysfs_target *damon_sysfs_target_alloc(void)
218c951cd3bSSeongJae Park {
219c951cd3bSSeongJae Park 	return kzalloc(sizeof(struct damon_sysfs_target), GFP_KERNEL);
220c951cd3bSSeongJae Park }
221c951cd3bSSeongJae Park 
damon_sysfs_target_add_dirs(struct damon_sysfs_target * target)2222031b14eSSeongJae Park static int damon_sysfs_target_add_dirs(struct damon_sysfs_target *target)
2232031b14eSSeongJae Park {
2242031b14eSSeongJae Park 	struct damon_sysfs_regions *regions = damon_sysfs_regions_alloc();
2252031b14eSSeongJae Park 	int err;
2262031b14eSSeongJae Park 
2272031b14eSSeongJae Park 	if (!regions)
2282031b14eSSeongJae Park 		return -ENOMEM;
2292031b14eSSeongJae Park 
2302031b14eSSeongJae Park 	err = kobject_init_and_add(&regions->kobj, &damon_sysfs_regions_ktype,
2312031b14eSSeongJae Park 			&target->kobj, "regions");
2322031b14eSSeongJae Park 	if (err)
2332031b14eSSeongJae Park 		kobject_put(&regions->kobj);
2342031b14eSSeongJae Park 	else
2352031b14eSSeongJae Park 		target->regions = regions;
2362031b14eSSeongJae Park 	return err;
2372031b14eSSeongJae Park }
2382031b14eSSeongJae Park 
damon_sysfs_target_rm_dirs(struct damon_sysfs_target * target)2392031b14eSSeongJae Park static void damon_sysfs_target_rm_dirs(struct damon_sysfs_target *target)
2402031b14eSSeongJae Park {
2412031b14eSSeongJae Park 	damon_sysfs_regions_rm_dirs(target->regions);
2422031b14eSSeongJae Park 	kobject_put(&target->regions->kobj);
2432031b14eSSeongJae Park }
2442031b14eSSeongJae Park 
pid_target_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)245c951cd3bSSeongJae Park static ssize_t pid_target_show(struct kobject *kobj,
246c951cd3bSSeongJae Park 		struct kobj_attribute *attr, char *buf)
247c951cd3bSSeongJae Park {
248c951cd3bSSeongJae Park 	struct damon_sysfs_target *target = container_of(kobj,
249c951cd3bSSeongJae Park 			struct damon_sysfs_target, kobj);
250c951cd3bSSeongJae Park 
251c951cd3bSSeongJae Park 	return sysfs_emit(buf, "%d\n", target->pid);
252c951cd3bSSeongJae Park }
253c951cd3bSSeongJae Park 
pid_target_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)254c951cd3bSSeongJae Park static ssize_t pid_target_store(struct kobject *kobj,
255c951cd3bSSeongJae Park 		struct kobj_attribute *attr, const char *buf, size_t count)
256c951cd3bSSeongJae Park {
257c951cd3bSSeongJae Park 	struct damon_sysfs_target *target = container_of(kobj,
258c951cd3bSSeongJae Park 			struct damon_sysfs_target, kobj);
259c951cd3bSSeongJae Park 	int err = kstrtoint(buf, 0, &target->pid);
260c951cd3bSSeongJae Park 
261c951cd3bSSeongJae Park 	if (err)
262c951cd3bSSeongJae Park 		return -EINVAL;
263c951cd3bSSeongJae Park 	return count;
264c951cd3bSSeongJae Park }
265c951cd3bSSeongJae Park 
damon_sysfs_target_release(struct kobject * kobj)266c951cd3bSSeongJae Park static void damon_sysfs_target_release(struct kobject *kobj)
267c951cd3bSSeongJae Park {
268c951cd3bSSeongJae Park 	kfree(container_of(kobj, struct damon_sysfs_target, kobj));
269c951cd3bSSeongJae Park }
270c951cd3bSSeongJae Park 
271c951cd3bSSeongJae Park static struct kobj_attribute damon_sysfs_target_pid_attr =
272c951cd3bSSeongJae Park 		__ATTR_RW_MODE(pid_target, 0600);
273c951cd3bSSeongJae Park 
274c951cd3bSSeongJae Park static struct attribute *damon_sysfs_target_attrs[] = {
275c951cd3bSSeongJae Park 	&damon_sysfs_target_pid_attr.attr,
276c951cd3bSSeongJae Park 	NULL,
277c951cd3bSSeongJae Park };
278c951cd3bSSeongJae Park ATTRIBUTE_GROUPS(damon_sysfs_target);
279c951cd3bSSeongJae Park 
280e56397e8SThomas Weißschuh static const struct kobj_type damon_sysfs_target_ktype = {
281c951cd3bSSeongJae Park 	.release = damon_sysfs_target_release,
282c951cd3bSSeongJae Park 	.sysfs_ops = &kobj_sysfs_ops,
283c951cd3bSSeongJae Park 	.default_groups = damon_sysfs_target_groups,
284c951cd3bSSeongJae Park };
285c951cd3bSSeongJae Park 
286c951cd3bSSeongJae Park /*
287c951cd3bSSeongJae Park  * targets directory
288c951cd3bSSeongJae Park  */
289c951cd3bSSeongJae Park 
290c951cd3bSSeongJae Park struct damon_sysfs_targets {
291c951cd3bSSeongJae Park 	struct kobject kobj;
292c951cd3bSSeongJae Park 	struct damon_sysfs_target **targets_arr;
293c951cd3bSSeongJae Park 	int nr;
294c951cd3bSSeongJae Park };
295c951cd3bSSeongJae Park 
damon_sysfs_targets_alloc(void)296c951cd3bSSeongJae Park static struct damon_sysfs_targets *damon_sysfs_targets_alloc(void)
297c951cd3bSSeongJae Park {
298c951cd3bSSeongJae Park 	return kzalloc(sizeof(struct damon_sysfs_targets), GFP_KERNEL);
299c951cd3bSSeongJae Park }
300c951cd3bSSeongJae Park 
damon_sysfs_targets_rm_dirs(struct damon_sysfs_targets * targets)301c951cd3bSSeongJae Park static void damon_sysfs_targets_rm_dirs(struct damon_sysfs_targets *targets)
302c951cd3bSSeongJae Park {
303c951cd3bSSeongJae Park 	struct damon_sysfs_target **targets_arr = targets->targets_arr;
304c951cd3bSSeongJae Park 	int i;
305c951cd3bSSeongJae Park 
3062031b14eSSeongJae Park 	for (i = 0; i < targets->nr; i++) {
3072031b14eSSeongJae Park 		damon_sysfs_target_rm_dirs(targets_arr[i]);
308c951cd3bSSeongJae Park 		kobject_put(&targets_arr[i]->kobj);
3092031b14eSSeongJae Park 	}
310c951cd3bSSeongJae Park 	targets->nr = 0;
311c951cd3bSSeongJae Park 	kfree(targets_arr);
312c951cd3bSSeongJae Park 	targets->targets_arr = NULL;
313c951cd3bSSeongJae Park }
314c951cd3bSSeongJae Park 
damon_sysfs_targets_add_dirs(struct damon_sysfs_targets * targets,int nr_targets)315c951cd3bSSeongJae Park static int damon_sysfs_targets_add_dirs(struct damon_sysfs_targets *targets,
316c951cd3bSSeongJae Park 		int nr_targets)
317c951cd3bSSeongJae Park {
318c951cd3bSSeongJae Park 	struct damon_sysfs_target **targets_arr, *target;
319c951cd3bSSeongJae Park 	int err, i;
320c951cd3bSSeongJae Park 
321c951cd3bSSeongJae Park 	damon_sysfs_targets_rm_dirs(targets);
322c951cd3bSSeongJae Park 	if (!nr_targets)
323c951cd3bSSeongJae Park 		return 0;
324c951cd3bSSeongJae Park 
325c951cd3bSSeongJae Park 	targets_arr = kmalloc_array(nr_targets, sizeof(*targets_arr),
326c951cd3bSSeongJae Park 			GFP_KERNEL | __GFP_NOWARN);
327c951cd3bSSeongJae Park 	if (!targets_arr)
328c951cd3bSSeongJae Park 		return -ENOMEM;
329c951cd3bSSeongJae Park 	targets->targets_arr = targets_arr;
330c951cd3bSSeongJae Park 
331c951cd3bSSeongJae Park 	for (i = 0; i < nr_targets; i++) {
332c951cd3bSSeongJae Park 		target = damon_sysfs_target_alloc();
333c951cd3bSSeongJae Park 		if (!target) {
334c951cd3bSSeongJae Park 			damon_sysfs_targets_rm_dirs(targets);
335c951cd3bSSeongJae Park 			return -ENOMEM;
336c951cd3bSSeongJae Park 		}
337c951cd3bSSeongJae Park 
338c951cd3bSSeongJae Park 		err = kobject_init_and_add(&target->kobj,
339c951cd3bSSeongJae Park 				&damon_sysfs_target_ktype, &targets->kobj,
340c951cd3bSSeongJae Park 				"%d", i);
341c951cd3bSSeongJae Park 		if (err)
342c951cd3bSSeongJae Park 			goto out;
343c951cd3bSSeongJae Park 
3442031b14eSSeongJae Park 		err = damon_sysfs_target_add_dirs(target);
3452031b14eSSeongJae Park 		if (err)
3462031b14eSSeongJae Park 			goto out;
3472031b14eSSeongJae Park 
348c951cd3bSSeongJae Park 		targets_arr[i] = target;
349c951cd3bSSeongJae Park 		targets->nr++;
350c951cd3bSSeongJae Park 	}
351c951cd3bSSeongJae Park 	return 0;
352c951cd3bSSeongJae Park 
353c951cd3bSSeongJae Park out:
354c951cd3bSSeongJae Park 	damon_sysfs_targets_rm_dirs(targets);
355c951cd3bSSeongJae Park 	kobject_put(&target->kobj);
356c951cd3bSSeongJae Park 	return err;
357c951cd3bSSeongJae Park }
358c951cd3bSSeongJae Park 
nr_targets_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)359c951cd3bSSeongJae Park static ssize_t nr_targets_show(struct kobject *kobj,
360c951cd3bSSeongJae Park 		struct kobj_attribute *attr, char *buf)
361c951cd3bSSeongJae Park {
362c951cd3bSSeongJae Park 	struct damon_sysfs_targets *targets = container_of(kobj,
363c951cd3bSSeongJae Park 			struct damon_sysfs_targets, kobj);
364c951cd3bSSeongJae Park 
365c951cd3bSSeongJae Park 	return sysfs_emit(buf, "%d\n", targets->nr);
366c951cd3bSSeongJae Park }
367c951cd3bSSeongJae Park 
nr_targets_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)368c951cd3bSSeongJae Park static ssize_t nr_targets_store(struct kobject *kobj,
369c951cd3bSSeongJae Park 		struct kobj_attribute *attr, const char *buf, size_t count)
370c951cd3bSSeongJae Park {
371a17a8b3bSXin Hao 	struct damon_sysfs_targets *targets;
372c951cd3bSSeongJae Park 	int nr, err = kstrtoint(buf, 0, &nr);
373c951cd3bSSeongJae Park 
374c951cd3bSSeongJae Park 	if (err)
375c951cd3bSSeongJae Park 		return err;
376c951cd3bSSeongJae Park 	if (nr < 0)
377c951cd3bSSeongJae Park 		return -EINVAL;
378c951cd3bSSeongJae Park 
379a17a8b3bSXin Hao 	targets = container_of(kobj, struct damon_sysfs_targets, kobj);
380a17a8b3bSXin Hao 
381c951cd3bSSeongJae Park 	if (!mutex_trylock(&damon_sysfs_lock))
382c951cd3bSSeongJae Park 		return -EBUSY;
383c951cd3bSSeongJae Park 	err = damon_sysfs_targets_add_dirs(targets, nr);
384c951cd3bSSeongJae Park 	mutex_unlock(&damon_sysfs_lock);
385c951cd3bSSeongJae Park 	if (err)
386c951cd3bSSeongJae Park 		return err;
387c951cd3bSSeongJae Park 
388c951cd3bSSeongJae Park 	return count;
389c951cd3bSSeongJae Park }
390c951cd3bSSeongJae Park 
damon_sysfs_targets_release(struct kobject * kobj)391c951cd3bSSeongJae Park static void damon_sysfs_targets_release(struct kobject *kobj)
392c951cd3bSSeongJae Park {
393c951cd3bSSeongJae Park 	kfree(container_of(kobj, struct damon_sysfs_targets, kobj));
394c951cd3bSSeongJae Park }
395c951cd3bSSeongJae Park 
396c951cd3bSSeongJae Park static struct kobj_attribute damon_sysfs_targets_nr_attr =
397c951cd3bSSeongJae Park 		__ATTR_RW_MODE(nr_targets, 0600);
398c951cd3bSSeongJae Park 
399c951cd3bSSeongJae Park static struct attribute *damon_sysfs_targets_attrs[] = {
400c951cd3bSSeongJae Park 	&damon_sysfs_targets_nr_attr.attr,
401c951cd3bSSeongJae Park 	NULL,
402c951cd3bSSeongJae Park };
403c951cd3bSSeongJae Park ATTRIBUTE_GROUPS(damon_sysfs_targets);
404c951cd3bSSeongJae Park 
405e56397e8SThomas Weißschuh static const struct kobj_type damon_sysfs_targets_ktype = {
406c951cd3bSSeongJae Park 	.release = damon_sysfs_targets_release,
407c951cd3bSSeongJae Park 	.sysfs_ops = &kobj_sysfs_ops,
408c951cd3bSSeongJae Park 	.default_groups = damon_sysfs_targets_groups,
409c951cd3bSSeongJae Park };
410c951cd3bSSeongJae Park 
411c951cd3bSSeongJae Park /*
412c951cd3bSSeongJae Park  * intervals directory
413c951cd3bSSeongJae Park  */
414c951cd3bSSeongJae Park 
415c951cd3bSSeongJae Park struct damon_sysfs_intervals {
416c951cd3bSSeongJae Park 	struct kobject kobj;
417c951cd3bSSeongJae Park 	unsigned long sample_us;
418c951cd3bSSeongJae Park 	unsigned long aggr_us;
419c951cd3bSSeongJae Park 	unsigned long update_us;
420c951cd3bSSeongJae Park };
421c951cd3bSSeongJae Park 
damon_sysfs_intervals_alloc(unsigned long sample_us,unsigned long aggr_us,unsigned long update_us)422c951cd3bSSeongJae Park static struct damon_sysfs_intervals *damon_sysfs_intervals_alloc(
423c951cd3bSSeongJae Park 		unsigned long sample_us, unsigned long aggr_us,
424c951cd3bSSeongJae Park 		unsigned long update_us)
425c951cd3bSSeongJae Park {
426c951cd3bSSeongJae Park 	struct damon_sysfs_intervals *intervals = kmalloc(sizeof(*intervals),
427c951cd3bSSeongJae Park 			GFP_KERNEL);
428c951cd3bSSeongJae Park 
429c951cd3bSSeongJae Park 	if (!intervals)
430c951cd3bSSeongJae Park 		return NULL;
431c951cd3bSSeongJae Park 
432c951cd3bSSeongJae Park 	intervals->kobj = (struct kobject){};
433c951cd3bSSeongJae Park 	intervals->sample_us = sample_us;
434c951cd3bSSeongJae Park 	intervals->aggr_us = aggr_us;
435c951cd3bSSeongJae Park 	intervals->update_us = update_us;
436c951cd3bSSeongJae Park 	return intervals;
437c951cd3bSSeongJae Park }
438c951cd3bSSeongJae Park 
sample_us_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)439c951cd3bSSeongJae Park static ssize_t sample_us_show(struct kobject *kobj,
440c951cd3bSSeongJae Park 		struct kobj_attribute *attr, char *buf)
441c951cd3bSSeongJae Park {
442c951cd3bSSeongJae Park 	struct damon_sysfs_intervals *intervals = container_of(kobj,
443c951cd3bSSeongJae Park 			struct damon_sysfs_intervals, kobj);
444c951cd3bSSeongJae Park 
445c951cd3bSSeongJae Park 	return sysfs_emit(buf, "%lu\n", intervals->sample_us);
446c951cd3bSSeongJae Park }
447c951cd3bSSeongJae Park 
sample_us_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)448c951cd3bSSeongJae Park static ssize_t sample_us_store(struct kobject *kobj,
449c951cd3bSSeongJae Park 		struct kobj_attribute *attr, const char *buf, size_t count)
450c951cd3bSSeongJae Park {
451c951cd3bSSeongJae Park 	struct damon_sysfs_intervals *intervals = container_of(kobj,
452c951cd3bSSeongJae Park 			struct damon_sysfs_intervals, kobj);
453c951cd3bSSeongJae Park 	unsigned long us;
454c951cd3bSSeongJae Park 	int err = kstrtoul(buf, 0, &us);
455c951cd3bSSeongJae Park 
456c951cd3bSSeongJae Park 	if (err)
45730b6242cSXin Hao 		return err;
458c951cd3bSSeongJae Park 
459c951cd3bSSeongJae Park 	intervals->sample_us = us;
460c951cd3bSSeongJae Park 	return count;
461c951cd3bSSeongJae Park }
462c951cd3bSSeongJae Park 
aggr_us_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)463c951cd3bSSeongJae Park static ssize_t aggr_us_show(struct kobject *kobj, struct kobj_attribute *attr,
464c951cd3bSSeongJae Park 		char *buf)
465c951cd3bSSeongJae Park {
466c951cd3bSSeongJae Park 	struct damon_sysfs_intervals *intervals = container_of(kobj,
467c951cd3bSSeongJae Park 			struct damon_sysfs_intervals, kobj);
468c951cd3bSSeongJae Park 
469c951cd3bSSeongJae Park 	return sysfs_emit(buf, "%lu\n", intervals->aggr_us);
470c951cd3bSSeongJae Park }
471c951cd3bSSeongJae Park 
aggr_us_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)472c951cd3bSSeongJae Park static ssize_t aggr_us_store(struct kobject *kobj, struct kobj_attribute *attr,
473c951cd3bSSeongJae Park 		const char *buf, size_t count)
474c951cd3bSSeongJae Park {
475c951cd3bSSeongJae Park 	struct damon_sysfs_intervals *intervals = container_of(kobj,
476c951cd3bSSeongJae Park 			struct damon_sysfs_intervals, kobj);
477c951cd3bSSeongJae Park 	unsigned long us;
478c951cd3bSSeongJae Park 	int err = kstrtoul(buf, 0, &us);
479c951cd3bSSeongJae Park 
480c951cd3bSSeongJae Park 	if (err)
48130b6242cSXin Hao 		return err;
482c951cd3bSSeongJae Park 
483c951cd3bSSeongJae Park 	intervals->aggr_us = us;
484c951cd3bSSeongJae Park 	return count;
485c951cd3bSSeongJae Park }
486c951cd3bSSeongJae Park 
update_us_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)487c951cd3bSSeongJae Park static ssize_t update_us_show(struct kobject *kobj,
488c951cd3bSSeongJae Park 		struct kobj_attribute *attr, char *buf)
489c951cd3bSSeongJae Park {
490c951cd3bSSeongJae Park 	struct damon_sysfs_intervals *intervals = container_of(kobj,
491c951cd3bSSeongJae Park 			struct damon_sysfs_intervals, kobj);
492c951cd3bSSeongJae Park 
493c951cd3bSSeongJae Park 	return sysfs_emit(buf, "%lu\n", intervals->update_us);
494c951cd3bSSeongJae Park }
495c951cd3bSSeongJae Park 
update_us_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)496c951cd3bSSeongJae Park static ssize_t update_us_store(struct kobject *kobj,
497c951cd3bSSeongJae Park 		struct kobj_attribute *attr, const char *buf, size_t count)
498c951cd3bSSeongJae Park {
499c951cd3bSSeongJae Park 	struct damon_sysfs_intervals *intervals = container_of(kobj,
500c951cd3bSSeongJae Park 			struct damon_sysfs_intervals, kobj);
501c951cd3bSSeongJae Park 	unsigned long us;
502c951cd3bSSeongJae Park 	int err = kstrtoul(buf, 0, &us);
503c951cd3bSSeongJae Park 
504c951cd3bSSeongJae Park 	if (err)
50530b6242cSXin Hao 		return err;
506c951cd3bSSeongJae Park 
507c951cd3bSSeongJae Park 	intervals->update_us = us;
508c951cd3bSSeongJae Park 	return count;
509c951cd3bSSeongJae Park }
510c951cd3bSSeongJae Park 
damon_sysfs_intervals_release(struct kobject * kobj)511c951cd3bSSeongJae Park static void damon_sysfs_intervals_release(struct kobject *kobj)
512c951cd3bSSeongJae Park {
513c951cd3bSSeongJae Park 	kfree(container_of(kobj, struct damon_sysfs_intervals, kobj));
514c951cd3bSSeongJae Park }
515c951cd3bSSeongJae Park 
516c951cd3bSSeongJae Park static struct kobj_attribute damon_sysfs_intervals_sample_us_attr =
517c951cd3bSSeongJae Park 		__ATTR_RW_MODE(sample_us, 0600);
518c951cd3bSSeongJae Park 
519c951cd3bSSeongJae Park static struct kobj_attribute damon_sysfs_intervals_aggr_us_attr =
520c951cd3bSSeongJae Park 		__ATTR_RW_MODE(aggr_us, 0600);
521c951cd3bSSeongJae Park 
522c951cd3bSSeongJae Park static struct kobj_attribute damon_sysfs_intervals_update_us_attr =
523c951cd3bSSeongJae Park 		__ATTR_RW_MODE(update_us, 0600);
524c951cd3bSSeongJae Park 
525c951cd3bSSeongJae Park static struct attribute *damon_sysfs_intervals_attrs[] = {
526c951cd3bSSeongJae Park 	&damon_sysfs_intervals_sample_us_attr.attr,
527c951cd3bSSeongJae Park 	&damon_sysfs_intervals_aggr_us_attr.attr,
528c951cd3bSSeongJae Park 	&damon_sysfs_intervals_update_us_attr.attr,
529c951cd3bSSeongJae Park 	NULL,
530c951cd3bSSeongJae Park };
531c951cd3bSSeongJae Park ATTRIBUTE_GROUPS(damon_sysfs_intervals);
532c951cd3bSSeongJae Park 
533e56397e8SThomas Weißschuh static const struct kobj_type damon_sysfs_intervals_ktype = {
534c951cd3bSSeongJae Park 	.release = damon_sysfs_intervals_release,
535c951cd3bSSeongJae Park 	.sysfs_ops = &kobj_sysfs_ops,
536c951cd3bSSeongJae Park 	.default_groups = damon_sysfs_intervals_groups,
537c951cd3bSSeongJae Park };
538c951cd3bSSeongJae Park 
539c951cd3bSSeongJae Park /*
540c951cd3bSSeongJae Park  * monitoring_attrs directory
541c951cd3bSSeongJae Park  */
542c951cd3bSSeongJae Park 
543c951cd3bSSeongJae Park struct damon_sysfs_attrs {
544c951cd3bSSeongJae Park 	struct kobject kobj;
545c951cd3bSSeongJae Park 	struct damon_sysfs_intervals *intervals;
546c951cd3bSSeongJae Park 	struct damon_sysfs_ul_range *nr_regions_range;
547c951cd3bSSeongJae Park };
548c951cd3bSSeongJae Park 
damon_sysfs_attrs_alloc(void)549c951cd3bSSeongJae Park static struct damon_sysfs_attrs *damon_sysfs_attrs_alloc(void)
550c951cd3bSSeongJae Park {
551c951cd3bSSeongJae Park 	struct damon_sysfs_attrs *attrs = kmalloc(sizeof(*attrs), GFP_KERNEL);
552c951cd3bSSeongJae Park 
553c951cd3bSSeongJae Park 	if (!attrs)
554c951cd3bSSeongJae Park 		return NULL;
555c951cd3bSSeongJae Park 	attrs->kobj = (struct kobject){};
556c951cd3bSSeongJae Park 	return attrs;
557c951cd3bSSeongJae Park }
558c951cd3bSSeongJae Park 
damon_sysfs_attrs_add_dirs(struct damon_sysfs_attrs * attrs)559c951cd3bSSeongJae Park static int damon_sysfs_attrs_add_dirs(struct damon_sysfs_attrs *attrs)
560c951cd3bSSeongJae Park {
561c951cd3bSSeongJae Park 	struct damon_sysfs_intervals *intervals;
562c951cd3bSSeongJae Park 	struct damon_sysfs_ul_range *nr_regions_range;
563c951cd3bSSeongJae Park 	int err;
564c951cd3bSSeongJae Park 
565c951cd3bSSeongJae Park 	intervals = damon_sysfs_intervals_alloc(5000, 100000, 60000000);
566c951cd3bSSeongJae Park 	if (!intervals)
567c951cd3bSSeongJae Park 		return -ENOMEM;
568c951cd3bSSeongJae Park 
569c951cd3bSSeongJae Park 	err = kobject_init_and_add(&intervals->kobj,
570c951cd3bSSeongJae Park 			&damon_sysfs_intervals_ktype, &attrs->kobj,
571c951cd3bSSeongJae Park 			"intervals");
572c951cd3bSSeongJae Park 	if (err)
573c951cd3bSSeongJae Park 		goto put_intervals_out;
574c951cd3bSSeongJae Park 	attrs->intervals = intervals;
575c951cd3bSSeongJae Park 
576c951cd3bSSeongJae Park 	nr_regions_range = damon_sysfs_ul_range_alloc(10, 1000);
577c951cd3bSSeongJae Park 	if (!nr_regions_range) {
578c951cd3bSSeongJae Park 		err = -ENOMEM;
579c951cd3bSSeongJae Park 		goto put_intervals_out;
580c951cd3bSSeongJae Park 	}
581c951cd3bSSeongJae Park 
582c951cd3bSSeongJae Park 	err = kobject_init_and_add(&nr_regions_range->kobj,
583c951cd3bSSeongJae Park 			&damon_sysfs_ul_range_ktype, &attrs->kobj,
584c951cd3bSSeongJae Park 			"nr_regions");
585c951cd3bSSeongJae Park 	if (err)
586c951cd3bSSeongJae Park 		goto put_nr_regions_intervals_out;
587c951cd3bSSeongJae Park 	attrs->nr_regions_range = nr_regions_range;
588c951cd3bSSeongJae Park 	return 0;
589c951cd3bSSeongJae Park 
590c951cd3bSSeongJae Park put_nr_regions_intervals_out:
591c951cd3bSSeongJae Park 	kobject_put(&nr_regions_range->kobj);
592c951cd3bSSeongJae Park 	attrs->nr_regions_range = NULL;
593c951cd3bSSeongJae Park put_intervals_out:
594c951cd3bSSeongJae Park 	kobject_put(&intervals->kobj);
595c951cd3bSSeongJae Park 	attrs->intervals = NULL;
596c951cd3bSSeongJae Park 	return err;
597c951cd3bSSeongJae Park }
598c951cd3bSSeongJae Park 
damon_sysfs_attrs_rm_dirs(struct damon_sysfs_attrs * attrs)599c951cd3bSSeongJae Park static void damon_sysfs_attrs_rm_dirs(struct damon_sysfs_attrs *attrs)
600c951cd3bSSeongJae Park {
601c951cd3bSSeongJae Park 	kobject_put(&attrs->nr_regions_range->kobj);
602c951cd3bSSeongJae Park 	kobject_put(&attrs->intervals->kobj);
603c951cd3bSSeongJae Park }
604c951cd3bSSeongJae Park 
damon_sysfs_attrs_release(struct kobject * kobj)605c951cd3bSSeongJae Park static void damon_sysfs_attrs_release(struct kobject *kobj)
606c951cd3bSSeongJae Park {
607c951cd3bSSeongJae Park 	kfree(container_of(kobj, struct damon_sysfs_attrs, kobj));
608c951cd3bSSeongJae Park }
609c951cd3bSSeongJae Park 
610c951cd3bSSeongJae Park static struct attribute *damon_sysfs_attrs_attrs[] = {
611c951cd3bSSeongJae Park 	NULL,
612c951cd3bSSeongJae Park };
613c951cd3bSSeongJae Park ATTRIBUTE_GROUPS(damon_sysfs_attrs);
614c951cd3bSSeongJae Park 
615e56397e8SThomas Weißschuh static const struct kobj_type damon_sysfs_attrs_ktype = {
616c951cd3bSSeongJae Park 	.release = damon_sysfs_attrs_release,
617c951cd3bSSeongJae Park 	.sysfs_ops = &kobj_sysfs_ops,
618c951cd3bSSeongJae Park 	.default_groups = damon_sysfs_attrs_groups,
619c951cd3bSSeongJae Park };
620c951cd3bSSeongJae Park 
621c951cd3bSSeongJae Park /*
622c951cd3bSSeongJae Park  * context directory
623c951cd3bSSeongJae Park  */
624c951cd3bSSeongJae Park 
625c951cd3bSSeongJae Park /* This should match with enum damon_ops_id */
626c951cd3bSSeongJae Park static const char * const damon_sysfs_ops_strs[] = {
627c951cd3bSSeongJae Park 	"vaddr",
628b8243447SSeongJae Park 	"fvaddr",
629c951cd3bSSeongJae Park 	"paddr",
630c951cd3bSSeongJae Park };
631c951cd3bSSeongJae Park 
632c951cd3bSSeongJae Park struct damon_sysfs_context {
633c951cd3bSSeongJae Park 	struct kobject kobj;
634c951cd3bSSeongJae Park 	enum damon_ops_id ops_id;
635c951cd3bSSeongJae Park 	struct damon_sysfs_attrs *attrs;
636c951cd3bSSeongJae Park 	struct damon_sysfs_targets *targets;
6377e84b1f8SSeongJae Park 	struct damon_sysfs_schemes *schemes;
638c951cd3bSSeongJae Park };
639c951cd3bSSeongJae Park 
damon_sysfs_context_alloc(enum damon_ops_id ops_id)640c951cd3bSSeongJae Park static struct damon_sysfs_context *damon_sysfs_context_alloc(
641c951cd3bSSeongJae Park 		enum damon_ops_id ops_id)
642c951cd3bSSeongJae Park {
643c951cd3bSSeongJae Park 	struct damon_sysfs_context *context = kmalloc(sizeof(*context),
644c951cd3bSSeongJae Park 				GFP_KERNEL);
645c951cd3bSSeongJae Park 
646c951cd3bSSeongJae Park 	if (!context)
647c951cd3bSSeongJae Park 		return NULL;
648c951cd3bSSeongJae Park 	context->kobj = (struct kobject){};
649c951cd3bSSeongJae Park 	context->ops_id = ops_id;
650c951cd3bSSeongJae Park 	return context;
651c951cd3bSSeongJae Park }
652c951cd3bSSeongJae Park 
damon_sysfs_context_set_attrs(struct damon_sysfs_context * context)653c951cd3bSSeongJae Park static int damon_sysfs_context_set_attrs(struct damon_sysfs_context *context)
654c951cd3bSSeongJae Park {
655c951cd3bSSeongJae Park 	struct damon_sysfs_attrs *attrs = damon_sysfs_attrs_alloc();
656c951cd3bSSeongJae Park 	int err;
657c951cd3bSSeongJae Park 
658c951cd3bSSeongJae Park 	if (!attrs)
659c951cd3bSSeongJae Park 		return -ENOMEM;
660c951cd3bSSeongJae Park 	err = kobject_init_and_add(&attrs->kobj, &damon_sysfs_attrs_ktype,
661c951cd3bSSeongJae Park 			&context->kobj, "monitoring_attrs");
662c951cd3bSSeongJae Park 	if (err)
663c951cd3bSSeongJae Park 		goto out;
664c951cd3bSSeongJae Park 	err = damon_sysfs_attrs_add_dirs(attrs);
665c951cd3bSSeongJae Park 	if (err)
666c951cd3bSSeongJae Park 		goto out;
667c951cd3bSSeongJae Park 	context->attrs = attrs;
668c951cd3bSSeongJae Park 	return 0;
669c951cd3bSSeongJae Park 
670c951cd3bSSeongJae Park out:
671c951cd3bSSeongJae Park 	kobject_put(&attrs->kobj);
672c951cd3bSSeongJae Park 	return err;
673c951cd3bSSeongJae Park }
674c951cd3bSSeongJae Park 
damon_sysfs_context_set_targets(struct damon_sysfs_context * context)675c951cd3bSSeongJae Park static int damon_sysfs_context_set_targets(struct damon_sysfs_context *context)
676c951cd3bSSeongJae Park {
677c951cd3bSSeongJae Park 	struct damon_sysfs_targets *targets = damon_sysfs_targets_alloc();
678c951cd3bSSeongJae Park 	int err;
679c951cd3bSSeongJae Park 
680c951cd3bSSeongJae Park 	if (!targets)
681c951cd3bSSeongJae Park 		return -ENOMEM;
682c951cd3bSSeongJae Park 	err = kobject_init_and_add(&targets->kobj, &damon_sysfs_targets_ktype,
683c951cd3bSSeongJae Park 			&context->kobj, "targets");
684c951cd3bSSeongJae Park 	if (err) {
685c951cd3bSSeongJae Park 		kobject_put(&targets->kobj);
686c951cd3bSSeongJae Park 		return err;
687c951cd3bSSeongJae Park 	}
688c951cd3bSSeongJae Park 	context->targets = targets;
689c951cd3bSSeongJae Park 	return 0;
690c951cd3bSSeongJae Park }
691c951cd3bSSeongJae Park 
damon_sysfs_context_set_schemes(struct damon_sysfs_context * context)6927e84b1f8SSeongJae Park static int damon_sysfs_context_set_schemes(struct damon_sysfs_context *context)
6937e84b1f8SSeongJae Park {
6947e84b1f8SSeongJae Park 	struct damon_sysfs_schemes *schemes = damon_sysfs_schemes_alloc();
6957e84b1f8SSeongJae Park 	int err;
6967e84b1f8SSeongJae Park 
6977e84b1f8SSeongJae Park 	if (!schemes)
6987e84b1f8SSeongJae Park 		return -ENOMEM;
6997e84b1f8SSeongJae Park 	err = kobject_init_and_add(&schemes->kobj, &damon_sysfs_schemes_ktype,
7007e84b1f8SSeongJae Park 			&context->kobj, "schemes");
7017e84b1f8SSeongJae Park 	if (err) {
7027e84b1f8SSeongJae Park 		kobject_put(&schemes->kobj);
7037e84b1f8SSeongJae Park 		return err;
7047e84b1f8SSeongJae Park 	}
7057e84b1f8SSeongJae Park 	context->schemes = schemes;
7067e84b1f8SSeongJae Park 	return 0;
7077e84b1f8SSeongJae Park }
7087e84b1f8SSeongJae Park 
damon_sysfs_context_add_dirs(struct damon_sysfs_context * context)709c951cd3bSSeongJae Park static int damon_sysfs_context_add_dirs(struct damon_sysfs_context *context)
710c951cd3bSSeongJae Park {
711c951cd3bSSeongJae Park 	int err;
712c951cd3bSSeongJae Park 
713c951cd3bSSeongJae Park 	err = damon_sysfs_context_set_attrs(context);
714c951cd3bSSeongJae Park 	if (err)
715c951cd3bSSeongJae Park 		return err;
716c951cd3bSSeongJae Park 
717c951cd3bSSeongJae Park 	err = damon_sysfs_context_set_targets(context);
718c951cd3bSSeongJae Park 	if (err)
719c951cd3bSSeongJae Park 		goto put_attrs_out;
7207e84b1f8SSeongJae Park 
7217e84b1f8SSeongJae Park 	err = damon_sysfs_context_set_schemes(context);
7227e84b1f8SSeongJae Park 	if (err)
7237e84b1f8SSeongJae Park 		goto put_targets_attrs_out;
724c951cd3bSSeongJae Park 	return 0;
725c951cd3bSSeongJae Park 
7267e84b1f8SSeongJae Park put_targets_attrs_out:
7277e84b1f8SSeongJae Park 	kobject_put(&context->targets->kobj);
7287e84b1f8SSeongJae Park 	context->targets = NULL;
729c951cd3bSSeongJae Park put_attrs_out:
730c951cd3bSSeongJae Park 	kobject_put(&context->attrs->kobj);
731c951cd3bSSeongJae Park 	context->attrs = NULL;
732c951cd3bSSeongJae Park 	return err;
733c951cd3bSSeongJae Park }
734c951cd3bSSeongJae Park 
damon_sysfs_context_rm_dirs(struct damon_sysfs_context * context)735c951cd3bSSeongJae Park static void damon_sysfs_context_rm_dirs(struct damon_sysfs_context *context)
736c951cd3bSSeongJae Park {
737c951cd3bSSeongJae Park 	damon_sysfs_attrs_rm_dirs(context->attrs);
738c951cd3bSSeongJae Park 	kobject_put(&context->attrs->kobj);
739c951cd3bSSeongJae Park 	damon_sysfs_targets_rm_dirs(context->targets);
740c951cd3bSSeongJae Park 	kobject_put(&context->targets->kobj);
7417e84b1f8SSeongJae Park 	damon_sysfs_schemes_rm_dirs(context->schemes);
7427e84b1f8SSeongJae Park 	kobject_put(&context->schemes->kobj);
743c951cd3bSSeongJae Park }
744c951cd3bSSeongJae Park 
avail_operations_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)7450f2cb588SSeongJae Park static ssize_t avail_operations_show(struct kobject *kobj,
7460f2cb588SSeongJae Park 		struct kobj_attribute *attr, char *buf)
7470f2cb588SSeongJae Park {
7480f2cb588SSeongJae Park 	enum damon_ops_id id;
7490f2cb588SSeongJae Park 	int len = 0;
7500f2cb588SSeongJae Park 
7510f2cb588SSeongJae Park 	for (id = 0; id < NR_DAMON_OPS; id++) {
7520f2cb588SSeongJae Park 		if (!damon_is_registered_ops(id))
7530f2cb588SSeongJae Park 			continue;
7540f2cb588SSeongJae Park 		len += sysfs_emit_at(buf, len, "%s\n",
7550f2cb588SSeongJae Park 				damon_sysfs_ops_strs[id]);
7560f2cb588SSeongJae Park 	}
7570f2cb588SSeongJae Park 	return len;
7580f2cb588SSeongJae Park }
7590f2cb588SSeongJae Park 
operations_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)760c951cd3bSSeongJae Park static ssize_t operations_show(struct kobject *kobj,
761c951cd3bSSeongJae Park 		struct kobj_attribute *attr, char *buf)
762c951cd3bSSeongJae Park {
763c951cd3bSSeongJae Park 	struct damon_sysfs_context *context = container_of(kobj,
764c951cd3bSSeongJae Park 			struct damon_sysfs_context, kobj);
765c951cd3bSSeongJae Park 
766c951cd3bSSeongJae Park 	return sysfs_emit(buf, "%s\n", damon_sysfs_ops_strs[context->ops_id]);
767c951cd3bSSeongJae Park }
768c951cd3bSSeongJae Park 
operations_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)769c951cd3bSSeongJae Park static ssize_t operations_store(struct kobject *kobj,
770c951cd3bSSeongJae Park 		struct kobj_attribute *attr, const char *buf, size_t count)
771c951cd3bSSeongJae Park {
772c951cd3bSSeongJae Park 	struct damon_sysfs_context *context = container_of(kobj,
773c951cd3bSSeongJae Park 			struct damon_sysfs_context, kobj);
774c951cd3bSSeongJae Park 	enum damon_ops_id id;
775c951cd3bSSeongJae Park 
776c951cd3bSSeongJae Park 	for (id = 0; id < NR_DAMON_OPS; id++) {
777c951cd3bSSeongJae Park 		if (sysfs_streq(buf, damon_sysfs_ops_strs[id])) {
778c951cd3bSSeongJae Park 			context->ops_id = id;
779c951cd3bSSeongJae Park 			return count;
780c951cd3bSSeongJae Park 		}
781c951cd3bSSeongJae Park 	}
782c951cd3bSSeongJae Park 	return -EINVAL;
783c951cd3bSSeongJae Park }
784c951cd3bSSeongJae Park 
damon_sysfs_context_release(struct kobject * kobj)785c951cd3bSSeongJae Park static void damon_sysfs_context_release(struct kobject *kobj)
786c951cd3bSSeongJae Park {
787c951cd3bSSeongJae Park 	kfree(container_of(kobj, struct damon_sysfs_context, kobj));
788c951cd3bSSeongJae Park }
789c951cd3bSSeongJae Park 
7900f2cb588SSeongJae Park static struct kobj_attribute damon_sysfs_context_avail_operations_attr =
7910f2cb588SSeongJae Park 		__ATTR_RO_MODE(avail_operations, 0400);
7920f2cb588SSeongJae Park 
793c951cd3bSSeongJae Park static struct kobj_attribute damon_sysfs_context_operations_attr =
794c951cd3bSSeongJae Park 		__ATTR_RW_MODE(operations, 0600);
795c951cd3bSSeongJae Park 
796c951cd3bSSeongJae Park static struct attribute *damon_sysfs_context_attrs[] = {
7970f2cb588SSeongJae Park 	&damon_sysfs_context_avail_operations_attr.attr,
798c951cd3bSSeongJae Park 	&damon_sysfs_context_operations_attr.attr,
799c951cd3bSSeongJae Park 	NULL,
800c951cd3bSSeongJae Park };
801c951cd3bSSeongJae Park ATTRIBUTE_GROUPS(damon_sysfs_context);
802c951cd3bSSeongJae Park 
803e56397e8SThomas Weißschuh static const struct kobj_type damon_sysfs_context_ktype = {
804c951cd3bSSeongJae Park 	.release = damon_sysfs_context_release,
805c951cd3bSSeongJae Park 	.sysfs_ops = &kobj_sysfs_ops,
806c951cd3bSSeongJae Park 	.default_groups = damon_sysfs_context_groups,
807c951cd3bSSeongJae Park };
808c951cd3bSSeongJae Park 
809c951cd3bSSeongJae Park /*
810c951cd3bSSeongJae Park  * contexts directory
811c951cd3bSSeongJae Park  */
812c951cd3bSSeongJae Park 
813c951cd3bSSeongJae Park struct damon_sysfs_contexts {
814c951cd3bSSeongJae Park 	struct kobject kobj;
815c951cd3bSSeongJae Park 	struct damon_sysfs_context **contexts_arr;
816c951cd3bSSeongJae Park 	int nr;
817c951cd3bSSeongJae Park };
818c951cd3bSSeongJae Park 
damon_sysfs_contexts_alloc(void)819c951cd3bSSeongJae Park static struct damon_sysfs_contexts *damon_sysfs_contexts_alloc(void)
820c951cd3bSSeongJae Park {
821c951cd3bSSeongJae Park 	return kzalloc(sizeof(struct damon_sysfs_contexts), GFP_KERNEL);
822c951cd3bSSeongJae Park }
823c951cd3bSSeongJae Park 
damon_sysfs_contexts_rm_dirs(struct damon_sysfs_contexts * contexts)824c951cd3bSSeongJae Park static void damon_sysfs_contexts_rm_dirs(struct damon_sysfs_contexts *contexts)
825c951cd3bSSeongJae Park {
826c951cd3bSSeongJae Park 	struct damon_sysfs_context **contexts_arr = contexts->contexts_arr;
827c951cd3bSSeongJae Park 	int i;
828c951cd3bSSeongJae Park 
829c951cd3bSSeongJae Park 	for (i = 0; i < contexts->nr; i++) {
830c951cd3bSSeongJae Park 		damon_sysfs_context_rm_dirs(contexts_arr[i]);
831c951cd3bSSeongJae Park 		kobject_put(&contexts_arr[i]->kobj);
832c951cd3bSSeongJae Park 	}
833c951cd3bSSeongJae Park 	contexts->nr = 0;
834c951cd3bSSeongJae Park 	kfree(contexts_arr);
835c951cd3bSSeongJae Park 	contexts->contexts_arr = NULL;
836c951cd3bSSeongJae Park }
837c951cd3bSSeongJae Park 
damon_sysfs_contexts_add_dirs(struct damon_sysfs_contexts * contexts,int nr_contexts)838c951cd3bSSeongJae Park static int damon_sysfs_contexts_add_dirs(struct damon_sysfs_contexts *contexts,
839c951cd3bSSeongJae Park 		int nr_contexts)
840c951cd3bSSeongJae Park {
841c951cd3bSSeongJae Park 	struct damon_sysfs_context **contexts_arr, *context;
842c951cd3bSSeongJae Park 	int err, i;
843c951cd3bSSeongJae Park 
844c951cd3bSSeongJae Park 	damon_sysfs_contexts_rm_dirs(contexts);
845c951cd3bSSeongJae Park 	if (!nr_contexts)
846c951cd3bSSeongJae Park 		return 0;
847c951cd3bSSeongJae Park 
848c951cd3bSSeongJae Park 	contexts_arr = kmalloc_array(nr_contexts, sizeof(*contexts_arr),
849c951cd3bSSeongJae Park 			GFP_KERNEL | __GFP_NOWARN);
850c951cd3bSSeongJae Park 	if (!contexts_arr)
851c951cd3bSSeongJae Park 		return -ENOMEM;
852c951cd3bSSeongJae Park 	contexts->contexts_arr = contexts_arr;
853c951cd3bSSeongJae Park 
854c951cd3bSSeongJae Park 	for (i = 0; i < nr_contexts; i++) {
855c951cd3bSSeongJae Park 		context = damon_sysfs_context_alloc(DAMON_OPS_VADDR);
856c951cd3bSSeongJae Park 		if (!context) {
857c951cd3bSSeongJae Park 			damon_sysfs_contexts_rm_dirs(contexts);
858c951cd3bSSeongJae Park 			return -ENOMEM;
859c951cd3bSSeongJae Park 		}
860c951cd3bSSeongJae Park 
861c951cd3bSSeongJae Park 		err = kobject_init_and_add(&context->kobj,
862c951cd3bSSeongJae Park 				&damon_sysfs_context_ktype, &contexts->kobj,
863c951cd3bSSeongJae Park 				"%d", i);
864c951cd3bSSeongJae Park 		if (err)
865c951cd3bSSeongJae Park 			goto out;
866c951cd3bSSeongJae Park 
867c951cd3bSSeongJae Park 		err = damon_sysfs_context_add_dirs(context);
868c951cd3bSSeongJae Park 		if (err)
869c951cd3bSSeongJae Park 			goto out;
870c951cd3bSSeongJae Park 
871c951cd3bSSeongJae Park 		contexts_arr[i] = context;
872c951cd3bSSeongJae Park 		contexts->nr++;
873c951cd3bSSeongJae Park 	}
874c951cd3bSSeongJae Park 	return 0;
875c951cd3bSSeongJae Park 
876c951cd3bSSeongJae Park out:
877c951cd3bSSeongJae Park 	damon_sysfs_contexts_rm_dirs(contexts);
878c951cd3bSSeongJae Park 	kobject_put(&context->kobj);
879c951cd3bSSeongJae Park 	return err;
880c951cd3bSSeongJae Park }
881c951cd3bSSeongJae Park 
nr_contexts_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)882c951cd3bSSeongJae Park static ssize_t nr_contexts_show(struct kobject *kobj,
883c951cd3bSSeongJae Park 		struct kobj_attribute *attr, char *buf)
884c951cd3bSSeongJae Park {
885c951cd3bSSeongJae Park 	struct damon_sysfs_contexts *contexts = container_of(kobj,
886c951cd3bSSeongJae Park 			struct damon_sysfs_contexts, kobj);
887c951cd3bSSeongJae Park 
888c951cd3bSSeongJae Park 	return sysfs_emit(buf, "%d\n", contexts->nr);
889c951cd3bSSeongJae Park }
890c951cd3bSSeongJae Park 
nr_contexts_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)891c951cd3bSSeongJae Park static ssize_t nr_contexts_store(struct kobject *kobj,
892c951cd3bSSeongJae Park 		struct kobj_attribute *attr, const char *buf, size_t count)
893c951cd3bSSeongJae Park {
894a17a8b3bSXin Hao 	struct damon_sysfs_contexts *contexts;
895c951cd3bSSeongJae Park 	int nr, err;
896c951cd3bSSeongJae Park 
897c951cd3bSSeongJae Park 	err = kstrtoint(buf, 0, &nr);
898c951cd3bSSeongJae Park 	if (err)
899c951cd3bSSeongJae Park 		return err;
900c951cd3bSSeongJae Park 	/* TODO: support multiple contexts per kdamond */
901c951cd3bSSeongJae Park 	if (nr < 0 || 1 < nr)
902c951cd3bSSeongJae Park 		return -EINVAL;
903c951cd3bSSeongJae Park 
904a17a8b3bSXin Hao 	contexts = container_of(kobj, struct damon_sysfs_contexts, kobj);
905c951cd3bSSeongJae Park 	if (!mutex_trylock(&damon_sysfs_lock))
906c951cd3bSSeongJae Park 		return -EBUSY;
907c951cd3bSSeongJae Park 	err = damon_sysfs_contexts_add_dirs(contexts, nr);
908c951cd3bSSeongJae Park 	mutex_unlock(&damon_sysfs_lock);
909c951cd3bSSeongJae Park 	if (err)
910c951cd3bSSeongJae Park 		return err;
911c951cd3bSSeongJae Park 
912c951cd3bSSeongJae Park 	return count;
913c951cd3bSSeongJae Park }
914c951cd3bSSeongJae Park 
damon_sysfs_contexts_release(struct kobject * kobj)915c951cd3bSSeongJae Park static void damon_sysfs_contexts_release(struct kobject *kobj)
916c951cd3bSSeongJae Park {
917c951cd3bSSeongJae Park 	kfree(container_of(kobj, struct damon_sysfs_contexts, kobj));
918c951cd3bSSeongJae Park }
919c951cd3bSSeongJae Park 
920c951cd3bSSeongJae Park static struct kobj_attribute damon_sysfs_contexts_nr_attr
921c951cd3bSSeongJae Park 		= __ATTR_RW_MODE(nr_contexts, 0600);
922c951cd3bSSeongJae Park 
923c951cd3bSSeongJae Park static struct attribute *damon_sysfs_contexts_attrs[] = {
924c951cd3bSSeongJae Park 	&damon_sysfs_contexts_nr_attr.attr,
925c951cd3bSSeongJae Park 	NULL,
926c951cd3bSSeongJae Park };
927c951cd3bSSeongJae Park ATTRIBUTE_GROUPS(damon_sysfs_contexts);
928c951cd3bSSeongJae Park 
929e56397e8SThomas Weißschuh static const struct kobj_type damon_sysfs_contexts_ktype = {
930c951cd3bSSeongJae Park 	.release = damon_sysfs_contexts_release,
931c951cd3bSSeongJae Park 	.sysfs_ops = &kobj_sysfs_ops,
932c951cd3bSSeongJae Park 	.default_groups = damon_sysfs_contexts_groups,
933c951cd3bSSeongJae Park };
934c951cd3bSSeongJae Park 
935c951cd3bSSeongJae Park /*
936c951cd3bSSeongJae Park  * kdamond directory
937c951cd3bSSeongJae Park  */
938c951cd3bSSeongJae Park 
939c951cd3bSSeongJae Park struct damon_sysfs_kdamond {
940c951cd3bSSeongJae Park 	struct kobject kobj;
941c951cd3bSSeongJae Park 	struct damon_sysfs_contexts *contexts;
942c951cd3bSSeongJae Park 	struct damon_ctx *damon_ctx;
943c951cd3bSSeongJae Park };
944c951cd3bSSeongJae Park 
damon_sysfs_kdamond_alloc(void)945c951cd3bSSeongJae Park static struct damon_sysfs_kdamond *damon_sysfs_kdamond_alloc(void)
946c951cd3bSSeongJae Park {
947c951cd3bSSeongJae Park 	return kzalloc(sizeof(struct damon_sysfs_kdamond), GFP_KERNEL);
948c951cd3bSSeongJae Park }
949c951cd3bSSeongJae Park 
damon_sysfs_kdamond_add_dirs(struct damon_sysfs_kdamond * kdamond)950c951cd3bSSeongJae Park static int damon_sysfs_kdamond_add_dirs(struct damon_sysfs_kdamond *kdamond)
951c951cd3bSSeongJae Park {
952c951cd3bSSeongJae Park 	struct damon_sysfs_contexts *contexts;
953c951cd3bSSeongJae Park 	int err;
954c951cd3bSSeongJae Park 
955c951cd3bSSeongJae Park 	contexts = damon_sysfs_contexts_alloc();
956c951cd3bSSeongJae Park 	if (!contexts)
957c951cd3bSSeongJae Park 		return -ENOMEM;
958c951cd3bSSeongJae Park 
959c951cd3bSSeongJae Park 	err = kobject_init_and_add(&contexts->kobj,
960c951cd3bSSeongJae Park 			&damon_sysfs_contexts_ktype, &kdamond->kobj,
961c951cd3bSSeongJae Park 			"contexts");
962c951cd3bSSeongJae Park 	if (err) {
963c951cd3bSSeongJae Park 		kobject_put(&contexts->kobj);
964c951cd3bSSeongJae Park 		return err;
965c951cd3bSSeongJae Park 	}
966c951cd3bSSeongJae Park 	kdamond->contexts = contexts;
967c951cd3bSSeongJae Park 
968c951cd3bSSeongJae Park 	return err;
969c951cd3bSSeongJae Park }
970c951cd3bSSeongJae Park 
damon_sysfs_kdamond_rm_dirs(struct damon_sysfs_kdamond * kdamond)971c951cd3bSSeongJae Park static void damon_sysfs_kdamond_rm_dirs(struct damon_sysfs_kdamond *kdamond)
972c951cd3bSSeongJae Park {
973c951cd3bSSeongJae Park 	damon_sysfs_contexts_rm_dirs(kdamond->contexts);
974c951cd3bSSeongJae Park 	kobject_put(&kdamond->contexts->kobj);
975c951cd3bSSeongJae Park }
976c951cd3bSSeongJae Park 
damon_sysfs_ctx_running(struct damon_ctx * ctx)977a61ea561SSeongJae Park static bool damon_sysfs_ctx_running(struct damon_ctx *ctx)
978a61ea561SSeongJae Park {
979a61ea561SSeongJae Park 	bool running;
980a61ea561SSeongJae Park 
981a61ea561SSeongJae Park 	mutex_lock(&ctx->kdamond_lock);
982a61ea561SSeongJae Park 	running = ctx->kdamond != NULL;
983a61ea561SSeongJae Park 	mutex_unlock(&ctx->kdamond_lock);
984a61ea561SSeongJae Park 	return running;
985a61ea561SSeongJae Park }
986a61ea561SSeongJae Park 
9873cbab4caSSeongJae Park /*
9883cbab4caSSeongJae Park  * enum damon_sysfs_cmd - Commands for a specific kdamond.
9893cbab4caSSeongJae Park  */
9903cbab4caSSeongJae Park enum damon_sysfs_cmd {
9913cbab4caSSeongJae Park 	/* @DAMON_SYSFS_CMD_ON: Turn the kdamond on. */
9923cbab4caSSeongJae Park 	DAMON_SYSFS_CMD_ON,
9933cbab4caSSeongJae Park 	/* @DAMON_SYSFS_CMD_OFF: Turn the kdamond off. */
9943cbab4caSSeongJae Park 	DAMON_SYSFS_CMD_OFF,
995da878780SSeongJae Park 	/* @DAMON_SYSFS_CMD_COMMIT: Update kdamond inputs. */
996da878780SSeongJae Park 	DAMON_SYSFS_CMD_COMMIT,
9973cbab4caSSeongJae Park 	/*
9983cbab4caSSeongJae Park 	 * @DAMON_SYSFS_CMD_UPDATE_SCHEMES_STATS: Update scheme stats sysfs
9993cbab4caSSeongJae Park 	 * files.
10003cbab4caSSeongJae Park 	 */
10013cbab4caSSeongJae Park 	DAMON_SYSFS_CMD_UPDATE_SCHEMES_STATS,
10023cbab4caSSeongJae Park 	/*
10036ad243b8SSeongJae Park 	 * @DAMON_SYSFS_CMD_UPDATE_SCHEMES_TRIED_BYTES: Update
10046ad243b8SSeongJae Park 	 * tried_regions/total_bytes sysfs files for each scheme.
10056ad243b8SSeongJae Park 	 */
10066ad243b8SSeongJae Park 	DAMON_SYSFS_CMD_UPDATE_SCHEMES_TRIED_BYTES,
10076ad243b8SSeongJae Park 	/*
1008f1d13cacSSeongJae Park 	 * @DAMON_SYSFS_CMD_UPDATE_SCHEMES_TRIED_REGIONS: Update schemes tried
1009f1d13cacSSeongJae Park 	 * regions
1010f1d13cacSSeongJae Park 	 */
1011f1d13cacSSeongJae Park 	DAMON_SYSFS_CMD_UPDATE_SCHEMES_TRIED_REGIONS,
1012f1d13cacSSeongJae Park 	/*
1013772c15e5SSeongJae Park 	 * @DAMON_SYSFS_CMD_CLEAR_SCHEMES_TRIED_REGIONS: Clear schemes tried
1014772c15e5SSeongJae Park 	 * regions
1015772c15e5SSeongJae Park 	 */
1016772c15e5SSeongJae Park 	DAMON_SYSFS_CMD_CLEAR_SCHEMES_TRIED_REGIONS,
1017772c15e5SSeongJae Park 	/*
10183cbab4caSSeongJae Park 	 * @NR_DAMON_SYSFS_CMDS: Total number of DAMON sysfs commands.
10193cbab4caSSeongJae Park 	 */
10203cbab4caSSeongJae Park 	NR_DAMON_SYSFS_CMDS,
10213cbab4caSSeongJae Park };
10223cbab4caSSeongJae Park 
10233cbab4caSSeongJae Park /* Should match with enum damon_sysfs_cmd */
10243cbab4caSSeongJae Park static const char * const damon_sysfs_cmd_strs[] = {
10253cbab4caSSeongJae Park 	"on",
10263cbab4caSSeongJae Park 	"off",
1027da878780SSeongJae Park 	"commit",
10283cbab4caSSeongJae Park 	"update_schemes_stats",
10296ad243b8SSeongJae Park 	"update_schemes_tried_bytes",
1030f1d13cacSSeongJae Park 	"update_schemes_tried_regions",
1031772c15e5SSeongJae Park 	"clear_schemes_tried_regions",
10323cbab4caSSeongJae Park };
10333cbab4caSSeongJae Park 
103401538719SSeongJae Park /*
103501538719SSeongJae Park  * struct damon_sysfs_cmd_request - A request to the DAMON callback.
103601538719SSeongJae Park  * @cmd:	The command that needs to be handled by the callback.
103701538719SSeongJae Park  * @kdamond:	The kobject wrapper that associated to the kdamond thread.
103801538719SSeongJae Park  *
103901538719SSeongJae Park  * This structure represents a sysfs command request that need to access some
104001538719SSeongJae Park  * DAMON context-internal data.  Because DAMON context-internal data can be
104101538719SSeongJae Park  * safely accessed from DAMON callbacks without additional synchronization, the
104201538719SSeongJae Park  * request will be handled by the DAMON callback.  None-``NULL`` @kdamond means
104301538719SSeongJae Park  * the request is valid.
104401538719SSeongJae Park  */
104501538719SSeongJae Park struct damon_sysfs_cmd_request {
104601538719SSeongJae Park 	enum damon_sysfs_cmd cmd;
104701538719SSeongJae Park 	struct damon_sysfs_kdamond *kdamond;
104801538719SSeongJae Park };
104901538719SSeongJae Park 
105001538719SSeongJae Park /* Current DAMON callback request.  Protected by damon_sysfs_lock. */
105101538719SSeongJae Park static struct damon_sysfs_cmd_request damon_sysfs_cmd_request;
105201538719SSeongJae Park 
state_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)1053c951cd3bSSeongJae Park static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr,
1054c951cd3bSSeongJae Park 		char *buf)
1055c951cd3bSSeongJae Park {
1056a61ea561SSeongJae Park 	struct damon_sysfs_kdamond *kdamond = container_of(kobj,
1057a61ea561SSeongJae Park 			struct damon_sysfs_kdamond, kobj);
1058a61ea561SSeongJae Park 	struct damon_ctx *ctx = kdamond->damon_ctx;
1059a61ea561SSeongJae Park 	bool running;
1060a61ea561SSeongJae Park 
1061a61ea561SSeongJae Park 	if (!ctx)
1062a61ea561SSeongJae Park 		running = false;
1063a61ea561SSeongJae Park 	else
1064a61ea561SSeongJae Park 		running = damon_sysfs_ctx_running(ctx);
1065a61ea561SSeongJae Park 
10663cbab4caSSeongJae Park 	return sysfs_emit(buf, "%s\n", running ?
10673cbab4caSSeongJae Park 			damon_sysfs_cmd_strs[DAMON_SYSFS_CMD_ON] :
10683cbab4caSSeongJae Park 			damon_sysfs_cmd_strs[DAMON_SYSFS_CMD_OFF]);
1069a61ea561SSeongJae Park }
1070a61ea561SSeongJae Park 
damon_sysfs_set_attrs(struct damon_ctx * ctx,struct damon_sysfs_attrs * sys_attrs)1071a61ea561SSeongJae Park static int damon_sysfs_set_attrs(struct damon_ctx *ctx,
1072a61ea561SSeongJae Park 		struct damon_sysfs_attrs *sys_attrs)
1073a61ea561SSeongJae Park {
1074a61ea561SSeongJae Park 	struct damon_sysfs_intervals *sys_intervals = sys_attrs->intervals;
1075a61ea561SSeongJae Park 	struct damon_sysfs_ul_range *sys_nr_regions =
1076a61ea561SSeongJae Park 		sys_attrs->nr_regions_range;
1077bead3b00SSeongJae Park 	struct damon_attrs attrs = {
1078bead3b00SSeongJae Park 		.sample_interval = sys_intervals->sample_us,
1079bead3b00SSeongJae Park 		.aggr_interval = sys_intervals->aggr_us,
1080bead3b00SSeongJae Park 		.ops_update_interval = sys_intervals->update_us,
1081bead3b00SSeongJae Park 		.min_nr_regions = sys_nr_regions->min,
1082bead3b00SSeongJae Park 		.max_nr_regions = sys_nr_regions->max,
1083bead3b00SSeongJae Park 	};
1084bead3b00SSeongJae Park 	return damon_set_attrs(ctx, &attrs);
1085a61ea561SSeongJae Park }
1086a61ea561SSeongJae Park 
damon_sysfs_destroy_targets(struct damon_ctx * ctx)1087a61ea561SSeongJae Park static void damon_sysfs_destroy_targets(struct damon_ctx *ctx)
1088a61ea561SSeongJae Park {
1089a61ea561SSeongJae Park 	struct damon_target *t, *next;
1090871f697bSXin Hao 	bool has_pid = damon_target_has_pid(ctx);
1091a61ea561SSeongJae Park 
1092a61ea561SSeongJae Park 	damon_for_each_target_safe(t, next, ctx) {
1093871f697bSXin Hao 		if (has_pid)
1094a61ea561SSeongJae Park 			put_pid(t->pid);
1095a61ea561SSeongJae Park 		damon_destroy_target(t);
1096a61ea561SSeongJae Park 	}
1097a61ea561SSeongJae Park }
1098a61ea561SSeongJae Park 
damon_sysfs_set_regions(struct damon_target * t,struct damon_sysfs_regions * sysfs_regions)10992031b14eSSeongJae Park static int damon_sysfs_set_regions(struct damon_target *t,
11002031b14eSSeongJae Park 		struct damon_sysfs_regions *sysfs_regions)
11012031b14eSSeongJae Park {
110297d482f4SSeongJae Park 	struct damon_addr_range *ranges = kmalloc_array(sysfs_regions->nr,
110397d482f4SSeongJae Park 			sizeof(*ranges), GFP_KERNEL | __GFP_NOWARN);
110497d482f4SSeongJae Park 	int i, err = -EINVAL;
11052031b14eSSeongJae Park 
110697d482f4SSeongJae Park 	if (!ranges)
110797d482f4SSeongJae Park 		return -ENOMEM;
11082031b14eSSeongJae Park 	for (i = 0; i < sysfs_regions->nr; i++) {
11092031b14eSSeongJae Park 		struct damon_sysfs_region *sys_region =
11102031b14eSSeongJae Park 			sysfs_regions->regions_arr[i];
11112031b14eSSeongJae Park 
1112789a2306SSeongJae Park 		if (sys_region->ar.start > sys_region->ar.end)
111397d482f4SSeongJae Park 			goto out;
111497d482f4SSeongJae Park 
1115789a2306SSeongJae Park 		ranges[i].start = sys_region->ar.start;
1116789a2306SSeongJae Park 		ranges[i].end = sys_region->ar.end;
111797d482f4SSeongJae Park 		if (i == 0)
111897d482f4SSeongJae Park 			continue;
111997d482f4SSeongJae Park 		if (ranges[i - 1].end > ranges[i].start)
112097d482f4SSeongJae Park 			goto out;
11212031b14eSSeongJae Park 	}
112297d482f4SSeongJae Park 	err = damon_set_regions(t, ranges, sysfs_regions->nr);
112397d482f4SSeongJae Park out:
112497d482f4SSeongJae Park 	kfree(ranges);
112597d482f4SSeongJae Park 	return err;
112697d482f4SSeongJae Park 
11272031b14eSSeongJae Park }
11282031b14eSSeongJae Park 
damon_sysfs_add_target(struct damon_sysfs_target * sys_target,struct damon_ctx * ctx)112974bd8b7dSSeongJae Park static int damon_sysfs_add_target(struct damon_sysfs_target *sys_target,
113074bd8b7dSSeongJae Park 		struct damon_ctx *ctx)
113174bd8b7dSSeongJae Park {
113274bd8b7dSSeongJae Park 	struct damon_target *t = damon_new_target();
113374bd8b7dSSeongJae Park 	int err = -EINVAL;
113474bd8b7dSSeongJae Park 
113574bd8b7dSSeongJae Park 	if (!t)
113674bd8b7dSSeongJae Park 		return -ENOMEM;
11371c8e2349SLevi Yun 	damon_add_target(ctx, t);
1138c9e124e0SSeongJae Park 	if (damon_target_has_pid(ctx)) {
113974bd8b7dSSeongJae Park 		t->pid = find_get_pid(sys_target->pid);
114074bd8b7dSSeongJae Park 		if (!t->pid)
114174bd8b7dSSeongJae Park 			goto destroy_targets_out;
114274bd8b7dSSeongJae Park 	}
114374bd8b7dSSeongJae Park 	err = damon_sysfs_set_regions(t, sys_target->regions);
114474bd8b7dSSeongJae Park 	if (err)
114574bd8b7dSSeongJae Park 		goto destroy_targets_out;
114674bd8b7dSSeongJae Park 	return 0;
114774bd8b7dSSeongJae Park 
114874bd8b7dSSeongJae Park destroy_targets_out:
114974bd8b7dSSeongJae Park 	damon_sysfs_destroy_targets(ctx);
115074bd8b7dSSeongJae Park 	return err;
115174bd8b7dSSeongJae Park }
115274bd8b7dSSeongJae Park 
damon_sysfs_update_target_pid(struct damon_target * target,int pid)1153938e5d28SSeongJae Park static int damon_sysfs_update_target_pid(struct damon_target *target, int pid)
1154938e5d28SSeongJae Park {
1155938e5d28SSeongJae Park 	struct pid *pid_new;
1156938e5d28SSeongJae Park 
1157938e5d28SSeongJae Park 	pid_new = find_get_pid(pid);
1158938e5d28SSeongJae Park 	if (!pid_new)
1159938e5d28SSeongJae Park 		return -EINVAL;
1160938e5d28SSeongJae Park 
1161938e5d28SSeongJae Park 	if (pid_new == target->pid) {
1162938e5d28SSeongJae Park 		put_pid(pid_new);
1163938e5d28SSeongJae Park 		return 0;
1164938e5d28SSeongJae Park 	}
1165938e5d28SSeongJae Park 
1166938e5d28SSeongJae Park 	put_pid(target->pid);
1167938e5d28SSeongJae Park 	target->pid = pid_new;
1168938e5d28SSeongJae Park 	return 0;
1169938e5d28SSeongJae Park }
1170938e5d28SSeongJae Park 
damon_sysfs_update_target(struct damon_target * target,struct damon_ctx * ctx,struct damon_sysfs_target * sys_target)11711f8d1e47SSeongJae Park static int damon_sysfs_update_target(struct damon_target *target,
11721f8d1e47SSeongJae Park 		struct damon_ctx *ctx,
11731f8d1e47SSeongJae Park 		struct damon_sysfs_target *sys_target)
1174da878780SSeongJae Park {
1175*577209e8SDan Carpenter 	int err = 0;
1176da878780SSeongJae Park 
1177938e5d28SSeongJae Park 	if (damon_target_has_pid(ctx)) {
1178938e5d28SSeongJae Park 		err = damon_sysfs_update_target_pid(target, sys_target->pid);
1179938e5d28SSeongJae Park 		if (err)
1180938e5d28SSeongJae Park 			return err;
1181da878780SSeongJae Park 	}
11821f8d1e47SSeongJae Park 
1183938e5d28SSeongJae Park 	/*
1184938e5d28SSeongJae Park 	 * Do monitoring target region boundary update only if one or more
1185938e5d28SSeongJae Park 	 * regions are set by the user.  This is for keeping current monitoring
1186938e5d28SSeongJae Park 	 * target results and range easier, especially for dynamic monitoring
1187938e5d28SSeongJae Park 	 * target regions update ops like 'vaddr'.
1188938e5d28SSeongJae Park 	 */
1189938e5d28SSeongJae Park 	if (sys_target->regions->nr)
1190938e5d28SSeongJae Park 		err = damon_sysfs_set_regions(target, sys_target->regions);
1191938e5d28SSeongJae Park 	return err;
1192da878780SSeongJae Park }
1193da878780SSeongJae Park 
damon_sysfs_set_targets(struct damon_ctx * ctx,struct damon_sysfs_targets * sysfs_targets)1194a61ea561SSeongJae Park static int damon_sysfs_set_targets(struct damon_ctx *ctx,
1195a61ea561SSeongJae Park 		struct damon_sysfs_targets *sysfs_targets)
1196a61ea561SSeongJae Park {
11971f8d1e47SSeongJae Park 	struct damon_target *t, *next;
11981f8d1e47SSeongJae Park 	int i = 0, err;
1199a61ea561SSeongJae Park 
12000a890a9fSSeongJae Park 	/* Multiple physical address space monitoring targets makes no sense */
12010a890a9fSSeongJae Park 	if (ctx->ops.id == DAMON_OPS_PADDR && sysfs_targets->nr > 1)
12020a890a9fSSeongJae Park 		return -EINVAL;
12030a890a9fSSeongJae Park 
12041f8d1e47SSeongJae Park 	damon_for_each_target_safe(t, next, ctx) {
12051f8d1e47SSeongJae Park 		if (i < sysfs_targets->nr) {
120633edac1fSSeongJae Park 			err = damon_sysfs_update_target(t, ctx,
12071f8d1e47SSeongJae Park 					sysfs_targets->targets_arr[i]);
120833edac1fSSeongJae Park 			if (err)
120933edac1fSSeongJae Park 				return err;
12101f8d1e47SSeongJae Park 		} else {
12111f8d1e47SSeongJae Park 			if (damon_target_has_pid(ctx))
12121f8d1e47SSeongJae Park 				put_pid(t->pid);
12131f8d1e47SSeongJae Park 			damon_destroy_target(t);
12141f8d1e47SSeongJae Park 		}
12151f8d1e47SSeongJae Park 		i++;
12161f8d1e47SSeongJae Park 	}
1217da878780SSeongJae Park 
12181f8d1e47SSeongJae Park 	for (; i < sysfs_targets->nr; i++) {
12191f8d1e47SSeongJae Park 		struct damon_sysfs_target *st = sysfs_targets->targets_arr[i];
12201f8d1e47SSeongJae Park 
1221da878780SSeongJae Park 		err = damon_sysfs_add_target(st, ctx);
122274bd8b7dSSeongJae Park 		if (err)
12232031b14eSSeongJae Park 			return err;
12242031b14eSSeongJae Park 	}
1225a61ea561SSeongJae Park 	return 0;
1226a61ea561SSeongJae Park }
1227a61ea561SSeongJae Park 
122876b7069bSSeongJae Park static bool damon_sysfs_schemes_regions_updating;
122976b7069bSSeongJae Park 
damon_sysfs_before_terminate(struct damon_ctx * ctx)1230a61ea561SSeongJae Park static void damon_sysfs_before_terminate(struct damon_ctx *ctx)
1231a61ea561SSeongJae Park {
1232a61ea561SSeongJae Park 	struct damon_target *t, *next;
1233f1d13cacSSeongJae Park 	struct damon_sysfs_kdamond *kdamond;
12346ad243b8SSeongJae Park 	enum damon_sysfs_cmd cmd;
1235f1d13cacSSeongJae Park 
1236f1d13cacSSeongJae Park 	/* damon_sysfs_schemes_update_regions_stop() might not yet called */
1237f1d13cacSSeongJae Park 	kdamond = damon_sysfs_cmd_request.kdamond;
12386ad243b8SSeongJae Park 	cmd = damon_sysfs_cmd_request.cmd;
12396ad243b8SSeongJae Park 	if (kdamond && ctx == kdamond->damon_ctx &&
12406ad243b8SSeongJae Park 			(cmd == DAMON_SYSFS_CMD_UPDATE_SCHEMES_TRIED_REGIONS ||
124176b7069bSSeongJae Park 			 cmd == DAMON_SYSFS_CMD_UPDATE_SCHEMES_TRIED_BYTES) &&
124276b7069bSSeongJae Park 			damon_sysfs_schemes_regions_updating) {
1243f1d13cacSSeongJae Park 		damon_sysfs_schemes_update_regions_stop(ctx);
124476b7069bSSeongJae Park 		damon_sysfs_schemes_regions_updating = false;
1245f1d13cacSSeongJae Park 		mutex_unlock(&damon_sysfs_lock);
1246f1d13cacSSeongJae Park 	}
1247a61ea561SSeongJae Park 
124861768a1bSXin Hao 	if (!damon_target_has_pid(ctx))
1249a61ea561SSeongJae Park 		return;
1250a61ea561SSeongJae Park 
1251a61ea561SSeongJae Park 	mutex_lock(&ctx->kdamond_lock);
1252a61ea561SSeongJae Park 	damon_for_each_target_safe(t, next, ctx) {
1253a61ea561SSeongJae Park 		put_pid(t->pid);
1254a61ea561SSeongJae Park 		damon_destroy_target(t);
1255a61ea561SSeongJae Park 	}
1256a61ea561SSeongJae Park 	mutex_unlock(&ctx->kdamond_lock);
1257a61ea561SSeongJae Park }
1258a61ea561SSeongJae Park 
12594acd715fSSeongJae Park /*
12604acd715fSSeongJae Park  * damon_sysfs_upd_schemes_stats() - Update schemes stats sysfs files.
12614acd715fSSeongJae Park  * @kdamond:	The kobject wrapper that associated to the kdamond thread.
12624acd715fSSeongJae Park  *
12634acd715fSSeongJae Park  * This function reads the schemes stats of specific kdamond and update the
12644acd715fSSeongJae Park  * related values for sysfs files.  This function should be called from DAMON
12654acd715fSSeongJae Park  * callbacks while holding ``damon_syfs_lock``, to safely access the DAMON
12664acd715fSSeongJae Park  * contexts-internal data and DAMON sysfs variables.
12674acd715fSSeongJae Park  */
damon_sysfs_upd_schemes_stats(struct damon_sysfs_kdamond * kdamond)12684acd715fSSeongJae Park static int damon_sysfs_upd_schemes_stats(struct damon_sysfs_kdamond *kdamond)
12694acd715fSSeongJae Park {
12704acd715fSSeongJae Park 	struct damon_ctx *ctx = kdamond->damon_ctx;
12714acd715fSSeongJae Park 
12724acd715fSSeongJae Park 	if (!ctx)
12734acd715fSSeongJae Park 		return -EINVAL;
12744acd715fSSeongJae Park 	damon_sysfs_schemes_update_stats(
12754acd715fSSeongJae Park 			kdamond->contexts->contexts_arr[0]->schemes, ctx);
127601538719SSeongJae Park 	return 0;
127701538719SSeongJae Park }
127801538719SSeongJae Park 
damon_sysfs_upd_schemes_regions_start(struct damon_sysfs_kdamond * kdamond,bool total_bytes_only)1279f1d13cacSSeongJae Park static int damon_sysfs_upd_schemes_regions_start(
12806ad243b8SSeongJae Park 		struct damon_sysfs_kdamond *kdamond, bool total_bytes_only)
1281f1d13cacSSeongJae Park {
1282f1d13cacSSeongJae Park 	struct damon_ctx *ctx = kdamond->damon_ctx;
1283f1d13cacSSeongJae Park 
1284f1d13cacSSeongJae Park 	if (!ctx)
1285f1d13cacSSeongJae Park 		return -EINVAL;
1286f1d13cacSSeongJae Park 	return damon_sysfs_schemes_update_regions_start(
12876ad243b8SSeongJae Park 			kdamond->contexts->contexts_arr[0]->schemes, ctx,
12886ad243b8SSeongJae Park 			total_bytes_only);
1289f1d13cacSSeongJae Park }
1290f1d13cacSSeongJae Park 
damon_sysfs_upd_schemes_regions_stop(struct damon_sysfs_kdamond * kdamond)1291f1d13cacSSeongJae Park static int damon_sysfs_upd_schemes_regions_stop(
1292f1d13cacSSeongJae Park 		struct damon_sysfs_kdamond *kdamond)
1293f1d13cacSSeongJae Park {
1294f1d13cacSSeongJae Park 	struct damon_ctx *ctx = kdamond->damon_ctx;
1295f1d13cacSSeongJae Park 
1296f1d13cacSSeongJae Park 	if (!ctx)
1297f1d13cacSSeongJae Park 		return -EINVAL;
1298f1d13cacSSeongJae Park 	return damon_sysfs_schemes_update_regions_stop(ctx);
1299f1d13cacSSeongJae Park }
1300f1d13cacSSeongJae Park 
damon_sysfs_clear_schemes_regions(struct damon_sysfs_kdamond * kdamond)1301772c15e5SSeongJae Park static int damon_sysfs_clear_schemes_regions(
1302772c15e5SSeongJae Park 		struct damon_sysfs_kdamond *kdamond)
1303772c15e5SSeongJae Park {
1304772c15e5SSeongJae Park 	struct damon_ctx *ctx = kdamond->damon_ctx;
1305772c15e5SSeongJae Park 
1306772c15e5SSeongJae Park 	if (!ctx)
1307772c15e5SSeongJae Park 		return -EINVAL;
1308772c15e5SSeongJae Park 	return damon_sysfs_schemes_clear_regions(
1309772c15e5SSeongJae Park 			kdamond->contexts->contexts_arr[0]->schemes, ctx);
1310772c15e5SSeongJae Park }
1311772c15e5SSeongJae Park 
damon_sysfs_kdamond_running(struct damon_sysfs_kdamond * kdamond)1312da878780SSeongJae Park static inline bool damon_sysfs_kdamond_running(
1313da878780SSeongJae Park 		struct damon_sysfs_kdamond *kdamond)
1314da878780SSeongJae Park {
1315da878780SSeongJae Park 	return kdamond->damon_ctx &&
1316da878780SSeongJae Park 		damon_sysfs_ctx_running(kdamond->damon_ctx);
1317da878780SSeongJae Park }
1318da878780SSeongJae Park 
damon_sysfs_apply_inputs(struct damon_ctx * ctx,struct damon_sysfs_context * sys_ctx)1319a79b68eeSSeongJae Park static int damon_sysfs_apply_inputs(struct damon_ctx *ctx,
1320a79b68eeSSeongJae Park 		struct damon_sysfs_context *sys_ctx)
1321da878780SSeongJae Park {
1322a79b68eeSSeongJae Park 	int err;
1323da878780SSeongJae Park 
1324da878780SSeongJae Park 	err = damon_select_ops(ctx, sys_ctx->ops_id);
1325da878780SSeongJae Park 	if (err)
1326da878780SSeongJae Park 		return err;
1327da878780SSeongJae Park 	err = damon_sysfs_set_attrs(ctx, sys_ctx->attrs);
1328da878780SSeongJae Park 	if (err)
1329da878780SSeongJae Park 		return err;
1330da878780SSeongJae Park 	err = damon_sysfs_set_targets(ctx, sys_ctx->targets);
1331da878780SSeongJae Park 	if (err)
1332da878780SSeongJae Park 		return err;
1333a79b68eeSSeongJae Park 	return damon_sysfs_set_schemes(ctx, sys_ctx->schemes);
1334a79b68eeSSeongJae Park }
1335a79b68eeSSeongJae Park 
1336a79b68eeSSeongJae Park /*
1337a79b68eeSSeongJae Park  * damon_sysfs_commit_input() - Commit user inputs to a running kdamond.
1338a79b68eeSSeongJae Park  * @kdamond:	The kobject wrapper for the associated kdamond.
1339a79b68eeSSeongJae Park  *
1340a79b68eeSSeongJae Park  * If the sysfs input is wrong, the kdamond will be terminated.
1341a79b68eeSSeongJae Park  */
damon_sysfs_commit_input(struct damon_sysfs_kdamond * kdamond)1342a79b68eeSSeongJae Park static int damon_sysfs_commit_input(struct damon_sysfs_kdamond *kdamond)
1343a79b68eeSSeongJae Park {
1344a79b68eeSSeongJae Park 	if (!damon_sysfs_kdamond_running(kdamond))
1345a79b68eeSSeongJae Park 		return -EINVAL;
1346a79b68eeSSeongJae Park 	/* TODO: Support multiple contexts per kdamond */
1347a79b68eeSSeongJae Park 	if (kdamond->contexts->nr != 1)
1348a79b68eeSSeongJae Park 		return -EINVAL;
1349a79b68eeSSeongJae Park 
1350a79b68eeSSeongJae Park 	return damon_sysfs_apply_inputs(kdamond->damon_ctx,
1351a79b68eeSSeongJae Park 			kdamond->contexts->contexts_arr[0]);
1352da878780SSeongJae Park }
1353da878780SSeongJae Park 
135401538719SSeongJae Park /*
135501538719SSeongJae Park  * damon_sysfs_cmd_request_callback() - DAMON callback for handling requests.
135601538719SSeongJae Park  * @c:	The DAMON context of the callback.
135701538719SSeongJae Park  *
135801538719SSeongJae Park  * This function is periodically called back from the kdamond thread for @c.
135901538719SSeongJae Park  * Then, it checks if there is a waiting DAMON sysfs request and handles it.
136001538719SSeongJae Park  */
damon_sysfs_cmd_request_callback(struct damon_ctx * c)136101538719SSeongJae Park static int damon_sysfs_cmd_request_callback(struct damon_ctx *c)
136201538719SSeongJae Park {
136301538719SSeongJae Park 	struct damon_sysfs_kdamond *kdamond;
13646ad243b8SSeongJae Park 	bool total_bytes_only = false;
136501538719SSeongJae Park 	int err = 0;
136601538719SSeongJae Park 
136701538719SSeongJae Park 	/* avoid deadlock due to concurrent state_store('off') */
1368f1d13cacSSeongJae Park 	if (!damon_sysfs_schemes_regions_updating &&
1369f1d13cacSSeongJae Park 			!mutex_trylock(&damon_sysfs_lock))
137001538719SSeongJae Park 		return 0;
137101538719SSeongJae Park 	kdamond = damon_sysfs_cmd_request.kdamond;
137201538719SSeongJae Park 	if (!kdamond || kdamond->damon_ctx != c)
137301538719SSeongJae Park 		goto out;
137401538719SSeongJae Park 	switch (damon_sysfs_cmd_request.cmd) {
137501538719SSeongJae Park 	case DAMON_SYSFS_CMD_UPDATE_SCHEMES_STATS:
137601538719SSeongJae Park 		err = damon_sysfs_upd_schemes_stats(kdamond);
137701538719SSeongJae Park 		break;
1378da878780SSeongJae Park 	case DAMON_SYSFS_CMD_COMMIT:
1379da878780SSeongJae Park 		err = damon_sysfs_commit_input(kdamond);
1380da878780SSeongJae Park 		break;
13816ad243b8SSeongJae Park 	case DAMON_SYSFS_CMD_UPDATE_SCHEMES_TRIED_BYTES:
13826ad243b8SSeongJae Park 		total_bytes_only = true;
13836ad243b8SSeongJae Park 		fallthrough;
1384f1d13cacSSeongJae Park 	case DAMON_SYSFS_CMD_UPDATE_SCHEMES_TRIED_REGIONS:
1385f1d13cacSSeongJae Park 		if (!damon_sysfs_schemes_regions_updating) {
13866ad243b8SSeongJae Park 			err = damon_sysfs_upd_schemes_regions_start(kdamond,
13876ad243b8SSeongJae Park 					total_bytes_only);
1388f1d13cacSSeongJae Park 			if (!err) {
1389f1d13cacSSeongJae Park 				damon_sysfs_schemes_regions_updating = true;
1390f1d13cacSSeongJae Park 				goto keep_lock_out;
1391f1d13cacSSeongJae Park 			}
1392f1d13cacSSeongJae Park 		} else {
1393f1d13cacSSeongJae Park 			err = damon_sysfs_upd_schemes_regions_stop(kdamond);
1394f1d13cacSSeongJae Park 			damon_sysfs_schemes_regions_updating = false;
1395f1d13cacSSeongJae Park 		}
1396f1d13cacSSeongJae Park 		break;
1397772c15e5SSeongJae Park 	case DAMON_SYSFS_CMD_CLEAR_SCHEMES_TRIED_REGIONS:
1398772c15e5SSeongJae Park 		err = damon_sysfs_clear_schemes_regions(kdamond);
1399772c15e5SSeongJae Park 		break;
140001538719SSeongJae Park 	default:
140101538719SSeongJae Park 		break;
140201538719SSeongJae Park 	}
140301538719SSeongJae Park 	/* Mark the request as invalid now. */
140401538719SSeongJae Park 	damon_sysfs_cmd_request.kdamond = NULL;
140501538719SSeongJae Park out:
1406f1d13cacSSeongJae Park 	if (!damon_sysfs_schemes_regions_updating)
140701538719SSeongJae Park 		mutex_unlock(&damon_sysfs_lock);
1408f1d13cacSSeongJae Park keep_lock_out:
140901538719SSeongJae Park 	return err;
141001538719SSeongJae Park }
141101538719SSeongJae Park 
damon_sysfs_build_ctx(struct damon_sysfs_context * sys_ctx)1412a61ea561SSeongJae Park static struct damon_ctx *damon_sysfs_build_ctx(
1413a61ea561SSeongJae Park 		struct damon_sysfs_context *sys_ctx)
1414a61ea561SSeongJae Park {
1415a61ea561SSeongJae Park 	struct damon_ctx *ctx = damon_new_ctx();
1416a61ea561SSeongJae Park 	int err;
1417a61ea561SSeongJae Park 
1418a61ea561SSeongJae Park 	if (!ctx)
1419a61ea561SSeongJae Park 		return ERR_PTR(-ENOMEM);
1420a61ea561SSeongJae Park 
1421a79b68eeSSeongJae Park 	err = damon_sysfs_apply_inputs(ctx, sys_ctx);
1422a79b68eeSSeongJae Park 	if (err) {
1423a79b68eeSSeongJae Park 		damon_destroy_ctx(ctx);
1424a79b68eeSSeongJae Park 		return ERR_PTR(err);
1425a79b68eeSSeongJae Park 	}
1426a61ea561SSeongJae Park 
142701538719SSeongJae Park 	ctx->callback.after_wmarks_check = damon_sysfs_cmd_request_callback;
142801538719SSeongJae Park 	ctx->callback.after_aggregation = damon_sysfs_cmd_request_callback;
1429a61ea561SSeongJae Park 	ctx->callback.before_terminate = damon_sysfs_before_terminate;
1430a61ea561SSeongJae Park 	return ctx;
1431a61ea561SSeongJae Park }
1432a61ea561SSeongJae Park 
damon_sysfs_turn_damon_on(struct damon_sysfs_kdamond * kdamond)1433a61ea561SSeongJae Park static int damon_sysfs_turn_damon_on(struct damon_sysfs_kdamond *kdamond)
1434a61ea561SSeongJae Park {
1435a61ea561SSeongJae Park 	struct damon_ctx *ctx;
1436a61ea561SSeongJae Park 	int err;
1437a61ea561SSeongJae Park 
1438e7fcac4cSKaixu Xia 	if (damon_sysfs_kdamond_running(kdamond))
1439a61ea561SSeongJae Park 		return -EBUSY;
144001538719SSeongJae Park 	if (damon_sysfs_cmd_request.kdamond == kdamond)
144101538719SSeongJae Park 		return -EBUSY;
1442a61ea561SSeongJae Park 	/* TODO: support multiple contexts per kdamond */
1443a61ea561SSeongJae Park 	if (kdamond->contexts->nr != 1)
1444a61ea561SSeongJae Park 		return -EINVAL;
1445a61ea561SSeongJae Park 
1446a61ea561SSeongJae Park 	if (kdamond->damon_ctx)
1447a61ea561SSeongJae Park 		damon_destroy_ctx(kdamond->damon_ctx);
1448a61ea561SSeongJae Park 	kdamond->damon_ctx = NULL;
1449a61ea561SSeongJae Park 
1450a61ea561SSeongJae Park 	ctx = damon_sysfs_build_ctx(kdamond->contexts->contexts_arr[0]);
1451a61ea561SSeongJae Park 	if (IS_ERR(ctx))
1452a61ea561SSeongJae Park 		return PTR_ERR(ctx);
1453a61ea561SSeongJae Park 	err = damon_start(&ctx, 1, false);
1454a61ea561SSeongJae Park 	if (err) {
1455a61ea561SSeongJae Park 		damon_destroy_ctx(ctx);
1456a61ea561SSeongJae Park 		return err;
1457a61ea561SSeongJae Park 	}
1458a61ea561SSeongJae Park 	kdamond->damon_ctx = ctx;
1459a61ea561SSeongJae Park 	return err;
1460a61ea561SSeongJae Park }
1461a61ea561SSeongJae Park 
damon_sysfs_turn_damon_off(struct damon_sysfs_kdamond * kdamond)1462a61ea561SSeongJae Park static int damon_sysfs_turn_damon_off(struct damon_sysfs_kdamond *kdamond)
1463a61ea561SSeongJae Park {
1464a61ea561SSeongJae Park 	if (!kdamond->damon_ctx)
1465a61ea561SSeongJae Park 		return -EINVAL;
1466a61ea561SSeongJae Park 	return damon_stop(&kdamond->damon_ctx, 1);
1467a61ea561SSeongJae Park 	/*
1468a61ea561SSeongJae Park 	 * To allow users show final monitoring results of already turned-off
1469a61ea561SSeongJae Park 	 * DAMON, we free kdamond->damon_ctx in next
1470a61ea561SSeongJae Park 	 * damon_sysfs_turn_damon_on(), or kdamonds_nr_store()
1471a61ea561SSeongJae Park 	 */
1472a61ea561SSeongJae Park }
1473c951cd3bSSeongJae Park 
14743cbab4caSSeongJae Park /*
14753cbab4caSSeongJae Park  * damon_sysfs_handle_cmd() - Handle a command for a specific kdamond.
14763cbab4caSSeongJae Park  * @cmd:	The command to handle.
14773cbab4caSSeongJae Park  * @kdamond:	The kobject wrapper for the associated kdamond.
14783cbab4caSSeongJae Park  *
147901538719SSeongJae Park  * This function handles a DAMON sysfs command for a kdamond.  For commands
148001538719SSeongJae Park  * that need to access running DAMON context-internal data, it requests
148101538719SSeongJae Park  * handling of the command to the DAMON callback
148201538719SSeongJae Park  * (@damon_sysfs_cmd_request_callback()) and wait until it is properly handled,
148301538719SSeongJae Park  * or the context is completed.
14843cbab4caSSeongJae Park  *
14853cbab4caSSeongJae Park  * Return: 0 on success, negative error code otherwise.
14863cbab4caSSeongJae Park  */
damon_sysfs_handle_cmd(enum damon_sysfs_cmd cmd,struct damon_sysfs_kdamond * kdamond)14873cbab4caSSeongJae Park static int damon_sysfs_handle_cmd(enum damon_sysfs_cmd cmd,
14883cbab4caSSeongJae Park 		struct damon_sysfs_kdamond *kdamond)
14893cbab4caSSeongJae Park {
149001538719SSeongJae Park 	bool need_wait = true;
149101538719SSeongJae Park 
149201538719SSeongJae Park 	/* Handle commands that doesn't access DAMON context-internal data */
14933cbab4caSSeongJae Park 	switch (cmd) {
14943cbab4caSSeongJae Park 	case DAMON_SYSFS_CMD_ON:
14953cbab4caSSeongJae Park 		return damon_sysfs_turn_damon_on(kdamond);
14963cbab4caSSeongJae Park 	case DAMON_SYSFS_CMD_OFF:
14973cbab4caSSeongJae Park 		return damon_sysfs_turn_damon_off(kdamond);
14983cbab4caSSeongJae Park 	default:
14993cbab4caSSeongJae Park 		break;
15003cbab4caSSeongJae Park 	}
150101538719SSeongJae Park 
150201538719SSeongJae Park 	/* Pass the command to DAMON callback for safe DAMON context access */
150301538719SSeongJae Park 	if (damon_sysfs_cmd_request.kdamond)
150401538719SSeongJae Park 		return -EBUSY;
150501538719SSeongJae Park 	if (!damon_sysfs_kdamond_running(kdamond))
15063cbab4caSSeongJae Park 		return -EINVAL;
150701538719SSeongJae Park 	damon_sysfs_cmd_request.cmd = cmd;
150801538719SSeongJae Park 	damon_sysfs_cmd_request.kdamond = kdamond;
150901538719SSeongJae Park 
151001538719SSeongJae Park 	/*
151101538719SSeongJae Park 	 * wait until damon_sysfs_cmd_request_callback() handles the request
151201538719SSeongJae Park 	 * from kdamond context
151301538719SSeongJae Park 	 */
151401538719SSeongJae Park 	mutex_unlock(&damon_sysfs_lock);
151501538719SSeongJae Park 	while (need_wait) {
151601538719SSeongJae Park 		schedule_timeout_idle(msecs_to_jiffies(100));
151701538719SSeongJae Park 		if (!mutex_trylock(&damon_sysfs_lock))
151801538719SSeongJae Park 			continue;
151901538719SSeongJae Park 		if (!damon_sysfs_cmd_request.kdamond) {
152001538719SSeongJae Park 			/* damon_sysfs_cmd_request_callback() handled */
152101538719SSeongJae Park 			need_wait = false;
152201538719SSeongJae Park 		} else if (!damon_sysfs_kdamond_running(kdamond)) {
152301538719SSeongJae Park 			/* kdamond has already finished */
152401538719SSeongJae Park 			need_wait = false;
152501538719SSeongJae Park 			damon_sysfs_cmd_request.kdamond = NULL;
152601538719SSeongJae Park 		}
152701538719SSeongJae Park 		mutex_unlock(&damon_sysfs_lock);
152801538719SSeongJae Park 	}
152901538719SSeongJae Park 	mutex_lock(&damon_sysfs_lock);
153001538719SSeongJae Park 	return 0;
15313cbab4caSSeongJae Park }
15323cbab4caSSeongJae Park 
state_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)1533c951cd3bSSeongJae Park static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
1534c951cd3bSSeongJae Park 		const char *buf, size_t count)
1535c951cd3bSSeongJae Park {
1536a61ea561SSeongJae Park 	struct damon_sysfs_kdamond *kdamond = container_of(kobj,
1537a61ea561SSeongJae Park 			struct damon_sysfs_kdamond, kobj);
15383cbab4caSSeongJae Park 	enum damon_sysfs_cmd cmd;
15393cbab4caSSeongJae Park 	ssize_t ret = -EINVAL;
1540a61ea561SSeongJae Park 
1541a61ea561SSeongJae Park 	if (!mutex_trylock(&damon_sysfs_lock))
1542a61ea561SSeongJae Park 		return -EBUSY;
15433cbab4caSSeongJae Park 	for (cmd = 0; cmd < NR_DAMON_SYSFS_CMDS; cmd++) {
15443cbab4caSSeongJae Park 		if (sysfs_streq(buf, damon_sysfs_cmd_strs[cmd])) {
15453cbab4caSSeongJae Park 			ret = damon_sysfs_handle_cmd(cmd, kdamond);
15463cbab4caSSeongJae Park 			break;
15473cbab4caSSeongJae Park 		}
15483cbab4caSSeongJae Park 	}
1549a61ea561SSeongJae Park 	mutex_unlock(&damon_sysfs_lock);
1550a61ea561SSeongJae Park 	if (!ret)
1551a61ea561SSeongJae Park 		ret = count;
1552a61ea561SSeongJae Park 	return ret;
1553c951cd3bSSeongJae Park }
1554c951cd3bSSeongJae Park 
pid_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)1555c951cd3bSSeongJae Park static ssize_t pid_show(struct kobject *kobj,
1556c951cd3bSSeongJae Park 		struct kobj_attribute *attr, char *buf)
1557c951cd3bSSeongJae Park {
1558a61ea561SSeongJae Park 	struct damon_sysfs_kdamond *kdamond = container_of(kobj,
1559a61ea561SSeongJae Park 			struct damon_sysfs_kdamond, kobj);
1560a61ea561SSeongJae Park 	struct damon_ctx *ctx;
1561f1c71c28SKaixu Xia 	int pid = -1;
1562a61ea561SSeongJae Park 
1563a61ea561SSeongJae Park 	if (!mutex_trylock(&damon_sysfs_lock))
1564a61ea561SSeongJae Park 		return -EBUSY;
1565a61ea561SSeongJae Park 	ctx = kdamond->damon_ctx;
1566f1c71c28SKaixu Xia 	if (!ctx)
1567a61ea561SSeongJae Park 		goto out;
1568f1c71c28SKaixu Xia 
1569a61ea561SSeongJae Park 	mutex_lock(&ctx->kdamond_lock);
1570f1c71c28SKaixu Xia 	if (ctx->kdamond)
1571a61ea561SSeongJae Park 		pid = ctx->kdamond->pid;
1572a61ea561SSeongJae Park 	mutex_unlock(&ctx->kdamond_lock);
1573a61ea561SSeongJae Park out:
1574a61ea561SSeongJae Park 	mutex_unlock(&damon_sysfs_lock);
1575a61ea561SSeongJae Park 	return sysfs_emit(buf, "%d\n", pid);
1576c951cd3bSSeongJae Park }
1577c951cd3bSSeongJae Park 
damon_sysfs_kdamond_release(struct kobject * kobj)1578c951cd3bSSeongJae Park static void damon_sysfs_kdamond_release(struct kobject *kobj)
1579c951cd3bSSeongJae Park {
1580c951cd3bSSeongJae Park 	struct damon_sysfs_kdamond *kdamond = container_of(kobj,
1581c951cd3bSSeongJae Park 			struct damon_sysfs_kdamond, kobj);
1582c951cd3bSSeongJae Park 
1583c951cd3bSSeongJae Park 	if (kdamond->damon_ctx)
1584c951cd3bSSeongJae Park 		damon_destroy_ctx(kdamond->damon_ctx);
158515423a52SXin Hao 	kfree(kdamond);
1586c951cd3bSSeongJae Park }
1587c951cd3bSSeongJae Park 
1588c951cd3bSSeongJae Park static struct kobj_attribute damon_sysfs_kdamond_state_attr =
1589c951cd3bSSeongJae Park 		__ATTR_RW_MODE(state, 0600);
1590c951cd3bSSeongJae Park 
1591c951cd3bSSeongJae Park static struct kobj_attribute damon_sysfs_kdamond_pid_attr =
1592c951cd3bSSeongJae Park 		__ATTR_RO_MODE(pid, 0400);
1593c951cd3bSSeongJae Park 
1594c951cd3bSSeongJae Park static struct attribute *damon_sysfs_kdamond_attrs[] = {
1595c951cd3bSSeongJae Park 	&damon_sysfs_kdamond_state_attr.attr,
1596c951cd3bSSeongJae Park 	&damon_sysfs_kdamond_pid_attr.attr,
1597c951cd3bSSeongJae Park 	NULL,
1598c951cd3bSSeongJae Park };
1599c951cd3bSSeongJae Park ATTRIBUTE_GROUPS(damon_sysfs_kdamond);
1600c951cd3bSSeongJae Park 
1601e56397e8SThomas Weißschuh static const struct kobj_type damon_sysfs_kdamond_ktype = {
1602c951cd3bSSeongJae Park 	.release = damon_sysfs_kdamond_release,
1603c951cd3bSSeongJae Park 	.sysfs_ops = &kobj_sysfs_ops,
1604c951cd3bSSeongJae Park 	.default_groups = damon_sysfs_kdamond_groups,
1605c951cd3bSSeongJae Park };
1606c951cd3bSSeongJae Park 
1607c951cd3bSSeongJae Park /*
1608c951cd3bSSeongJae Park  * kdamonds directory
1609c951cd3bSSeongJae Park  */
1610c951cd3bSSeongJae Park 
1611c951cd3bSSeongJae Park struct damon_sysfs_kdamonds {
1612c951cd3bSSeongJae Park 	struct kobject kobj;
1613c951cd3bSSeongJae Park 	struct damon_sysfs_kdamond **kdamonds_arr;
1614c951cd3bSSeongJae Park 	int nr;
1615c951cd3bSSeongJae Park };
1616c951cd3bSSeongJae Park 
damon_sysfs_kdamonds_alloc(void)1617c951cd3bSSeongJae Park static struct damon_sysfs_kdamonds *damon_sysfs_kdamonds_alloc(void)
1618c951cd3bSSeongJae Park {
1619c951cd3bSSeongJae Park 	return kzalloc(sizeof(struct damon_sysfs_kdamonds), GFP_KERNEL);
1620c951cd3bSSeongJae Park }
1621c951cd3bSSeongJae Park 
damon_sysfs_kdamonds_rm_dirs(struct damon_sysfs_kdamonds * kdamonds)1622c951cd3bSSeongJae Park static void damon_sysfs_kdamonds_rm_dirs(struct damon_sysfs_kdamonds *kdamonds)
1623c951cd3bSSeongJae Park {
1624c951cd3bSSeongJae Park 	struct damon_sysfs_kdamond **kdamonds_arr = kdamonds->kdamonds_arr;
1625c951cd3bSSeongJae Park 	int i;
1626c951cd3bSSeongJae Park 
1627c951cd3bSSeongJae Park 	for (i = 0; i < kdamonds->nr; i++) {
1628c951cd3bSSeongJae Park 		damon_sysfs_kdamond_rm_dirs(kdamonds_arr[i]);
1629c951cd3bSSeongJae Park 		kobject_put(&kdamonds_arr[i]->kobj);
1630c951cd3bSSeongJae Park 	}
1631c951cd3bSSeongJae Park 	kdamonds->nr = 0;
1632c951cd3bSSeongJae Park 	kfree(kdamonds_arr);
1633c951cd3bSSeongJae Park 	kdamonds->kdamonds_arr = NULL;
1634c951cd3bSSeongJae Park }
1635c951cd3bSSeongJae Park 
damon_sysfs_kdamonds_busy(struct damon_sysfs_kdamond ** kdamonds,int nr_kdamonds)1636c274cd5cSKaixu Xia static bool damon_sysfs_kdamonds_busy(struct damon_sysfs_kdamond **kdamonds,
1637c951cd3bSSeongJae Park 		int nr_kdamonds)
1638c951cd3bSSeongJae Park {
1639c951cd3bSSeongJae Park 	int i;
1640c951cd3bSSeongJae Park 
1641c951cd3bSSeongJae Park 	for (i = 0; i < nr_kdamonds; i++) {
1642c274cd5cSKaixu Xia 		if (damon_sysfs_kdamond_running(kdamonds[i]) ||
1643c274cd5cSKaixu Xia 		    damon_sysfs_cmd_request.kdamond == kdamonds[i])
1644c274cd5cSKaixu Xia 			return true;
1645c951cd3bSSeongJae Park 	}
1646c274cd5cSKaixu Xia 
1647c274cd5cSKaixu Xia 	return false;
1648c951cd3bSSeongJae Park }
1649c951cd3bSSeongJae Park 
damon_sysfs_kdamonds_add_dirs(struct damon_sysfs_kdamonds * kdamonds,int nr_kdamonds)1650c951cd3bSSeongJae Park static int damon_sysfs_kdamonds_add_dirs(struct damon_sysfs_kdamonds *kdamonds,
1651c951cd3bSSeongJae Park 		int nr_kdamonds)
1652c951cd3bSSeongJae Park {
1653c951cd3bSSeongJae Park 	struct damon_sysfs_kdamond **kdamonds_arr, *kdamond;
1654c951cd3bSSeongJae Park 	int err, i;
1655c951cd3bSSeongJae Park 
1656c274cd5cSKaixu Xia 	if (damon_sysfs_kdamonds_busy(kdamonds->kdamonds_arr, kdamonds->nr))
1657c951cd3bSSeongJae Park 		return -EBUSY;
1658c951cd3bSSeongJae Park 
1659c951cd3bSSeongJae Park 	damon_sysfs_kdamonds_rm_dirs(kdamonds);
1660c951cd3bSSeongJae Park 	if (!nr_kdamonds)
1661c951cd3bSSeongJae Park 		return 0;
1662c951cd3bSSeongJae Park 
1663c951cd3bSSeongJae Park 	kdamonds_arr = kmalloc_array(nr_kdamonds, sizeof(*kdamonds_arr),
1664c951cd3bSSeongJae Park 			GFP_KERNEL | __GFP_NOWARN);
1665c951cd3bSSeongJae Park 	if (!kdamonds_arr)
1666c951cd3bSSeongJae Park 		return -ENOMEM;
1667c951cd3bSSeongJae Park 	kdamonds->kdamonds_arr = kdamonds_arr;
1668c951cd3bSSeongJae Park 
1669c951cd3bSSeongJae Park 	for (i = 0; i < nr_kdamonds; i++) {
1670c951cd3bSSeongJae Park 		kdamond = damon_sysfs_kdamond_alloc();
1671c951cd3bSSeongJae Park 		if (!kdamond) {
1672c951cd3bSSeongJae Park 			damon_sysfs_kdamonds_rm_dirs(kdamonds);
1673c951cd3bSSeongJae Park 			return -ENOMEM;
1674c951cd3bSSeongJae Park 		}
1675c951cd3bSSeongJae Park 
1676c951cd3bSSeongJae Park 		err = kobject_init_and_add(&kdamond->kobj,
1677c951cd3bSSeongJae Park 				&damon_sysfs_kdamond_ktype, &kdamonds->kobj,
1678c951cd3bSSeongJae Park 				"%d", i);
1679c951cd3bSSeongJae Park 		if (err)
1680c951cd3bSSeongJae Park 			goto out;
1681c951cd3bSSeongJae Park 
1682c951cd3bSSeongJae Park 		err = damon_sysfs_kdamond_add_dirs(kdamond);
1683c951cd3bSSeongJae Park 		if (err)
1684c951cd3bSSeongJae Park 			goto out;
1685c951cd3bSSeongJae Park 
1686c951cd3bSSeongJae Park 		kdamonds_arr[i] = kdamond;
1687c951cd3bSSeongJae Park 		kdamonds->nr++;
1688c951cd3bSSeongJae Park 	}
1689c951cd3bSSeongJae Park 	return 0;
1690c951cd3bSSeongJae Park 
1691c951cd3bSSeongJae Park out:
1692c951cd3bSSeongJae Park 	damon_sysfs_kdamonds_rm_dirs(kdamonds);
1693c951cd3bSSeongJae Park 	kobject_put(&kdamond->kobj);
1694c951cd3bSSeongJae Park 	return err;
1695c951cd3bSSeongJae Park }
1696c951cd3bSSeongJae Park 
nr_kdamonds_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)1697c951cd3bSSeongJae Park static ssize_t nr_kdamonds_show(struct kobject *kobj,
1698c951cd3bSSeongJae Park 		struct kobj_attribute *attr, char *buf)
1699c951cd3bSSeongJae Park {
1700c951cd3bSSeongJae Park 	struct damon_sysfs_kdamonds *kdamonds = container_of(kobj,
1701c951cd3bSSeongJae Park 			struct damon_sysfs_kdamonds, kobj);
1702c951cd3bSSeongJae Park 
1703c951cd3bSSeongJae Park 	return sysfs_emit(buf, "%d\n", kdamonds->nr);
1704c951cd3bSSeongJae Park }
1705c951cd3bSSeongJae Park 
nr_kdamonds_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)1706c951cd3bSSeongJae Park static ssize_t nr_kdamonds_store(struct kobject *kobj,
1707c951cd3bSSeongJae Park 		struct kobj_attribute *attr, const char *buf, size_t count)
1708c951cd3bSSeongJae Park {
1709a17a8b3bSXin Hao 	struct damon_sysfs_kdamonds *kdamonds;
1710c951cd3bSSeongJae Park 	int nr, err;
1711c951cd3bSSeongJae Park 
1712c951cd3bSSeongJae Park 	err = kstrtoint(buf, 0, &nr);
1713c951cd3bSSeongJae Park 	if (err)
1714c951cd3bSSeongJae Park 		return err;
1715c951cd3bSSeongJae Park 	if (nr < 0)
1716c951cd3bSSeongJae Park 		return -EINVAL;
1717c951cd3bSSeongJae Park 
1718a17a8b3bSXin Hao 	kdamonds = container_of(kobj, struct damon_sysfs_kdamonds, kobj);
1719a17a8b3bSXin Hao 
1720c951cd3bSSeongJae Park 	if (!mutex_trylock(&damon_sysfs_lock))
1721c951cd3bSSeongJae Park 		return -EBUSY;
1722c951cd3bSSeongJae Park 	err = damon_sysfs_kdamonds_add_dirs(kdamonds, nr);
1723c951cd3bSSeongJae Park 	mutex_unlock(&damon_sysfs_lock);
1724c951cd3bSSeongJae Park 	if (err)
1725c951cd3bSSeongJae Park 		return err;
1726c951cd3bSSeongJae Park 
1727c951cd3bSSeongJae Park 	return count;
1728c951cd3bSSeongJae Park }
1729c951cd3bSSeongJae Park 
damon_sysfs_kdamonds_release(struct kobject * kobj)1730c951cd3bSSeongJae Park static void damon_sysfs_kdamonds_release(struct kobject *kobj)
1731c951cd3bSSeongJae Park {
1732c951cd3bSSeongJae Park 	kfree(container_of(kobj, struct damon_sysfs_kdamonds, kobj));
1733c951cd3bSSeongJae Park }
1734c951cd3bSSeongJae Park 
1735c951cd3bSSeongJae Park static struct kobj_attribute damon_sysfs_kdamonds_nr_attr =
1736c951cd3bSSeongJae Park 		__ATTR_RW_MODE(nr_kdamonds, 0600);
1737c951cd3bSSeongJae Park 
1738c951cd3bSSeongJae Park static struct attribute *damon_sysfs_kdamonds_attrs[] = {
1739c951cd3bSSeongJae Park 	&damon_sysfs_kdamonds_nr_attr.attr,
1740c951cd3bSSeongJae Park 	NULL,
1741c951cd3bSSeongJae Park };
1742c951cd3bSSeongJae Park ATTRIBUTE_GROUPS(damon_sysfs_kdamonds);
1743c951cd3bSSeongJae Park 
1744e56397e8SThomas Weißschuh static const struct kobj_type damon_sysfs_kdamonds_ktype = {
1745c951cd3bSSeongJae Park 	.release = damon_sysfs_kdamonds_release,
1746c951cd3bSSeongJae Park 	.sysfs_ops = &kobj_sysfs_ops,
1747c951cd3bSSeongJae Park 	.default_groups = damon_sysfs_kdamonds_groups,
1748c951cd3bSSeongJae Park };
1749c951cd3bSSeongJae Park 
1750c951cd3bSSeongJae Park /*
1751c951cd3bSSeongJae Park  * damon user interface directory
1752c951cd3bSSeongJae Park  */
1753c951cd3bSSeongJae Park 
1754c951cd3bSSeongJae Park struct damon_sysfs_ui_dir {
1755c951cd3bSSeongJae Park 	struct kobject kobj;
1756c951cd3bSSeongJae Park 	struct damon_sysfs_kdamonds *kdamonds;
1757c951cd3bSSeongJae Park };
1758c951cd3bSSeongJae Park 
damon_sysfs_ui_dir_alloc(void)1759c951cd3bSSeongJae Park static struct damon_sysfs_ui_dir *damon_sysfs_ui_dir_alloc(void)
1760c951cd3bSSeongJae Park {
1761c951cd3bSSeongJae Park 	return kzalloc(sizeof(struct damon_sysfs_ui_dir), GFP_KERNEL);
1762c951cd3bSSeongJae Park }
1763c951cd3bSSeongJae Park 
damon_sysfs_ui_dir_add_dirs(struct damon_sysfs_ui_dir * ui_dir)1764c951cd3bSSeongJae Park static int damon_sysfs_ui_dir_add_dirs(struct damon_sysfs_ui_dir *ui_dir)
1765c951cd3bSSeongJae Park {
1766c951cd3bSSeongJae Park 	struct damon_sysfs_kdamonds *kdamonds;
1767c951cd3bSSeongJae Park 	int err;
1768c951cd3bSSeongJae Park 
1769c951cd3bSSeongJae Park 	kdamonds = damon_sysfs_kdamonds_alloc();
1770c951cd3bSSeongJae Park 	if (!kdamonds)
1771c951cd3bSSeongJae Park 		return -ENOMEM;
1772c951cd3bSSeongJae Park 
1773c951cd3bSSeongJae Park 	err = kobject_init_and_add(&kdamonds->kobj,
1774c951cd3bSSeongJae Park 			&damon_sysfs_kdamonds_ktype, &ui_dir->kobj,
1775c951cd3bSSeongJae Park 			"kdamonds");
1776c951cd3bSSeongJae Park 	if (err) {
1777c951cd3bSSeongJae Park 		kobject_put(&kdamonds->kobj);
1778c951cd3bSSeongJae Park 		return err;
1779c951cd3bSSeongJae Park 	}
1780c951cd3bSSeongJae Park 	ui_dir->kdamonds = kdamonds;
1781c951cd3bSSeongJae Park 	return err;
1782c951cd3bSSeongJae Park }
1783c951cd3bSSeongJae Park 
damon_sysfs_ui_dir_release(struct kobject * kobj)1784c951cd3bSSeongJae Park static void damon_sysfs_ui_dir_release(struct kobject *kobj)
1785c951cd3bSSeongJae Park {
1786c951cd3bSSeongJae Park 	kfree(container_of(kobj, struct damon_sysfs_ui_dir, kobj));
1787c951cd3bSSeongJae Park }
1788c951cd3bSSeongJae Park 
1789c951cd3bSSeongJae Park static struct attribute *damon_sysfs_ui_dir_attrs[] = {
1790c951cd3bSSeongJae Park 	NULL,
1791c951cd3bSSeongJae Park };
1792c951cd3bSSeongJae Park ATTRIBUTE_GROUPS(damon_sysfs_ui_dir);
1793c951cd3bSSeongJae Park 
1794e56397e8SThomas Weißschuh static const struct kobj_type damon_sysfs_ui_dir_ktype = {
1795c951cd3bSSeongJae Park 	.release = damon_sysfs_ui_dir_release,
1796c951cd3bSSeongJae Park 	.sysfs_ops = &kobj_sysfs_ops,
1797c951cd3bSSeongJae Park 	.default_groups = damon_sysfs_ui_dir_groups,
1798c951cd3bSSeongJae Park };
1799c951cd3bSSeongJae Park 
damon_sysfs_init(void)1800c951cd3bSSeongJae Park static int __init damon_sysfs_init(void)
1801c951cd3bSSeongJae Park {
1802c951cd3bSSeongJae Park 	struct kobject *damon_sysfs_root;
1803c951cd3bSSeongJae Park 	struct damon_sysfs_ui_dir *admin;
1804c951cd3bSSeongJae Park 	int err;
1805c951cd3bSSeongJae Park 
1806c951cd3bSSeongJae Park 	damon_sysfs_root = kobject_create_and_add("damon", mm_kobj);
1807c951cd3bSSeongJae Park 	if (!damon_sysfs_root)
1808c951cd3bSSeongJae Park 		return -ENOMEM;
1809c951cd3bSSeongJae Park 
1810c951cd3bSSeongJae Park 	admin = damon_sysfs_ui_dir_alloc();
1811c951cd3bSSeongJae Park 	if (!admin) {
1812c951cd3bSSeongJae Park 		kobject_put(damon_sysfs_root);
1813c951cd3bSSeongJae Park 		return -ENOMEM;
1814c951cd3bSSeongJae Park 	}
1815c951cd3bSSeongJae Park 	err = kobject_init_and_add(&admin->kobj, &damon_sysfs_ui_dir_ktype,
1816c951cd3bSSeongJae Park 			damon_sysfs_root, "admin");
1817c951cd3bSSeongJae Park 	if (err)
1818c951cd3bSSeongJae Park 		goto out;
1819c951cd3bSSeongJae Park 	err = damon_sysfs_ui_dir_add_dirs(admin);
1820c951cd3bSSeongJae Park 	if (err)
1821c951cd3bSSeongJae Park 		goto out;
1822c951cd3bSSeongJae Park 	return 0;
1823c951cd3bSSeongJae Park 
1824c951cd3bSSeongJae Park out:
1825c951cd3bSSeongJae Park 	kobject_put(&admin->kobj);
1826c951cd3bSSeongJae Park 	kobject_put(damon_sysfs_root);
1827c951cd3bSSeongJae Park 	return err;
1828c951cd3bSSeongJae Park }
1829c951cd3bSSeongJae Park subsys_initcall(damon_sysfs_init);
1830