162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * DAMON sysfs Interface
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2022 SeongJae Park <sj@kernel.org>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/pid.h>
962306a36Sopenharmony_ci#include <linux/sched.h>
1062306a36Sopenharmony_ci#include <linux/slab.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include "sysfs-common.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci/*
1562306a36Sopenharmony_ci * init region directory
1662306a36Sopenharmony_ci */
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistruct damon_sysfs_region {
1962306a36Sopenharmony_ci	struct kobject kobj;
2062306a36Sopenharmony_ci	struct damon_addr_range ar;
2162306a36Sopenharmony_ci};
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistatic struct damon_sysfs_region *damon_sysfs_region_alloc(void)
2462306a36Sopenharmony_ci{
2562306a36Sopenharmony_ci	return kzalloc(sizeof(struct damon_sysfs_region), GFP_KERNEL);
2662306a36Sopenharmony_ci}
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic ssize_t start_show(struct kobject *kobj, struct kobj_attribute *attr,
2962306a36Sopenharmony_ci		char *buf)
3062306a36Sopenharmony_ci{
3162306a36Sopenharmony_ci	struct damon_sysfs_region *region = container_of(kobj,
3262306a36Sopenharmony_ci			struct damon_sysfs_region, kobj);
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	return sysfs_emit(buf, "%lu\n", region->ar.start);
3562306a36Sopenharmony_ci}
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic ssize_t start_store(struct kobject *kobj, struct kobj_attribute *attr,
3862306a36Sopenharmony_ci		const char *buf, size_t count)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	struct damon_sysfs_region *region = container_of(kobj,
4162306a36Sopenharmony_ci			struct damon_sysfs_region, kobj);
4262306a36Sopenharmony_ci	int err = kstrtoul(buf, 0, &region->ar.start);
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	return err ? err : count;
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic ssize_t end_show(struct kobject *kobj, struct kobj_attribute *attr,
4862306a36Sopenharmony_ci		char *buf)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	struct damon_sysfs_region *region = container_of(kobj,
5162306a36Sopenharmony_ci			struct damon_sysfs_region, kobj);
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	return sysfs_emit(buf, "%lu\n", region->ar.end);
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic ssize_t end_store(struct kobject *kobj, struct kobj_attribute *attr,
5762306a36Sopenharmony_ci		const char *buf, size_t count)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	struct damon_sysfs_region *region = container_of(kobj,
6062306a36Sopenharmony_ci			struct damon_sysfs_region, kobj);
6162306a36Sopenharmony_ci	int err = kstrtoul(buf, 0, &region->ar.end);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	return err ? err : count;
6462306a36Sopenharmony_ci}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistatic void damon_sysfs_region_release(struct kobject *kobj)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	kfree(container_of(kobj, struct damon_sysfs_region, kobj));
6962306a36Sopenharmony_ci}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic struct kobj_attribute damon_sysfs_region_start_attr =
7262306a36Sopenharmony_ci		__ATTR_RW_MODE(start, 0600);
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic struct kobj_attribute damon_sysfs_region_end_attr =
7562306a36Sopenharmony_ci		__ATTR_RW_MODE(end, 0600);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistatic struct attribute *damon_sysfs_region_attrs[] = {
7862306a36Sopenharmony_ci	&damon_sysfs_region_start_attr.attr,
7962306a36Sopenharmony_ci	&damon_sysfs_region_end_attr.attr,
8062306a36Sopenharmony_ci	NULL,
8162306a36Sopenharmony_ci};
8262306a36Sopenharmony_ciATTRIBUTE_GROUPS(damon_sysfs_region);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistatic const struct kobj_type damon_sysfs_region_ktype = {
8562306a36Sopenharmony_ci	.release = damon_sysfs_region_release,
8662306a36Sopenharmony_ci	.sysfs_ops = &kobj_sysfs_ops,
8762306a36Sopenharmony_ci	.default_groups = damon_sysfs_region_groups,
8862306a36Sopenharmony_ci};
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci/*
9162306a36Sopenharmony_ci * init_regions directory
9262306a36Sopenharmony_ci */
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistruct damon_sysfs_regions {
9562306a36Sopenharmony_ci	struct kobject kobj;
9662306a36Sopenharmony_ci	struct damon_sysfs_region **regions_arr;
9762306a36Sopenharmony_ci	int nr;
9862306a36Sopenharmony_ci};
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic struct damon_sysfs_regions *damon_sysfs_regions_alloc(void)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	return kzalloc(sizeof(struct damon_sysfs_regions), GFP_KERNEL);
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistatic void damon_sysfs_regions_rm_dirs(struct damon_sysfs_regions *regions)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	struct damon_sysfs_region **regions_arr = regions->regions_arr;
10862306a36Sopenharmony_ci	int i;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	for (i = 0; i < regions->nr; i++)
11162306a36Sopenharmony_ci		kobject_put(&regions_arr[i]->kobj);
11262306a36Sopenharmony_ci	regions->nr = 0;
11362306a36Sopenharmony_ci	kfree(regions_arr);
11462306a36Sopenharmony_ci	regions->regions_arr = NULL;
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic int damon_sysfs_regions_add_dirs(struct damon_sysfs_regions *regions,
11862306a36Sopenharmony_ci		int nr_regions)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	struct damon_sysfs_region **regions_arr, *region;
12162306a36Sopenharmony_ci	int err, i;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	damon_sysfs_regions_rm_dirs(regions);
12462306a36Sopenharmony_ci	if (!nr_regions)
12562306a36Sopenharmony_ci		return 0;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	regions_arr = kmalloc_array(nr_regions, sizeof(*regions_arr),
12862306a36Sopenharmony_ci			GFP_KERNEL | __GFP_NOWARN);
12962306a36Sopenharmony_ci	if (!regions_arr)
13062306a36Sopenharmony_ci		return -ENOMEM;
13162306a36Sopenharmony_ci	regions->regions_arr = regions_arr;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	for (i = 0; i < nr_regions; i++) {
13462306a36Sopenharmony_ci		region = damon_sysfs_region_alloc();
13562306a36Sopenharmony_ci		if (!region) {
13662306a36Sopenharmony_ci			damon_sysfs_regions_rm_dirs(regions);
13762306a36Sopenharmony_ci			return -ENOMEM;
13862306a36Sopenharmony_ci		}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci		err = kobject_init_and_add(&region->kobj,
14162306a36Sopenharmony_ci				&damon_sysfs_region_ktype, &regions->kobj,
14262306a36Sopenharmony_ci				"%d", i);
14362306a36Sopenharmony_ci		if (err) {
14462306a36Sopenharmony_ci			kobject_put(&region->kobj);
14562306a36Sopenharmony_ci			damon_sysfs_regions_rm_dirs(regions);
14662306a36Sopenharmony_ci			return err;
14762306a36Sopenharmony_ci		}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci		regions_arr[i] = region;
15062306a36Sopenharmony_ci		regions->nr++;
15162306a36Sopenharmony_ci	}
15262306a36Sopenharmony_ci	return 0;
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_cistatic ssize_t nr_regions_show(struct kobject *kobj,
15662306a36Sopenharmony_ci		struct kobj_attribute *attr, char *buf)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	struct damon_sysfs_regions *regions = container_of(kobj,
15962306a36Sopenharmony_ci			struct damon_sysfs_regions, kobj);
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	return sysfs_emit(buf, "%d\n", regions->nr);
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistatic ssize_t nr_regions_store(struct kobject *kobj,
16562306a36Sopenharmony_ci		struct kobj_attribute *attr, const char *buf, size_t count)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	struct damon_sysfs_regions *regions;
16862306a36Sopenharmony_ci	int nr, err = kstrtoint(buf, 0, &nr);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	if (err)
17162306a36Sopenharmony_ci		return err;
17262306a36Sopenharmony_ci	if (nr < 0)
17362306a36Sopenharmony_ci		return -EINVAL;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	regions = container_of(kobj, struct damon_sysfs_regions, kobj);
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	if (!mutex_trylock(&damon_sysfs_lock))
17862306a36Sopenharmony_ci		return -EBUSY;
17962306a36Sopenharmony_ci	err = damon_sysfs_regions_add_dirs(regions, nr);
18062306a36Sopenharmony_ci	mutex_unlock(&damon_sysfs_lock);
18162306a36Sopenharmony_ci	if (err)
18262306a36Sopenharmony_ci		return err;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	return count;
18562306a36Sopenharmony_ci}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_cistatic void damon_sysfs_regions_release(struct kobject *kobj)
18862306a36Sopenharmony_ci{
18962306a36Sopenharmony_ci	kfree(container_of(kobj, struct damon_sysfs_regions, kobj));
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cistatic struct kobj_attribute damon_sysfs_regions_nr_attr =
19362306a36Sopenharmony_ci		__ATTR_RW_MODE(nr_regions, 0600);
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_cistatic struct attribute *damon_sysfs_regions_attrs[] = {
19662306a36Sopenharmony_ci	&damon_sysfs_regions_nr_attr.attr,
19762306a36Sopenharmony_ci	NULL,
19862306a36Sopenharmony_ci};
19962306a36Sopenharmony_ciATTRIBUTE_GROUPS(damon_sysfs_regions);
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic const struct kobj_type damon_sysfs_regions_ktype = {
20262306a36Sopenharmony_ci	.release = damon_sysfs_regions_release,
20362306a36Sopenharmony_ci	.sysfs_ops = &kobj_sysfs_ops,
20462306a36Sopenharmony_ci	.default_groups = damon_sysfs_regions_groups,
20562306a36Sopenharmony_ci};
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci/*
20862306a36Sopenharmony_ci * target directory
20962306a36Sopenharmony_ci */
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_cistruct damon_sysfs_target {
21262306a36Sopenharmony_ci	struct kobject kobj;
21362306a36Sopenharmony_ci	struct damon_sysfs_regions *regions;
21462306a36Sopenharmony_ci	int pid;
21562306a36Sopenharmony_ci};
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_cistatic struct damon_sysfs_target *damon_sysfs_target_alloc(void)
21862306a36Sopenharmony_ci{
21962306a36Sopenharmony_ci	return kzalloc(sizeof(struct damon_sysfs_target), GFP_KERNEL);
22062306a36Sopenharmony_ci}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_cistatic int damon_sysfs_target_add_dirs(struct damon_sysfs_target *target)
22362306a36Sopenharmony_ci{
22462306a36Sopenharmony_ci	struct damon_sysfs_regions *regions = damon_sysfs_regions_alloc();
22562306a36Sopenharmony_ci	int err;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	if (!regions)
22862306a36Sopenharmony_ci		return -ENOMEM;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	err = kobject_init_and_add(&regions->kobj, &damon_sysfs_regions_ktype,
23162306a36Sopenharmony_ci			&target->kobj, "regions");
23262306a36Sopenharmony_ci	if (err)
23362306a36Sopenharmony_ci		kobject_put(&regions->kobj);
23462306a36Sopenharmony_ci	else
23562306a36Sopenharmony_ci		target->regions = regions;
23662306a36Sopenharmony_ci	return err;
23762306a36Sopenharmony_ci}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_cistatic void damon_sysfs_target_rm_dirs(struct damon_sysfs_target *target)
24062306a36Sopenharmony_ci{
24162306a36Sopenharmony_ci	damon_sysfs_regions_rm_dirs(target->regions);
24262306a36Sopenharmony_ci	kobject_put(&target->regions->kobj);
24362306a36Sopenharmony_ci}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_cistatic ssize_t pid_target_show(struct kobject *kobj,
24662306a36Sopenharmony_ci		struct kobj_attribute *attr, char *buf)
24762306a36Sopenharmony_ci{
24862306a36Sopenharmony_ci	struct damon_sysfs_target *target = container_of(kobj,
24962306a36Sopenharmony_ci			struct damon_sysfs_target, kobj);
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	return sysfs_emit(buf, "%d\n", target->pid);
25262306a36Sopenharmony_ci}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_cistatic ssize_t pid_target_store(struct kobject *kobj,
25562306a36Sopenharmony_ci		struct kobj_attribute *attr, const char *buf, size_t count)
25662306a36Sopenharmony_ci{
25762306a36Sopenharmony_ci	struct damon_sysfs_target *target = container_of(kobj,
25862306a36Sopenharmony_ci			struct damon_sysfs_target, kobj);
25962306a36Sopenharmony_ci	int err = kstrtoint(buf, 0, &target->pid);
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	if (err)
26262306a36Sopenharmony_ci		return -EINVAL;
26362306a36Sopenharmony_ci	return count;
26462306a36Sopenharmony_ci}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_cistatic void damon_sysfs_target_release(struct kobject *kobj)
26762306a36Sopenharmony_ci{
26862306a36Sopenharmony_ci	kfree(container_of(kobj, struct damon_sysfs_target, kobj));
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_cistatic struct kobj_attribute damon_sysfs_target_pid_attr =
27262306a36Sopenharmony_ci		__ATTR_RW_MODE(pid_target, 0600);
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_cistatic struct attribute *damon_sysfs_target_attrs[] = {
27562306a36Sopenharmony_ci	&damon_sysfs_target_pid_attr.attr,
27662306a36Sopenharmony_ci	NULL,
27762306a36Sopenharmony_ci};
27862306a36Sopenharmony_ciATTRIBUTE_GROUPS(damon_sysfs_target);
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_cistatic const struct kobj_type damon_sysfs_target_ktype = {
28162306a36Sopenharmony_ci	.release = damon_sysfs_target_release,
28262306a36Sopenharmony_ci	.sysfs_ops = &kobj_sysfs_ops,
28362306a36Sopenharmony_ci	.default_groups = damon_sysfs_target_groups,
28462306a36Sopenharmony_ci};
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci/*
28762306a36Sopenharmony_ci * targets directory
28862306a36Sopenharmony_ci */
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_cistruct damon_sysfs_targets {
29162306a36Sopenharmony_ci	struct kobject kobj;
29262306a36Sopenharmony_ci	struct damon_sysfs_target **targets_arr;
29362306a36Sopenharmony_ci	int nr;
29462306a36Sopenharmony_ci};
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_cistatic struct damon_sysfs_targets *damon_sysfs_targets_alloc(void)
29762306a36Sopenharmony_ci{
29862306a36Sopenharmony_ci	return kzalloc(sizeof(struct damon_sysfs_targets), GFP_KERNEL);
29962306a36Sopenharmony_ci}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_cistatic void damon_sysfs_targets_rm_dirs(struct damon_sysfs_targets *targets)
30262306a36Sopenharmony_ci{
30362306a36Sopenharmony_ci	struct damon_sysfs_target **targets_arr = targets->targets_arr;
30462306a36Sopenharmony_ci	int i;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	for (i = 0; i < targets->nr; i++) {
30762306a36Sopenharmony_ci		damon_sysfs_target_rm_dirs(targets_arr[i]);
30862306a36Sopenharmony_ci		kobject_put(&targets_arr[i]->kobj);
30962306a36Sopenharmony_ci	}
31062306a36Sopenharmony_ci	targets->nr = 0;
31162306a36Sopenharmony_ci	kfree(targets_arr);
31262306a36Sopenharmony_ci	targets->targets_arr = NULL;
31362306a36Sopenharmony_ci}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_cistatic int damon_sysfs_targets_add_dirs(struct damon_sysfs_targets *targets,
31662306a36Sopenharmony_ci		int nr_targets)
31762306a36Sopenharmony_ci{
31862306a36Sopenharmony_ci	struct damon_sysfs_target **targets_arr, *target;
31962306a36Sopenharmony_ci	int err, i;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	damon_sysfs_targets_rm_dirs(targets);
32262306a36Sopenharmony_ci	if (!nr_targets)
32362306a36Sopenharmony_ci		return 0;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	targets_arr = kmalloc_array(nr_targets, sizeof(*targets_arr),
32662306a36Sopenharmony_ci			GFP_KERNEL | __GFP_NOWARN);
32762306a36Sopenharmony_ci	if (!targets_arr)
32862306a36Sopenharmony_ci		return -ENOMEM;
32962306a36Sopenharmony_ci	targets->targets_arr = targets_arr;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	for (i = 0; i < nr_targets; i++) {
33262306a36Sopenharmony_ci		target = damon_sysfs_target_alloc();
33362306a36Sopenharmony_ci		if (!target) {
33462306a36Sopenharmony_ci			damon_sysfs_targets_rm_dirs(targets);
33562306a36Sopenharmony_ci			return -ENOMEM;
33662306a36Sopenharmony_ci		}
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci		err = kobject_init_and_add(&target->kobj,
33962306a36Sopenharmony_ci				&damon_sysfs_target_ktype, &targets->kobj,
34062306a36Sopenharmony_ci				"%d", i);
34162306a36Sopenharmony_ci		if (err)
34262306a36Sopenharmony_ci			goto out;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci		err = damon_sysfs_target_add_dirs(target);
34562306a36Sopenharmony_ci		if (err)
34662306a36Sopenharmony_ci			goto out;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci		targets_arr[i] = target;
34962306a36Sopenharmony_ci		targets->nr++;
35062306a36Sopenharmony_ci	}
35162306a36Sopenharmony_ci	return 0;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ciout:
35462306a36Sopenharmony_ci	damon_sysfs_targets_rm_dirs(targets);
35562306a36Sopenharmony_ci	kobject_put(&target->kobj);
35662306a36Sopenharmony_ci	return err;
35762306a36Sopenharmony_ci}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_cistatic ssize_t nr_targets_show(struct kobject *kobj,
36062306a36Sopenharmony_ci		struct kobj_attribute *attr, char *buf)
36162306a36Sopenharmony_ci{
36262306a36Sopenharmony_ci	struct damon_sysfs_targets *targets = container_of(kobj,
36362306a36Sopenharmony_ci			struct damon_sysfs_targets, kobj);
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	return sysfs_emit(buf, "%d\n", targets->nr);
36662306a36Sopenharmony_ci}
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_cistatic ssize_t nr_targets_store(struct kobject *kobj,
36962306a36Sopenharmony_ci		struct kobj_attribute *attr, const char *buf, size_t count)
37062306a36Sopenharmony_ci{
37162306a36Sopenharmony_ci	struct damon_sysfs_targets *targets;
37262306a36Sopenharmony_ci	int nr, err = kstrtoint(buf, 0, &nr);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	if (err)
37562306a36Sopenharmony_ci		return err;
37662306a36Sopenharmony_ci	if (nr < 0)
37762306a36Sopenharmony_ci		return -EINVAL;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	targets = container_of(kobj, struct damon_sysfs_targets, kobj);
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	if (!mutex_trylock(&damon_sysfs_lock))
38262306a36Sopenharmony_ci		return -EBUSY;
38362306a36Sopenharmony_ci	err = damon_sysfs_targets_add_dirs(targets, nr);
38462306a36Sopenharmony_ci	mutex_unlock(&damon_sysfs_lock);
38562306a36Sopenharmony_ci	if (err)
38662306a36Sopenharmony_ci		return err;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	return count;
38962306a36Sopenharmony_ci}
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_cistatic void damon_sysfs_targets_release(struct kobject *kobj)
39262306a36Sopenharmony_ci{
39362306a36Sopenharmony_ci	kfree(container_of(kobj, struct damon_sysfs_targets, kobj));
39462306a36Sopenharmony_ci}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_cistatic struct kobj_attribute damon_sysfs_targets_nr_attr =
39762306a36Sopenharmony_ci		__ATTR_RW_MODE(nr_targets, 0600);
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_cistatic struct attribute *damon_sysfs_targets_attrs[] = {
40062306a36Sopenharmony_ci	&damon_sysfs_targets_nr_attr.attr,
40162306a36Sopenharmony_ci	NULL,
40262306a36Sopenharmony_ci};
40362306a36Sopenharmony_ciATTRIBUTE_GROUPS(damon_sysfs_targets);
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_cistatic const struct kobj_type damon_sysfs_targets_ktype = {
40662306a36Sopenharmony_ci	.release = damon_sysfs_targets_release,
40762306a36Sopenharmony_ci	.sysfs_ops = &kobj_sysfs_ops,
40862306a36Sopenharmony_ci	.default_groups = damon_sysfs_targets_groups,
40962306a36Sopenharmony_ci};
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci/*
41262306a36Sopenharmony_ci * intervals directory
41362306a36Sopenharmony_ci */
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_cistruct damon_sysfs_intervals {
41662306a36Sopenharmony_ci	struct kobject kobj;
41762306a36Sopenharmony_ci	unsigned long sample_us;
41862306a36Sopenharmony_ci	unsigned long aggr_us;
41962306a36Sopenharmony_ci	unsigned long update_us;
42062306a36Sopenharmony_ci};
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_cistatic struct damon_sysfs_intervals *damon_sysfs_intervals_alloc(
42362306a36Sopenharmony_ci		unsigned long sample_us, unsigned long aggr_us,
42462306a36Sopenharmony_ci		unsigned long update_us)
42562306a36Sopenharmony_ci{
42662306a36Sopenharmony_ci	struct damon_sysfs_intervals *intervals = kmalloc(sizeof(*intervals),
42762306a36Sopenharmony_ci			GFP_KERNEL);
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	if (!intervals)
43062306a36Sopenharmony_ci		return NULL;
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	intervals->kobj = (struct kobject){};
43362306a36Sopenharmony_ci	intervals->sample_us = sample_us;
43462306a36Sopenharmony_ci	intervals->aggr_us = aggr_us;
43562306a36Sopenharmony_ci	intervals->update_us = update_us;
43662306a36Sopenharmony_ci	return intervals;
43762306a36Sopenharmony_ci}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_cistatic ssize_t sample_us_show(struct kobject *kobj,
44062306a36Sopenharmony_ci		struct kobj_attribute *attr, char *buf)
44162306a36Sopenharmony_ci{
44262306a36Sopenharmony_ci	struct damon_sysfs_intervals *intervals = container_of(kobj,
44362306a36Sopenharmony_ci			struct damon_sysfs_intervals, kobj);
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	return sysfs_emit(buf, "%lu\n", intervals->sample_us);
44662306a36Sopenharmony_ci}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_cistatic ssize_t sample_us_store(struct kobject *kobj,
44962306a36Sopenharmony_ci		struct kobj_attribute *attr, const char *buf, size_t count)
45062306a36Sopenharmony_ci{
45162306a36Sopenharmony_ci	struct damon_sysfs_intervals *intervals = container_of(kobj,
45262306a36Sopenharmony_ci			struct damon_sysfs_intervals, kobj);
45362306a36Sopenharmony_ci	unsigned long us;
45462306a36Sopenharmony_ci	int err = kstrtoul(buf, 0, &us);
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	if (err)
45762306a36Sopenharmony_ci		return err;
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	intervals->sample_us = us;
46062306a36Sopenharmony_ci	return count;
46162306a36Sopenharmony_ci}
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_cistatic ssize_t aggr_us_show(struct kobject *kobj, struct kobj_attribute *attr,
46462306a36Sopenharmony_ci		char *buf)
46562306a36Sopenharmony_ci{
46662306a36Sopenharmony_ci	struct damon_sysfs_intervals *intervals = container_of(kobj,
46762306a36Sopenharmony_ci			struct damon_sysfs_intervals, kobj);
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	return sysfs_emit(buf, "%lu\n", intervals->aggr_us);
47062306a36Sopenharmony_ci}
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_cistatic ssize_t aggr_us_store(struct kobject *kobj, struct kobj_attribute *attr,
47362306a36Sopenharmony_ci		const char *buf, size_t count)
47462306a36Sopenharmony_ci{
47562306a36Sopenharmony_ci	struct damon_sysfs_intervals *intervals = container_of(kobj,
47662306a36Sopenharmony_ci			struct damon_sysfs_intervals, kobj);
47762306a36Sopenharmony_ci	unsigned long us;
47862306a36Sopenharmony_ci	int err = kstrtoul(buf, 0, &us);
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	if (err)
48162306a36Sopenharmony_ci		return err;
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	intervals->aggr_us = us;
48462306a36Sopenharmony_ci	return count;
48562306a36Sopenharmony_ci}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_cistatic ssize_t update_us_show(struct kobject *kobj,
48862306a36Sopenharmony_ci		struct kobj_attribute *attr, char *buf)
48962306a36Sopenharmony_ci{
49062306a36Sopenharmony_ci	struct damon_sysfs_intervals *intervals = container_of(kobj,
49162306a36Sopenharmony_ci			struct damon_sysfs_intervals, kobj);
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	return sysfs_emit(buf, "%lu\n", intervals->update_us);
49462306a36Sopenharmony_ci}
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_cistatic ssize_t update_us_store(struct kobject *kobj,
49762306a36Sopenharmony_ci		struct kobj_attribute *attr, const char *buf, size_t count)
49862306a36Sopenharmony_ci{
49962306a36Sopenharmony_ci	struct damon_sysfs_intervals *intervals = container_of(kobj,
50062306a36Sopenharmony_ci			struct damon_sysfs_intervals, kobj);
50162306a36Sopenharmony_ci	unsigned long us;
50262306a36Sopenharmony_ci	int err = kstrtoul(buf, 0, &us);
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	if (err)
50562306a36Sopenharmony_ci		return err;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	intervals->update_us = us;
50862306a36Sopenharmony_ci	return count;
50962306a36Sopenharmony_ci}
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_cistatic void damon_sysfs_intervals_release(struct kobject *kobj)
51262306a36Sopenharmony_ci{
51362306a36Sopenharmony_ci	kfree(container_of(kobj, struct damon_sysfs_intervals, kobj));
51462306a36Sopenharmony_ci}
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_cistatic struct kobj_attribute damon_sysfs_intervals_sample_us_attr =
51762306a36Sopenharmony_ci		__ATTR_RW_MODE(sample_us, 0600);
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_cistatic struct kobj_attribute damon_sysfs_intervals_aggr_us_attr =
52062306a36Sopenharmony_ci		__ATTR_RW_MODE(aggr_us, 0600);
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_cistatic struct kobj_attribute damon_sysfs_intervals_update_us_attr =
52362306a36Sopenharmony_ci		__ATTR_RW_MODE(update_us, 0600);
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_cistatic struct attribute *damon_sysfs_intervals_attrs[] = {
52662306a36Sopenharmony_ci	&damon_sysfs_intervals_sample_us_attr.attr,
52762306a36Sopenharmony_ci	&damon_sysfs_intervals_aggr_us_attr.attr,
52862306a36Sopenharmony_ci	&damon_sysfs_intervals_update_us_attr.attr,
52962306a36Sopenharmony_ci	NULL,
53062306a36Sopenharmony_ci};
53162306a36Sopenharmony_ciATTRIBUTE_GROUPS(damon_sysfs_intervals);
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_cistatic const struct kobj_type damon_sysfs_intervals_ktype = {
53462306a36Sopenharmony_ci	.release = damon_sysfs_intervals_release,
53562306a36Sopenharmony_ci	.sysfs_ops = &kobj_sysfs_ops,
53662306a36Sopenharmony_ci	.default_groups = damon_sysfs_intervals_groups,
53762306a36Sopenharmony_ci};
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci/*
54062306a36Sopenharmony_ci * monitoring_attrs directory
54162306a36Sopenharmony_ci */
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_cistruct damon_sysfs_attrs {
54462306a36Sopenharmony_ci	struct kobject kobj;
54562306a36Sopenharmony_ci	struct damon_sysfs_intervals *intervals;
54662306a36Sopenharmony_ci	struct damon_sysfs_ul_range *nr_regions_range;
54762306a36Sopenharmony_ci};
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_cistatic struct damon_sysfs_attrs *damon_sysfs_attrs_alloc(void)
55062306a36Sopenharmony_ci{
55162306a36Sopenharmony_ci	struct damon_sysfs_attrs *attrs = kmalloc(sizeof(*attrs), GFP_KERNEL);
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	if (!attrs)
55462306a36Sopenharmony_ci		return NULL;
55562306a36Sopenharmony_ci	attrs->kobj = (struct kobject){};
55662306a36Sopenharmony_ci	return attrs;
55762306a36Sopenharmony_ci}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_cistatic int damon_sysfs_attrs_add_dirs(struct damon_sysfs_attrs *attrs)
56062306a36Sopenharmony_ci{
56162306a36Sopenharmony_ci	struct damon_sysfs_intervals *intervals;
56262306a36Sopenharmony_ci	struct damon_sysfs_ul_range *nr_regions_range;
56362306a36Sopenharmony_ci	int err;
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	intervals = damon_sysfs_intervals_alloc(5000, 100000, 60000000);
56662306a36Sopenharmony_ci	if (!intervals)
56762306a36Sopenharmony_ci		return -ENOMEM;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	err = kobject_init_and_add(&intervals->kobj,
57062306a36Sopenharmony_ci			&damon_sysfs_intervals_ktype, &attrs->kobj,
57162306a36Sopenharmony_ci			"intervals");
57262306a36Sopenharmony_ci	if (err)
57362306a36Sopenharmony_ci		goto put_intervals_out;
57462306a36Sopenharmony_ci	attrs->intervals = intervals;
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	nr_regions_range = damon_sysfs_ul_range_alloc(10, 1000);
57762306a36Sopenharmony_ci	if (!nr_regions_range) {
57862306a36Sopenharmony_ci		err = -ENOMEM;
57962306a36Sopenharmony_ci		goto put_intervals_out;
58062306a36Sopenharmony_ci	}
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	err = kobject_init_and_add(&nr_regions_range->kobj,
58362306a36Sopenharmony_ci			&damon_sysfs_ul_range_ktype, &attrs->kobj,
58462306a36Sopenharmony_ci			"nr_regions");
58562306a36Sopenharmony_ci	if (err)
58662306a36Sopenharmony_ci		goto put_nr_regions_intervals_out;
58762306a36Sopenharmony_ci	attrs->nr_regions_range = nr_regions_range;
58862306a36Sopenharmony_ci	return 0;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ciput_nr_regions_intervals_out:
59162306a36Sopenharmony_ci	kobject_put(&nr_regions_range->kobj);
59262306a36Sopenharmony_ci	attrs->nr_regions_range = NULL;
59362306a36Sopenharmony_ciput_intervals_out:
59462306a36Sopenharmony_ci	kobject_put(&intervals->kobj);
59562306a36Sopenharmony_ci	attrs->intervals = NULL;
59662306a36Sopenharmony_ci	return err;
59762306a36Sopenharmony_ci}
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_cistatic void damon_sysfs_attrs_rm_dirs(struct damon_sysfs_attrs *attrs)
60062306a36Sopenharmony_ci{
60162306a36Sopenharmony_ci	kobject_put(&attrs->nr_regions_range->kobj);
60262306a36Sopenharmony_ci	kobject_put(&attrs->intervals->kobj);
60362306a36Sopenharmony_ci}
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_cistatic void damon_sysfs_attrs_release(struct kobject *kobj)
60662306a36Sopenharmony_ci{
60762306a36Sopenharmony_ci	kfree(container_of(kobj, struct damon_sysfs_attrs, kobj));
60862306a36Sopenharmony_ci}
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_cistatic struct attribute *damon_sysfs_attrs_attrs[] = {
61162306a36Sopenharmony_ci	NULL,
61262306a36Sopenharmony_ci};
61362306a36Sopenharmony_ciATTRIBUTE_GROUPS(damon_sysfs_attrs);
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_cistatic const struct kobj_type damon_sysfs_attrs_ktype = {
61662306a36Sopenharmony_ci	.release = damon_sysfs_attrs_release,
61762306a36Sopenharmony_ci	.sysfs_ops = &kobj_sysfs_ops,
61862306a36Sopenharmony_ci	.default_groups = damon_sysfs_attrs_groups,
61962306a36Sopenharmony_ci};
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci/*
62262306a36Sopenharmony_ci * context directory
62362306a36Sopenharmony_ci */
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci/* This should match with enum damon_ops_id */
62662306a36Sopenharmony_cistatic const char * const damon_sysfs_ops_strs[] = {
62762306a36Sopenharmony_ci	"vaddr",
62862306a36Sopenharmony_ci	"fvaddr",
62962306a36Sopenharmony_ci	"paddr",
63062306a36Sopenharmony_ci};
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_cistruct damon_sysfs_context {
63362306a36Sopenharmony_ci	struct kobject kobj;
63462306a36Sopenharmony_ci	enum damon_ops_id ops_id;
63562306a36Sopenharmony_ci	struct damon_sysfs_attrs *attrs;
63662306a36Sopenharmony_ci	struct damon_sysfs_targets *targets;
63762306a36Sopenharmony_ci	struct damon_sysfs_schemes *schemes;
63862306a36Sopenharmony_ci};
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_cistatic struct damon_sysfs_context *damon_sysfs_context_alloc(
64162306a36Sopenharmony_ci		enum damon_ops_id ops_id)
64262306a36Sopenharmony_ci{
64362306a36Sopenharmony_ci	struct damon_sysfs_context *context = kmalloc(sizeof(*context),
64462306a36Sopenharmony_ci				GFP_KERNEL);
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	if (!context)
64762306a36Sopenharmony_ci		return NULL;
64862306a36Sopenharmony_ci	context->kobj = (struct kobject){};
64962306a36Sopenharmony_ci	context->ops_id = ops_id;
65062306a36Sopenharmony_ci	return context;
65162306a36Sopenharmony_ci}
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_cistatic int damon_sysfs_context_set_attrs(struct damon_sysfs_context *context)
65462306a36Sopenharmony_ci{
65562306a36Sopenharmony_ci	struct damon_sysfs_attrs *attrs = damon_sysfs_attrs_alloc();
65662306a36Sopenharmony_ci	int err;
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	if (!attrs)
65962306a36Sopenharmony_ci		return -ENOMEM;
66062306a36Sopenharmony_ci	err = kobject_init_and_add(&attrs->kobj, &damon_sysfs_attrs_ktype,
66162306a36Sopenharmony_ci			&context->kobj, "monitoring_attrs");
66262306a36Sopenharmony_ci	if (err)
66362306a36Sopenharmony_ci		goto out;
66462306a36Sopenharmony_ci	err = damon_sysfs_attrs_add_dirs(attrs);
66562306a36Sopenharmony_ci	if (err)
66662306a36Sopenharmony_ci		goto out;
66762306a36Sopenharmony_ci	context->attrs = attrs;
66862306a36Sopenharmony_ci	return 0;
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ciout:
67162306a36Sopenharmony_ci	kobject_put(&attrs->kobj);
67262306a36Sopenharmony_ci	return err;
67362306a36Sopenharmony_ci}
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_cistatic int damon_sysfs_context_set_targets(struct damon_sysfs_context *context)
67662306a36Sopenharmony_ci{
67762306a36Sopenharmony_ci	struct damon_sysfs_targets *targets = damon_sysfs_targets_alloc();
67862306a36Sopenharmony_ci	int err;
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	if (!targets)
68162306a36Sopenharmony_ci		return -ENOMEM;
68262306a36Sopenharmony_ci	err = kobject_init_and_add(&targets->kobj, &damon_sysfs_targets_ktype,
68362306a36Sopenharmony_ci			&context->kobj, "targets");
68462306a36Sopenharmony_ci	if (err) {
68562306a36Sopenharmony_ci		kobject_put(&targets->kobj);
68662306a36Sopenharmony_ci		return err;
68762306a36Sopenharmony_ci	}
68862306a36Sopenharmony_ci	context->targets = targets;
68962306a36Sopenharmony_ci	return 0;
69062306a36Sopenharmony_ci}
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_cistatic int damon_sysfs_context_set_schemes(struct damon_sysfs_context *context)
69362306a36Sopenharmony_ci{
69462306a36Sopenharmony_ci	struct damon_sysfs_schemes *schemes = damon_sysfs_schemes_alloc();
69562306a36Sopenharmony_ci	int err;
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	if (!schemes)
69862306a36Sopenharmony_ci		return -ENOMEM;
69962306a36Sopenharmony_ci	err = kobject_init_and_add(&schemes->kobj, &damon_sysfs_schemes_ktype,
70062306a36Sopenharmony_ci			&context->kobj, "schemes");
70162306a36Sopenharmony_ci	if (err) {
70262306a36Sopenharmony_ci		kobject_put(&schemes->kobj);
70362306a36Sopenharmony_ci		return err;
70462306a36Sopenharmony_ci	}
70562306a36Sopenharmony_ci	context->schemes = schemes;
70662306a36Sopenharmony_ci	return 0;
70762306a36Sopenharmony_ci}
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_cistatic int damon_sysfs_context_add_dirs(struct damon_sysfs_context *context)
71062306a36Sopenharmony_ci{
71162306a36Sopenharmony_ci	int err;
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	err = damon_sysfs_context_set_attrs(context);
71462306a36Sopenharmony_ci	if (err)
71562306a36Sopenharmony_ci		return err;
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	err = damon_sysfs_context_set_targets(context);
71862306a36Sopenharmony_ci	if (err)
71962306a36Sopenharmony_ci		goto put_attrs_out;
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	err = damon_sysfs_context_set_schemes(context);
72262306a36Sopenharmony_ci	if (err)
72362306a36Sopenharmony_ci		goto put_targets_attrs_out;
72462306a36Sopenharmony_ci	return 0;
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ciput_targets_attrs_out:
72762306a36Sopenharmony_ci	kobject_put(&context->targets->kobj);
72862306a36Sopenharmony_ci	context->targets = NULL;
72962306a36Sopenharmony_ciput_attrs_out:
73062306a36Sopenharmony_ci	kobject_put(&context->attrs->kobj);
73162306a36Sopenharmony_ci	context->attrs = NULL;
73262306a36Sopenharmony_ci	return err;
73362306a36Sopenharmony_ci}
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_cistatic void damon_sysfs_context_rm_dirs(struct damon_sysfs_context *context)
73662306a36Sopenharmony_ci{
73762306a36Sopenharmony_ci	damon_sysfs_attrs_rm_dirs(context->attrs);
73862306a36Sopenharmony_ci	kobject_put(&context->attrs->kobj);
73962306a36Sopenharmony_ci	damon_sysfs_targets_rm_dirs(context->targets);
74062306a36Sopenharmony_ci	kobject_put(&context->targets->kobj);
74162306a36Sopenharmony_ci	damon_sysfs_schemes_rm_dirs(context->schemes);
74262306a36Sopenharmony_ci	kobject_put(&context->schemes->kobj);
74362306a36Sopenharmony_ci}
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_cistatic ssize_t avail_operations_show(struct kobject *kobj,
74662306a36Sopenharmony_ci		struct kobj_attribute *attr, char *buf)
74762306a36Sopenharmony_ci{
74862306a36Sopenharmony_ci	enum damon_ops_id id;
74962306a36Sopenharmony_ci	int len = 0;
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	for (id = 0; id < NR_DAMON_OPS; id++) {
75262306a36Sopenharmony_ci		if (!damon_is_registered_ops(id))
75362306a36Sopenharmony_ci			continue;
75462306a36Sopenharmony_ci		len += sysfs_emit_at(buf, len, "%s\n",
75562306a36Sopenharmony_ci				damon_sysfs_ops_strs[id]);
75662306a36Sopenharmony_ci	}
75762306a36Sopenharmony_ci	return len;
75862306a36Sopenharmony_ci}
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_cistatic ssize_t operations_show(struct kobject *kobj,
76162306a36Sopenharmony_ci		struct kobj_attribute *attr, char *buf)
76262306a36Sopenharmony_ci{
76362306a36Sopenharmony_ci	struct damon_sysfs_context *context = container_of(kobj,
76462306a36Sopenharmony_ci			struct damon_sysfs_context, kobj);
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	return sysfs_emit(buf, "%s\n", damon_sysfs_ops_strs[context->ops_id]);
76762306a36Sopenharmony_ci}
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_cistatic ssize_t operations_store(struct kobject *kobj,
77062306a36Sopenharmony_ci		struct kobj_attribute *attr, const char *buf, size_t count)
77162306a36Sopenharmony_ci{
77262306a36Sopenharmony_ci	struct damon_sysfs_context *context = container_of(kobj,
77362306a36Sopenharmony_ci			struct damon_sysfs_context, kobj);
77462306a36Sopenharmony_ci	enum damon_ops_id id;
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	for (id = 0; id < NR_DAMON_OPS; id++) {
77762306a36Sopenharmony_ci		if (sysfs_streq(buf, damon_sysfs_ops_strs[id])) {
77862306a36Sopenharmony_ci			context->ops_id = id;
77962306a36Sopenharmony_ci			return count;
78062306a36Sopenharmony_ci		}
78162306a36Sopenharmony_ci	}
78262306a36Sopenharmony_ci	return -EINVAL;
78362306a36Sopenharmony_ci}
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_cistatic void damon_sysfs_context_release(struct kobject *kobj)
78662306a36Sopenharmony_ci{
78762306a36Sopenharmony_ci	kfree(container_of(kobj, struct damon_sysfs_context, kobj));
78862306a36Sopenharmony_ci}
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_cistatic struct kobj_attribute damon_sysfs_context_avail_operations_attr =
79162306a36Sopenharmony_ci		__ATTR_RO_MODE(avail_operations, 0400);
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_cistatic struct kobj_attribute damon_sysfs_context_operations_attr =
79462306a36Sopenharmony_ci		__ATTR_RW_MODE(operations, 0600);
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_cistatic struct attribute *damon_sysfs_context_attrs[] = {
79762306a36Sopenharmony_ci	&damon_sysfs_context_avail_operations_attr.attr,
79862306a36Sopenharmony_ci	&damon_sysfs_context_operations_attr.attr,
79962306a36Sopenharmony_ci	NULL,
80062306a36Sopenharmony_ci};
80162306a36Sopenharmony_ciATTRIBUTE_GROUPS(damon_sysfs_context);
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_cistatic const struct kobj_type damon_sysfs_context_ktype = {
80462306a36Sopenharmony_ci	.release = damon_sysfs_context_release,
80562306a36Sopenharmony_ci	.sysfs_ops = &kobj_sysfs_ops,
80662306a36Sopenharmony_ci	.default_groups = damon_sysfs_context_groups,
80762306a36Sopenharmony_ci};
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci/*
81062306a36Sopenharmony_ci * contexts directory
81162306a36Sopenharmony_ci */
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_cistruct damon_sysfs_contexts {
81462306a36Sopenharmony_ci	struct kobject kobj;
81562306a36Sopenharmony_ci	struct damon_sysfs_context **contexts_arr;
81662306a36Sopenharmony_ci	int nr;
81762306a36Sopenharmony_ci};
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_cistatic struct damon_sysfs_contexts *damon_sysfs_contexts_alloc(void)
82062306a36Sopenharmony_ci{
82162306a36Sopenharmony_ci	return kzalloc(sizeof(struct damon_sysfs_contexts), GFP_KERNEL);
82262306a36Sopenharmony_ci}
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_cistatic void damon_sysfs_contexts_rm_dirs(struct damon_sysfs_contexts *contexts)
82562306a36Sopenharmony_ci{
82662306a36Sopenharmony_ci	struct damon_sysfs_context **contexts_arr = contexts->contexts_arr;
82762306a36Sopenharmony_ci	int i;
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	for (i = 0; i < contexts->nr; i++) {
83062306a36Sopenharmony_ci		damon_sysfs_context_rm_dirs(contexts_arr[i]);
83162306a36Sopenharmony_ci		kobject_put(&contexts_arr[i]->kobj);
83262306a36Sopenharmony_ci	}
83362306a36Sopenharmony_ci	contexts->nr = 0;
83462306a36Sopenharmony_ci	kfree(contexts_arr);
83562306a36Sopenharmony_ci	contexts->contexts_arr = NULL;
83662306a36Sopenharmony_ci}
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_cistatic int damon_sysfs_contexts_add_dirs(struct damon_sysfs_contexts *contexts,
83962306a36Sopenharmony_ci		int nr_contexts)
84062306a36Sopenharmony_ci{
84162306a36Sopenharmony_ci	struct damon_sysfs_context **contexts_arr, *context;
84262306a36Sopenharmony_ci	int err, i;
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	damon_sysfs_contexts_rm_dirs(contexts);
84562306a36Sopenharmony_ci	if (!nr_contexts)
84662306a36Sopenharmony_ci		return 0;
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	contexts_arr = kmalloc_array(nr_contexts, sizeof(*contexts_arr),
84962306a36Sopenharmony_ci			GFP_KERNEL | __GFP_NOWARN);
85062306a36Sopenharmony_ci	if (!contexts_arr)
85162306a36Sopenharmony_ci		return -ENOMEM;
85262306a36Sopenharmony_ci	contexts->contexts_arr = contexts_arr;
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	for (i = 0; i < nr_contexts; i++) {
85562306a36Sopenharmony_ci		context = damon_sysfs_context_alloc(DAMON_OPS_VADDR);
85662306a36Sopenharmony_ci		if (!context) {
85762306a36Sopenharmony_ci			damon_sysfs_contexts_rm_dirs(contexts);
85862306a36Sopenharmony_ci			return -ENOMEM;
85962306a36Sopenharmony_ci		}
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci		err = kobject_init_and_add(&context->kobj,
86262306a36Sopenharmony_ci				&damon_sysfs_context_ktype, &contexts->kobj,
86362306a36Sopenharmony_ci				"%d", i);
86462306a36Sopenharmony_ci		if (err)
86562306a36Sopenharmony_ci			goto out;
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci		err = damon_sysfs_context_add_dirs(context);
86862306a36Sopenharmony_ci		if (err)
86962306a36Sopenharmony_ci			goto out;
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci		contexts_arr[i] = context;
87262306a36Sopenharmony_ci		contexts->nr++;
87362306a36Sopenharmony_ci	}
87462306a36Sopenharmony_ci	return 0;
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ciout:
87762306a36Sopenharmony_ci	damon_sysfs_contexts_rm_dirs(contexts);
87862306a36Sopenharmony_ci	kobject_put(&context->kobj);
87962306a36Sopenharmony_ci	return err;
88062306a36Sopenharmony_ci}
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_cistatic ssize_t nr_contexts_show(struct kobject *kobj,
88362306a36Sopenharmony_ci		struct kobj_attribute *attr, char *buf)
88462306a36Sopenharmony_ci{
88562306a36Sopenharmony_ci	struct damon_sysfs_contexts *contexts = container_of(kobj,
88662306a36Sopenharmony_ci			struct damon_sysfs_contexts, kobj);
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	return sysfs_emit(buf, "%d\n", contexts->nr);
88962306a36Sopenharmony_ci}
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_cistatic ssize_t nr_contexts_store(struct kobject *kobj,
89262306a36Sopenharmony_ci		struct kobj_attribute *attr, const char *buf, size_t count)
89362306a36Sopenharmony_ci{
89462306a36Sopenharmony_ci	struct damon_sysfs_contexts *contexts;
89562306a36Sopenharmony_ci	int nr, err;
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	err = kstrtoint(buf, 0, &nr);
89862306a36Sopenharmony_ci	if (err)
89962306a36Sopenharmony_ci		return err;
90062306a36Sopenharmony_ci	/* TODO: support multiple contexts per kdamond */
90162306a36Sopenharmony_ci	if (nr < 0 || 1 < nr)
90262306a36Sopenharmony_ci		return -EINVAL;
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	contexts = container_of(kobj, struct damon_sysfs_contexts, kobj);
90562306a36Sopenharmony_ci	if (!mutex_trylock(&damon_sysfs_lock))
90662306a36Sopenharmony_ci		return -EBUSY;
90762306a36Sopenharmony_ci	err = damon_sysfs_contexts_add_dirs(contexts, nr);
90862306a36Sopenharmony_ci	mutex_unlock(&damon_sysfs_lock);
90962306a36Sopenharmony_ci	if (err)
91062306a36Sopenharmony_ci		return err;
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci	return count;
91362306a36Sopenharmony_ci}
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_cistatic void damon_sysfs_contexts_release(struct kobject *kobj)
91662306a36Sopenharmony_ci{
91762306a36Sopenharmony_ci	kfree(container_of(kobj, struct damon_sysfs_contexts, kobj));
91862306a36Sopenharmony_ci}
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_cistatic struct kobj_attribute damon_sysfs_contexts_nr_attr
92162306a36Sopenharmony_ci		= __ATTR_RW_MODE(nr_contexts, 0600);
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_cistatic struct attribute *damon_sysfs_contexts_attrs[] = {
92462306a36Sopenharmony_ci	&damon_sysfs_contexts_nr_attr.attr,
92562306a36Sopenharmony_ci	NULL,
92662306a36Sopenharmony_ci};
92762306a36Sopenharmony_ciATTRIBUTE_GROUPS(damon_sysfs_contexts);
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_cistatic const struct kobj_type damon_sysfs_contexts_ktype = {
93062306a36Sopenharmony_ci	.release = damon_sysfs_contexts_release,
93162306a36Sopenharmony_ci	.sysfs_ops = &kobj_sysfs_ops,
93262306a36Sopenharmony_ci	.default_groups = damon_sysfs_contexts_groups,
93362306a36Sopenharmony_ci};
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci/*
93662306a36Sopenharmony_ci * kdamond directory
93762306a36Sopenharmony_ci */
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_cistruct damon_sysfs_kdamond {
94062306a36Sopenharmony_ci	struct kobject kobj;
94162306a36Sopenharmony_ci	struct damon_sysfs_contexts *contexts;
94262306a36Sopenharmony_ci	struct damon_ctx *damon_ctx;
94362306a36Sopenharmony_ci};
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_cistatic struct damon_sysfs_kdamond *damon_sysfs_kdamond_alloc(void)
94662306a36Sopenharmony_ci{
94762306a36Sopenharmony_ci	return kzalloc(sizeof(struct damon_sysfs_kdamond), GFP_KERNEL);
94862306a36Sopenharmony_ci}
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_cistatic int damon_sysfs_kdamond_add_dirs(struct damon_sysfs_kdamond *kdamond)
95162306a36Sopenharmony_ci{
95262306a36Sopenharmony_ci	struct damon_sysfs_contexts *contexts;
95362306a36Sopenharmony_ci	int err;
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	contexts = damon_sysfs_contexts_alloc();
95662306a36Sopenharmony_ci	if (!contexts)
95762306a36Sopenharmony_ci		return -ENOMEM;
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	err = kobject_init_and_add(&contexts->kobj,
96062306a36Sopenharmony_ci			&damon_sysfs_contexts_ktype, &kdamond->kobj,
96162306a36Sopenharmony_ci			"contexts");
96262306a36Sopenharmony_ci	if (err) {
96362306a36Sopenharmony_ci		kobject_put(&contexts->kobj);
96462306a36Sopenharmony_ci		return err;
96562306a36Sopenharmony_ci	}
96662306a36Sopenharmony_ci	kdamond->contexts = contexts;
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	return err;
96962306a36Sopenharmony_ci}
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_cistatic void damon_sysfs_kdamond_rm_dirs(struct damon_sysfs_kdamond *kdamond)
97262306a36Sopenharmony_ci{
97362306a36Sopenharmony_ci	damon_sysfs_contexts_rm_dirs(kdamond->contexts);
97462306a36Sopenharmony_ci	kobject_put(&kdamond->contexts->kobj);
97562306a36Sopenharmony_ci}
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_cistatic bool damon_sysfs_ctx_running(struct damon_ctx *ctx)
97862306a36Sopenharmony_ci{
97962306a36Sopenharmony_ci	bool running;
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci	mutex_lock(&ctx->kdamond_lock);
98262306a36Sopenharmony_ci	running = ctx->kdamond != NULL;
98362306a36Sopenharmony_ci	mutex_unlock(&ctx->kdamond_lock);
98462306a36Sopenharmony_ci	return running;
98562306a36Sopenharmony_ci}
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci/*
98862306a36Sopenharmony_ci * enum damon_sysfs_cmd - Commands for a specific kdamond.
98962306a36Sopenharmony_ci */
99062306a36Sopenharmony_cienum damon_sysfs_cmd {
99162306a36Sopenharmony_ci	/* @DAMON_SYSFS_CMD_ON: Turn the kdamond on. */
99262306a36Sopenharmony_ci	DAMON_SYSFS_CMD_ON,
99362306a36Sopenharmony_ci	/* @DAMON_SYSFS_CMD_OFF: Turn the kdamond off. */
99462306a36Sopenharmony_ci	DAMON_SYSFS_CMD_OFF,
99562306a36Sopenharmony_ci	/* @DAMON_SYSFS_CMD_COMMIT: Update kdamond inputs. */
99662306a36Sopenharmony_ci	DAMON_SYSFS_CMD_COMMIT,
99762306a36Sopenharmony_ci	/*
99862306a36Sopenharmony_ci	 * @DAMON_SYSFS_CMD_UPDATE_SCHEMES_STATS: Update scheme stats sysfs
99962306a36Sopenharmony_ci	 * files.
100062306a36Sopenharmony_ci	 */
100162306a36Sopenharmony_ci	DAMON_SYSFS_CMD_UPDATE_SCHEMES_STATS,
100262306a36Sopenharmony_ci	/*
100362306a36Sopenharmony_ci	 * @DAMON_SYSFS_CMD_UPDATE_SCHEMES_TRIED_BYTES: Update
100462306a36Sopenharmony_ci	 * tried_regions/total_bytes sysfs files for each scheme.
100562306a36Sopenharmony_ci	 */
100662306a36Sopenharmony_ci	DAMON_SYSFS_CMD_UPDATE_SCHEMES_TRIED_BYTES,
100762306a36Sopenharmony_ci	/*
100862306a36Sopenharmony_ci	 * @DAMON_SYSFS_CMD_UPDATE_SCHEMES_TRIED_REGIONS: Update schemes tried
100962306a36Sopenharmony_ci	 * regions
101062306a36Sopenharmony_ci	 */
101162306a36Sopenharmony_ci	DAMON_SYSFS_CMD_UPDATE_SCHEMES_TRIED_REGIONS,
101262306a36Sopenharmony_ci	/*
101362306a36Sopenharmony_ci	 * @DAMON_SYSFS_CMD_CLEAR_SCHEMES_TRIED_REGIONS: Clear schemes tried
101462306a36Sopenharmony_ci	 * regions
101562306a36Sopenharmony_ci	 */
101662306a36Sopenharmony_ci	DAMON_SYSFS_CMD_CLEAR_SCHEMES_TRIED_REGIONS,
101762306a36Sopenharmony_ci	/*
101862306a36Sopenharmony_ci	 * @NR_DAMON_SYSFS_CMDS: Total number of DAMON sysfs commands.
101962306a36Sopenharmony_ci	 */
102062306a36Sopenharmony_ci	NR_DAMON_SYSFS_CMDS,
102162306a36Sopenharmony_ci};
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci/* Should match with enum damon_sysfs_cmd */
102462306a36Sopenharmony_cistatic const char * const damon_sysfs_cmd_strs[] = {
102562306a36Sopenharmony_ci	"on",
102662306a36Sopenharmony_ci	"off",
102762306a36Sopenharmony_ci	"commit",
102862306a36Sopenharmony_ci	"update_schemes_stats",
102962306a36Sopenharmony_ci	"update_schemes_tried_bytes",
103062306a36Sopenharmony_ci	"update_schemes_tried_regions",
103162306a36Sopenharmony_ci	"clear_schemes_tried_regions",
103262306a36Sopenharmony_ci};
103362306a36Sopenharmony_ci
103462306a36Sopenharmony_ci/*
103562306a36Sopenharmony_ci * struct damon_sysfs_cmd_request - A request to the DAMON callback.
103662306a36Sopenharmony_ci * @cmd:	The command that needs to be handled by the callback.
103762306a36Sopenharmony_ci * @kdamond:	The kobject wrapper that associated to the kdamond thread.
103862306a36Sopenharmony_ci *
103962306a36Sopenharmony_ci * This structure represents a sysfs command request that need to access some
104062306a36Sopenharmony_ci * DAMON context-internal data.  Because DAMON context-internal data can be
104162306a36Sopenharmony_ci * safely accessed from DAMON callbacks without additional synchronization, the
104262306a36Sopenharmony_ci * request will be handled by the DAMON callback.  None-``NULL`` @kdamond means
104362306a36Sopenharmony_ci * the request is valid.
104462306a36Sopenharmony_ci */
104562306a36Sopenharmony_cistruct damon_sysfs_cmd_request {
104662306a36Sopenharmony_ci	enum damon_sysfs_cmd cmd;
104762306a36Sopenharmony_ci	struct damon_sysfs_kdamond *kdamond;
104862306a36Sopenharmony_ci};
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci/* Current DAMON callback request.  Protected by damon_sysfs_lock. */
105162306a36Sopenharmony_cistatic struct damon_sysfs_cmd_request damon_sysfs_cmd_request;
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_cistatic ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr,
105462306a36Sopenharmony_ci		char *buf)
105562306a36Sopenharmony_ci{
105662306a36Sopenharmony_ci	struct damon_sysfs_kdamond *kdamond = container_of(kobj,
105762306a36Sopenharmony_ci			struct damon_sysfs_kdamond, kobj);
105862306a36Sopenharmony_ci	struct damon_ctx *ctx = kdamond->damon_ctx;
105962306a36Sopenharmony_ci	bool running;
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ci	if (!ctx)
106262306a36Sopenharmony_ci		running = false;
106362306a36Sopenharmony_ci	else
106462306a36Sopenharmony_ci		running = damon_sysfs_ctx_running(ctx);
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	return sysfs_emit(buf, "%s\n", running ?
106762306a36Sopenharmony_ci			damon_sysfs_cmd_strs[DAMON_SYSFS_CMD_ON] :
106862306a36Sopenharmony_ci			damon_sysfs_cmd_strs[DAMON_SYSFS_CMD_OFF]);
106962306a36Sopenharmony_ci}
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_cistatic int damon_sysfs_set_attrs(struct damon_ctx *ctx,
107262306a36Sopenharmony_ci		struct damon_sysfs_attrs *sys_attrs)
107362306a36Sopenharmony_ci{
107462306a36Sopenharmony_ci	struct damon_sysfs_intervals *sys_intervals = sys_attrs->intervals;
107562306a36Sopenharmony_ci	struct damon_sysfs_ul_range *sys_nr_regions =
107662306a36Sopenharmony_ci		sys_attrs->nr_regions_range;
107762306a36Sopenharmony_ci	struct damon_attrs attrs = {
107862306a36Sopenharmony_ci		.sample_interval = sys_intervals->sample_us,
107962306a36Sopenharmony_ci		.aggr_interval = sys_intervals->aggr_us,
108062306a36Sopenharmony_ci		.ops_update_interval = sys_intervals->update_us,
108162306a36Sopenharmony_ci		.min_nr_regions = sys_nr_regions->min,
108262306a36Sopenharmony_ci		.max_nr_regions = sys_nr_regions->max,
108362306a36Sopenharmony_ci	};
108462306a36Sopenharmony_ci	return damon_set_attrs(ctx, &attrs);
108562306a36Sopenharmony_ci}
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_cistatic void damon_sysfs_destroy_targets(struct damon_ctx *ctx)
108862306a36Sopenharmony_ci{
108962306a36Sopenharmony_ci	struct damon_target *t, *next;
109062306a36Sopenharmony_ci	bool has_pid = damon_target_has_pid(ctx);
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	damon_for_each_target_safe(t, next, ctx) {
109362306a36Sopenharmony_ci		if (has_pid)
109462306a36Sopenharmony_ci			put_pid(t->pid);
109562306a36Sopenharmony_ci		damon_destroy_target(t);
109662306a36Sopenharmony_ci	}
109762306a36Sopenharmony_ci}
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_cistatic int damon_sysfs_set_regions(struct damon_target *t,
110062306a36Sopenharmony_ci		struct damon_sysfs_regions *sysfs_regions)
110162306a36Sopenharmony_ci{
110262306a36Sopenharmony_ci	struct damon_addr_range *ranges = kmalloc_array(sysfs_regions->nr,
110362306a36Sopenharmony_ci			sizeof(*ranges), GFP_KERNEL | __GFP_NOWARN);
110462306a36Sopenharmony_ci	int i, err = -EINVAL;
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci	if (!ranges)
110762306a36Sopenharmony_ci		return -ENOMEM;
110862306a36Sopenharmony_ci	for (i = 0; i < sysfs_regions->nr; i++) {
110962306a36Sopenharmony_ci		struct damon_sysfs_region *sys_region =
111062306a36Sopenharmony_ci			sysfs_regions->regions_arr[i];
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ci		if (sys_region->ar.start > sys_region->ar.end)
111362306a36Sopenharmony_ci			goto out;
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_ci		ranges[i].start = sys_region->ar.start;
111662306a36Sopenharmony_ci		ranges[i].end = sys_region->ar.end;
111762306a36Sopenharmony_ci		if (i == 0)
111862306a36Sopenharmony_ci			continue;
111962306a36Sopenharmony_ci		if (ranges[i - 1].end > ranges[i].start)
112062306a36Sopenharmony_ci			goto out;
112162306a36Sopenharmony_ci	}
112262306a36Sopenharmony_ci	err = damon_set_regions(t, ranges, sysfs_regions->nr);
112362306a36Sopenharmony_ciout:
112462306a36Sopenharmony_ci	kfree(ranges);
112562306a36Sopenharmony_ci	return err;
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci}
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_cistatic int damon_sysfs_add_target(struct damon_sysfs_target *sys_target,
113062306a36Sopenharmony_ci		struct damon_ctx *ctx)
113162306a36Sopenharmony_ci{
113262306a36Sopenharmony_ci	struct damon_target *t = damon_new_target();
113362306a36Sopenharmony_ci	int err = -EINVAL;
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci	if (!t)
113662306a36Sopenharmony_ci		return -ENOMEM;
113762306a36Sopenharmony_ci	damon_add_target(ctx, t);
113862306a36Sopenharmony_ci	if (damon_target_has_pid(ctx)) {
113962306a36Sopenharmony_ci		t->pid = find_get_pid(sys_target->pid);
114062306a36Sopenharmony_ci		if (!t->pid)
114162306a36Sopenharmony_ci			goto destroy_targets_out;
114262306a36Sopenharmony_ci	}
114362306a36Sopenharmony_ci	err = damon_sysfs_set_regions(t, sys_target->regions);
114462306a36Sopenharmony_ci	if (err)
114562306a36Sopenharmony_ci		goto destroy_targets_out;
114662306a36Sopenharmony_ci	return 0;
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_cidestroy_targets_out:
114962306a36Sopenharmony_ci	damon_sysfs_destroy_targets(ctx);
115062306a36Sopenharmony_ci	return err;
115162306a36Sopenharmony_ci}
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_cistatic int damon_sysfs_update_target_pid(struct damon_target *target, int pid)
115462306a36Sopenharmony_ci{
115562306a36Sopenharmony_ci	struct pid *pid_new;
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci	pid_new = find_get_pid(pid);
115862306a36Sopenharmony_ci	if (!pid_new)
115962306a36Sopenharmony_ci		return -EINVAL;
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_ci	if (pid_new == target->pid) {
116262306a36Sopenharmony_ci		put_pid(pid_new);
116362306a36Sopenharmony_ci		return 0;
116462306a36Sopenharmony_ci	}
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci	put_pid(target->pid);
116762306a36Sopenharmony_ci	target->pid = pid_new;
116862306a36Sopenharmony_ci	return 0;
116962306a36Sopenharmony_ci}
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_cistatic int damon_sysfs_update_target(struct damon_target *target,
117262306a36Sopenharmony_ci		struct damon_ctx *ctx,
117362306a36Sopenharmony_ci		struct damon_sysfs_target *sys_target)
117462306a36Sopenharmony_ci{
117562306a36Sopenharmony_ci	int err = 0;
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci	if (damon_target_has_pid(ctx)) {
117862306a36Sopenharmony_ci		err = damon_sysfs_update_target_pid(target, sys_target->pid);
117962306a36Sopenharmony_ci		if (err)
118062306a36Sopenharmony_ci			return err;
118162306a36Sopenharmony_ci	}
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci	/*
118462306a36Sopenharmony_ci	 * Do monitoring target region boundary update only if one or more
118562306a36Sopenharmony_ci	 * regions are set by the user.  This is for keeping current monitoring
118662306a36Sopenharmony_ci	 * target results and range easier, especially for dynamic monitoring
118762306a36Sopenharmony_ci	 * target regions update ops like 'vaddr'.
118862306a36Sopenharmony_ci	 */
118962306a36Sopenharmony_ci	if (sys_target->regions->nr)
119062306a36Sopenharmony_ci		err = damon_sysfs_set_regions(target, sys_target->regions);
119162306a36Sopenharmony_ci	return err;
119262306a36Sopenharmony_ci}
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_cistatic int damon_sysfs_set_targets(struct damon_ctx *ctx,
119562306a36Sopenharmony_ci		struct damon_sysfs_targets *sysfs_targets)
119662306a36Sopenharmony_ci{
119762306a36Sopenharmony_ci	struct damon_target *t, *next;
119862306a36Sopenharmony_ci	int i = 0, err;
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_ci	/* Multiple physical address space monitoring targets makes no sense */
120162306a36Sopenharmony_ci	if (ctx->ops.id == DAMON_OPS_PADDR && sysfs_targets->nr > 1)
120262306a36Sopenharmony_ci		return -EINVAL;
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ci	damon_for_each_target_safe(t, next, ctx) {
120562306a36Sopenharmony_ci		if (i < sysfs_targets->nr) {
120662306a36Sopenharmony_ci			err = damon_sysfs_update_target(t, ctx,
120762306a36Sopenharmony_ci					sysfs_targets->targets_arr[i]);
120862306a36Sopenharmony_ci			if (err)
120962306a36Sopenharmony_ci				return err;
121062306a36Sopenharmony_ci		} else {
121162306a36Sopenharmony_ci			if (damon_target_has_pid(ctx))
121262306a36Sopenharmony_ci				put_pid(t->pid);
121362306a36Sopenharmony_ci			damon_destroy_target(t);
121462306a36Sopenharmony_ci		}
121562306a36Sopenharmony_ci		i++;
121662306a36Sopenharmony_ci	}
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_ci	for (; i < sysfs_targets->nr; i++) {
121962306a36Sopenharmony_ci		struct damon_sysfs_target *st = sysfs_targets->targets_arr[i];
122062306a36Sopenharmony_ci
122162306a36Sopenharmony_ci		err = damon_sysfs_add_target(st, ctx);
122262306a36Sopenharmony_ci		if (err)
122362306a36Sopenharmony_ci			return err;
122462306a36Sopenharmony_ci	}
122562306a36Sopenharmony_ci	return 0;
122662306a36Sopenharmony_ci}
122762306a36Sopenharmony_ci
122862306a36Sopenharmony_cistatic bool damon_sysfs_schemes_regions_updating;
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_cistatic void damon_sysfs_before_terminate(struct damon_ctx *ctx)
123162306a36Sopenharmony_ci{
123262306a36Sopenharmony_ci	struct damon_target *t, *next;
123362306a36Sopenharmony_ci	struct damon_sysfs_kdamond *kdamond;
123462306a36Sopenharmony_ci	enum damon_sysfs_cmd cmd;
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ci	/* damon_sysfs_schemes_update_regions_stop() might not yet called */
123762306a36Sopenharmony_ci	kdamond = damon_sysfs_cmd_request.kdamond;
123862306a36Sopenharmony_ci	cmd = damon_sysfs_cmd_request.cmd;
123962306a36Sopenharmony_ci	if (kdamond && ctx == kdamond->damon_ctx &&
124062306a36Sopenharmony_ci			(cmd == DAMON_SYSFS_CMD_UPDATE_SCHEMES_TRIED_REGIONS ||
124162306a36Sopenharmony_ci			 cmd == DAMON_SYSFS_CMD_UPDATE_SCHEMES_TRIED_BYTES) &&
124262306a36Sopenharmony_ci			damon_sysfs_schemes_regions_updating) {
124362306a36Sopenharmony_ci		damon_sysfs_schemes_update_regions_stop(ctx);
124462306a36Sopenharmony_ci		damon_sysfs_schemes_regions_updating = false;
124562306a36Sopenharmony_ci		mutex_unlock(&damon_sysfs_lock);
124662306a36Sopenharmony_ci	}
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_ci	if (!damon_target_has_pid(ctx))
124962306a36Sopenharmony_ci		return;
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_ci	mutex_lock(&ctx->kdamond_lock);
125262306a36Sopenharmony_ci	damon_for_each_target_safe(t, next, ctx) {
125362306a36Sopenharmony_ci		put_pid(t->pid);
125462306a36Sopenharmony_ci		damon_destroy_target(t);
125562306a36Sopenharmony_ci	}
125662306a36Sopenharmony_ci	mutex_unlock(&ctx->kdamond_lock);
125762306a36Sopenharmony_ci}
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci/*
126062306a36Sopenharmony_ci * damon_sysfs_upd_schemes_stats() - Update schemes stats sysfs files.
126162306a36Sopenharmony_ci * @kdamond:	The kobject wrapper that associated to the kdamond thread.
126262306a36Sopenharmony_ci *
126362306a36Sopenharmony_ci * This function reads the schemes stats of specific kdamond and update the
126462306a36Sopenharmony_ci * related values for sysfs files.  This function should be called from DAMON
126562306a36Sopenharmony_ci * callbacks while holding ``damon_syfs_lock``, to safely access the DAMON
126662306a36Sopenharmony_ci * contexts-internal data and DAMON sysfs variables.
126762306a36Sopenharmony_ci */
126862306a36Sopenharmony_cistatic int damon_sysfs_upd_schemes_stats(struct damon_sysfs_kdamond *kdamond)
126962306a36Sopenharmony_ci{
127062306a36Sopenharmony_ci	struct damon_ctx *ctx = kdamond->damon_ctx;
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ci	if (!ctx)
127362306a36Sopenharmony_ci		return -EINVAL;
127462306a36Sopenharmony_ci	damon_sysfs_schemes_update_stats(
127562306a36Sopenharmony_ci			kdamond->contexts->contexts_arr[0]->schemes, ctx);
127662306a36Sopenharmony_ci	return 0;
127762306a36Sopenharmony_ci}
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_cistatic int damon_sysfs_upd_schemes_regions_start(
128062306a36Sopenharmony_ci		struct damon_sysfs_kdamond *kdamond, bool total_bytes_only)
128162306a36Sopenharmony_ci{
128262306a36Sopenharmony_ci	struct damon_ctx *ctx = kdamond->damon_ctx;
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci	if (!ctx)
128562306a36Sopenharmony_ci		return -EINVAL;
128662306a36Sopenharmony_ci	return damon_sysfs_schemes_update_regions_start(
128762306a36Sopenharmony_ci			kdamond->contexts->contexts_arr[0]->schemes, ctx,
128862306a36Sopenharmony_ci			total_bytes_only);
128962306a36Sopenharmony_ci}
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_cistatic int damon_sysfs_upd_schemes_regions_stop(
129262306a36Sopenharmony_ci		struct damon_sysfs_kdamond *kdamond)
129362306a36Sopenharmony_ci{
129462306a36Sopenharmony_ci	struct damon_ctx *ctx = kdamond->damon_ctx;
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci	if (!ctx)
129762306a36Sopenharmony_ci		return -EINVAL;
129862306a36Sopenharmony_ci	return damon_sysfs_schemes_update_regions_stop(ctx);
129962306a36Sopenharmony_ci}
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_cistatic int damon_sysfs_clear_schemes_regions(
130262306a36Sopenharmony_ci		struct damon_sysfs_kdamond *kdamond)
130362306a36Sopenharmony_ci{
130462306a36Sopenharmony_ci	struct damon_ctx *ctx = kdamond->damon_ctx;
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_ci	if (!ctx)
130762306a36Sopenharmony_ci		return -EINVAL;
130862306a36Sopenharmony_ci	return damon_sysfs_schemes_clear_regions(
130962306a36Sopenharmony_ci			kdamond->contexts->contexts_arr[0]->schemes, ctx);
131062306a36Sopenharmony_ci}
131162306a36Sopenharmony_ci
131262306a36Sopenharmony_cistatic inline bool damon_sysfs_kdamond_running(
131362306a36Sopenharmony_ci		struct damon_sysfs_kdamond *kdamond)
131462306a36Sopenharmony_ci{
131562306a36Sopenharmony_ci	return kdamond->damon_ctx &&
131662306a36Sopenharmony_ci		damon_sysfs_ctx_running(kdamond->damon_ctx);
131762306a36Sopenharmony_ci}
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_cistatic int damon_sysfs_apply_inputs(struct damon_ctx *ctx,
132062306a36Sopenharmony_ci		struct damon_sysfs_context *sys_ctx)
132162306a36Sopenharmony_ci{
132262306a36Sopenharmony_ci	int err;
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ci	err = damon_select_ops(ctx, sys_ctx->ops_id);
132562306a36Sopenharmony_ci	if (err)
132662306a36Sopenharmony_ci		return err;
132762306a36Sopenharmony_ci	err = damon_sysfs_set_attrs(ctx, sys_ctx->attrs);
132862306a36Sopenharmony_ci	if (err)
132962306a36Sopenharmony_ci		return err;
133062306a36Sopenharmony_ci	err = damon_sysfs_set_targets(ctx, sys_ctx->targets);
133162306a36Sopenharmony_ci	if (err)
133262306a36Sopenharmony_ci		return err;
133362306a36Sopenharmony_ci	return damon_sysfs_set_schemes(ctx, sys_ctx->schemes);
133462306a36Sopenharmony_ci}
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci/*
133762306a36Sopenharmony_ci * damon_sysfs_commit_input() - Commit user inputs to a running kdamond.
133862306a36Sopenharmony_ci * @kdamond:	The kobject wrapper for the associated kdamond.
133962306a36Sopenharmony_ci *
134062306a36Sopenharmony_ci * If the sysfs input is wrong, the kdamond will be terminated.
134162306a36Sopenharmony_ci */
134262306a36Sopenharmony_cistatic int damon_sysfs_commit_input(struct damon_sysfs_kdamond *kdamond)
134362306a36Sopenharmony_ci{
134462306a36Sopenharmony_ci	if (!damon_sysfs_kdamond_running(kdamond))
134562306a36Sopenharmony_ci		return -EINVAL;
134662306a36Sopenharmony_ci	/* TODO: Support multiple contexts per kdamond */
134762306a36Sopenharmony_ci	if (kdamond->contexts->nr != 1)
134862306a36Sopenharmony_ci		return -EINVAL;
134962306a36Sopenharmony_ci
135062306a36Sopenharmony_ci	return damon_sysfs_apply_inputs(kdamond->damon_ctx,
135162306a36Sopenharmony_ci			kdamond->contexts->contexts_arr[0]);
135262306a36Sopenharmony_ci}
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_ci/*
135562306a36Sopenharmony_ci * damon_sysfs_cmd_request_callback() - DAMON callback for handling requests.
135662306a36Sopenharmony_ci * @c:	The DAMON context of the callback.
135762306a36Sopenharmony_ci *
135862306a36Sopenharmony_ci * This function is periodically called back from the kdamond thread for @c.
135962306a36Sopenharmony_ci * Then, it checks if there is a waiting DAMON sysfs request and handles it.
136062306a36Sopenharmony_ci */
136162306a36Sopenharmony_cistatic int damon_sysfs_cmd_request_callback(struct damon_ctx *c)
136262306a36Sopenharmony_ci{
136362306a36Sopenharmony_ci	struct damon_sysfs_kdamond *kdamond;
136462306a36Sopenharmony_ci	bool total_bytes_only = false;
136562306a36Sopenharmony_ci	int err = 0;
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_ci	/* avoid deadlock due to concurrent state_store('off') */
136862306a36Sopenharmony_ci	if (!damon_sysfs_schemes_regions_updating &&
136962306a36Sopenharmony_ci			!mutex_trylock(&damon_sysfs_lock))
137062306a36Sopenharmony_ci		return 0;
137162306a36Sopenharmony_ci	kdamond = damon_sysfs_cmd_request.kdamond;
137262306a36Sopenharmony_ci	if (!kdamond || kdamond->damon_ctx != c)
137362306a36Sopenharmony_ci		goto out;
137462306a36Sopenharmony_ci	switch (damon_sysfs_cmd_request.cmd) {
137562306a36Sopenharmony_ci	case DAMON_SYSFS_CMD_UPDATE_SCHEMES_STATS:
137662306a36Sopenharmony_ci		err = damon_sysfs_upd_schemes_stats(kdamond);
137762306a36Sopenharmony_ci		break;
137862306a36Sopenharmony_ci	case DAMON_SYSFS_CMD_COMMIT:
137962306a36Sopenharmony_ci		err = damon_sysfs_commit_input(kdamond);
138062306a36Sopenharmony_ci		break;
138162306a36Sopenharmony_ci	case DAMON_SYSFS_CMD_UPDATE_SCHEMES_TRIED_BYTES:
138262306a36Sopenharmony_ci		total_bytes_only = true;
138362306a36Sopenharmony_ci		fallthrough;
138462306a36Sopenharmony_ci	case DAMON_SYSFS_CMD_UPDATE_SCHEMES_TRIED_REGIONS:
138562306a36Sopenharmony_ci		if (!damon_sysfs_schemes_regions_updating) {
138662306a36Sopenharmony_ci			err = damon_sysfs_upd_schemes_regions_start(kdamond,
138762306a36Sopenharmony_ci					total_bytes_only);
138862306a36Sopenharmony_ci			if (!err) {
138962306a36Sopenharmony_ci				damon_sysfs_schemes_regions_updating = true;
139062306a36Sopenharmony_ci				goto keep_lock_out;
139162306a36Sopenharmony_ci			}
139262306a36Sopenharmony_ci		} else {
139362306a36Sopenharmony_ci			err = damon_sysfs_upd_schemes_regions_stop(kdamond);
139462306a36Sopenharmony_ci			damon_sysfs_schemes_regions_updating = false;
139562306a36Sopenharmony_ci		}
139662306a36Sopenharmony_ci		break;
139762306a36Sopenharmony_ci	case DAMON_SYSFS_CMD_CLEAR_SCHEMES_TRIED_REGIONS:
139862306a36Sopenharmony_ci		err = damon_sysfs_clear_schemes_regions(kdamond);
139962306a36Sopenharmony_ci		break;
140062306a36Sopenharmony_ci	default:
140162306a36Sopenharmony_ci		break;
140262306a36Sopenharmony_ci	}
140362306a36Sopenharmony_ci	/* Mark the request as invalid now. */
140462306a36Sopenharmony_ci	damon_sysfs_cmd_request.kdamond = NULL;
140562306a36Sopenharmony_ciout:
140662306a36Sopenharmony_ci	if (!damon_sysfs_schemes_regions_updating)
140762306a36Sopenharmony_ci		mutex_unlock(&damon_sysfs_lock);
140862306a36Sopenharmony_cikeep_lock_out:
140962306a36Sopenharmony_ci	return err;
141062306a36Sopenharmony_ci}
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_cistatic struct damon_ctx *damon_sysfs_build_ctx(
141362306a36Sopenharmony_ci		struct damon_sysfs_context *sys_ctx)
141462306a36Sopenharmony_ci{
141562306a36Sopenharmony_ci	struct damon_ctx *ctx = damon_new_ctx();
141662306a36Sopenharmony_ci	int err;
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_ci	if (!ctx)
141962306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
142062306a36Sopenharmony_ci
142162306a36Sopenharmony_ci	err = damon_sysfs_apply_inputs(ctx, sys_ctx);
142262306a36Sopenharmony_ci	if (err) {
142362306a36Sopenharmony_ci		damon_destroy_ctx(ctx);
142462306a36Sopenharmony_ci		return ERR_PTR(err);
142562306a36Sopenharmony_ci	}
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_ci	ctx->callback.after_wmarks_check = damon_sysfs_cmd_request_callback;
142862306a36Sopenharmony_ci	ctx->callback.after_aggregation = damon_sysfs_cmd_request_callback;
142962306a36Sopenharmony_ci	ctx->callback.before_terminate = damon_sysfs_before_terminate;
143062306a36Sopenharmony_ci	return ctx;
143162306a36Sopenharmony_ci}
143262306a36Sopenharmony_ci
143362306a36Sopenharmony_cistatic int damon_sysfs_turn_damon_on(struct damon_sysfs_kdamond *kdamond)
143462306a36Sopenharmony_ci{
143562306a36Sopenharmony_ci	struct damon_ctx *ctx;
143662306a36Sopenharmony_ci	int err;
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci	if (damon_sysfs_kdamond_running(kdamond))
143962306a36Sopenharmony_ci		return -EBUSY;
144062306a36Sopenharmony_ci	if (damon_sysfs_cmd_request.kdamond == kdamond)
144162306a36Sopenharmony_ci		return -EBUSY;
144262306a36Sopenharmony_ci	/* TODO: support multiple contexts per kdamond */
144362306a36Sopenharmony_ci	if (kdamond->contexts->nr != 1)
144462306a36Sopenharmony_ci		return -EINVAL;
144562306a36Sopenharmony_ci
144662306a36Sopenharmony_ci	if (kdamond->damon_ctx)
144762306a36Sopenharmony_ci		damon_destroy_ctx(kdamond->damon_ctx);
144862306a36Sopenharmony_ci	kdamond->damon_ctx = NULL;
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_ci	ctx = damon_sysfs_build_ctx(kdamond->contexts->contexts_arr[0]);
145162306a36Sopenharmony_ci	if (IS_ERR(ctx))
145262306a36Sopenharmony_ci		return PTR_ERR(ctx);
145362306a36Sopenharmony_ci	err = damon_start(&ctx, 1, false);
145462306a36Sopenharmony_ci	if (err) {
145562306a36Sopenharmony_ci		damon_destroy_ctx(ctx);
145662306a36Sopenharmony_ci		return err;
145762306a36Sopenharmony_ci	}
145862306a36Sopenharmony_ci	kdamond->damon_ctx = ctx;
145962306a36Sopenharmony_ci	return err;
146062306a36Sopenharmony_ci}
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_cistatic int damon_sysfs_turn_damon_off(struct damon_sysfs_kdamond *kdamond)
146362306a36Sopenharmony_ci{
146462306a36Sopenharmony_ci	if (!kdamond->damon_ctx)
146562306a36Sopenharmony_ci		return -EINVAL;
146662306a36Sopenharmony_ci	return damon_stop(&kdamond->damon_ctx, 1);
146762306a36Sopenharmony_ci	/*
146862306a36Sopenharmony_ci	 * To allow users show final monitoring results of already turned-off
146962306a36Sopenharmony_ci	 * DAMON, we free kdamond->damon_ctx in next
147062306a36Sopenharmony_ci	 * damon_sysfs_turn_damon_on(), or kdamonds_nr_store()
147162306a36Sopenharmony_ci	 */
147262306a36Sopenharmony_ci}
147362306a36Sopenharmony_ci
147462306a36Sopenharmony_ci/*
147562306a36Sopenharmony_ci * damon_sysfs_handle_cmd() - Handle a command for a specific kdamond.
147662306a36Sopenharmony_ci * @cmd:	The command to handle.
147762306a36Sopenharmony_ci * @kdamond:	The kobject wrapper for the associated kdamond.
147862306a36Sopenharmony_ci *
147962306a36Sopenharmony_ci * This function handles a DAMON sysfs command for a kdamond.  For commands
148062306a36Sopenharmony_ci * that need to access running DAMON context-internal data, it requests
148162306a36Sopenharmony_ci * handling of the command to the DAMON callback
148262306a36Sopenharmony_ci * (@damon_sysfs_cmd_request_callback()) and wait until it is properly handled,
148362306a36Sopenharmony_ci * or the context is completed.
148462306a36Sopenharmony_ci *
148562306a36Sopenharmony_ci * Return: 0 on success, negative error code otherwise.
148662306a36Sopenharmony_ci */
148762306a36Sopenharmony_cistatic int damon_sysfs_handle_cmd(enum damon_sysfs_cmd cmd,
148862306a36Sopenharmony_ci		struct damon_sysfs_kdamond *kdamond)
148962306a36Sopenharmony_ci{
149062306a36Sopenharmony_ci	bool need_wait = true;
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_ci	/* Handle commands that doesn't access DAMON context-internal data */
149362306a36Sopenharmony_ci	switch (cmd) {
149462306a36Sopenharmony_ci	case DAMON_SYSFS_CMD_ON:
149562306a36Sopenharmony_ci		return damon_sysfs_turn_damon_on(kdamond);
149662306a36Sopenharmony_ci	case DAMON_SYSFS_CMD_OFF:
149762306a36Sopenharmony_ci		return damon_sysfs_turn_damon_off(kdamond);
149862306a36Sopenharmony_ci	default:
149962306a36Sopenharmony_ci		break;
150062306a36Sopenharmony_ci	}
150162306a36Sopenharmony_ci
150262306a36Sopenharmony_ci	/* Pass the command to DAMON callback for safe DAMON context access */
150362306a36Sopenharmony_ci	if (damon_sysfs_cmd_request.kdamond)
150462306a36Sopenharmony_ci		return -EBUSY;
150562306a36Sopenharmony_ci	if (!damon_sysfs_kdamond_running(kdamond))
150662306a36Sopenharmony_ci		return -EINVAL;
150762306a36Sopenharmony_ci	damon_sysfs_cmd_request.cmd = cmd;
150862306a36Sopenharmony_ci	damon_sysfs_cmd_request.kdamond = kdamond;
150962306a36Sopenharmony_ci
151062306a36Sopenharmony_ci	/*
151162306a36Sopenharmony_ci	 * wait until damon_sysfs_cmd_request_callback() handles the request
151262306a36Sopenharmony_ci	 * from kdamond context
151362306a36Sopenharmony_ci	 */
151462306a36Sopenharmony_ci	mutex_unlock(&damon_sysfs_lock);
151562306a36Sopenharmony_ci	while (need_wait) {
151662306a36Sopenharmony_ci		schedule_timeout_idle(msecs_to_jiffies(100));
151762306a36Sopenharmony_ci		if (!mutex_trylock(&damon_sysfs_lock))
151862306a36Sopenharmony_ci			continue;
151962306a36Sopenharmony_ci		if (!damon_sysfs_cmd_request.kdamond) {
152062306a36Sopenharmony_ci			/* damon_sysfs_cmd_request_callback() handled */
152162306a36Sopenharmony_ci			need_wait = false;
152262306a36Sopenharmony_ci		} else if (!damon_sysfs_kdamond_running(kdamond)) {
152362306a36Sopenharmony_ci			/* kdamond has already finished */
152462306a36Sopenharmony_ci			need_wait = false;
152562306a36Sopenharmony_ci			damon_sysfs_cmd_request.kdamond = NULL;
152662306a36Sopenharmony_ci		}
152762306a36Sopenharmony_ci		mutex_unlock(&damon_sysfs_lock);
152862306a36Sopenharmony_ci	}
152962306a36Sopenharmony_ci	mutex_lock(&damon_sysfs_lock);
153062306a36Sopenharmony_ci	return 0;
153162306a36Sopenharmony_ci}
153262306a36Sopenharmony_ci
153362306a36Sopenharmony_cistatic ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
153462306a36Sopenharmony_ci		const char *buf, size_t count)
153562306a36Sopenharmony_ci{
153662306a36Sopenharmony_ci	struct damon_sysfs_kdamond *kdamond = container_of(kobj,
153762306a36Sopenharmony_ci			struct damon_sysfs_kdamond, kobj);
153862306a36Sopenharmony_ci	enum damon_sysfs_cmd cmd;
153962306a36Sopenharmony_ci	ssize_t ret = -EINVAL;
154062306a36Sopenharmony_ci
154162306a36Sopenharmony_ci	if (!mutex_trylock(&damon_sysfs_lock))
154262306a36Sopenharmony_ci		return -EBUSY;
154362306a36Sopenharmony_ci	for (cmd = 0; cmd < NR_DAMON_SYSFS_CMDS; cmd++) {
154462306a36Sopenharmony_ci		if (sysfs_streq(buf, damon_sysfs_cmd_strs[cmd])) {
154562306a36Sopenharmony_ci			ret = damon_sysfs_handle_cmd(cmd, kdamond);
154662306a36Sopenharmony_ci			break;
154762306a36Sopenharmony_ci		}
154862306a36Sopenharmony_ci	}
154962306a36Sopenharmony_ci	mutex_unlock(&damon_sysfs_lock);
155062306a36Sopenharmony_ci	if (!ret)
155162306a36Sopenharmony_ci		ret = count;
155262306a36Sopenharmony_ci	return ret;
155362306a36Sopenharmony_ci}
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_cistatic ssize_t pid_show(struct kobject *kobj,
155662306a36Sopenharmony_ci		struct kobj_attribute *attr, char *buf)
155762306a36Sopenharmony_ci{
155862306a36Sopenharmony_ci	struct damon_sysfs_kdamond *kdamond = container_of(kobj,
155962306a36Sopenharmony_ci			struct damon_sysfs_kdamond, kobj);
156062306a36Sopenharmony_ci	struct damon_ctx *ctx;
156162306a36Sopenharmony_ci	int pid = -1;
156262306a36Sopenharmony_ci
156362306a36Sopenharmony_ci	if (!mutex_trylock(&damon_sysfs_lock))
156462306a36Sopenharmony_ci		return -EBUSY;
156562306a36Sopenharmony_ci	ctx = kdamond->damon_ctx;
156662306a36Sopenharmony_ci	if (!ctx)
156762306a36Sopenharmony_ci		goto out;
156862306a36Sopenharmony_ci
156962306a36Sopenharmony_ci	mutex_lock(&ctx->kdamond_lock);
157062306a36Sopenharmony_ci	if (ctx->kdamond)
157162306a36Sopenharmony_ci		pid = ctx->kdamond->pid;
157262306a36Sopenharmony_ci	mutex_unlock(&ctx->kdamond_lock);
157362306a36Sopenharmony_ciout:
157462306a36Sopenharmony_ci	mutex_unlock(&damon_sysfs_lock);
157562306a36Sopenharmony_ci	return sysfs_emit(buf, "%d\n", pid);
157662306a36Sopenharmony_ci}
157762306a36Sopenharmony_ci
157862306a36Sopenharmony_cistatic void damon_sysfs_kdamond_release(struct kobject *kobj)
157962306a36Sopenharmony_ci{
158062306a36Sopenharmony_ci	struct damon_sysfs_kdamond *kdamond = container_of(kobj,
158162306a36Sopenharmony_ci			struct damon_sysfs_kdamond, kobj);
158262306a36Sopenharmony_ci
158362306a36Sopenharmony_ci	if (kdamond->damon_ctx)
158462306a36Sopenharmony_ci		damon_destroy_ctx(kdamond->damon_ctx);
158562306a36Sopenharmony_ci	kfree(kdamond);
158662306a36Sopenharmony_ci}
158762306a36Sopenharmony_ci
158862306a36Sopenharmony_cistatic struct kobj_attribute damon_sysfs_kdamond_state_attr =
158962306a36Sopenharmony_ci		__ATTR_RW_MODE(state, 0600);
159062306a36Sopenharmony_ci
159162306a36Sopenharmony_cistatic struct kobj_attribute damon_sysfs_kdamond_pid_attr =
159262306a36Sopenharmony_ci		__ATTR_RO_MODE(pid, 0400);
159362306a36Sopenharmony_ci
159462306a36Sopenharmony_cistatic struct attribute *damon_sysfs_kdamond_attrs[] = {
159562306a36Sopenharmony_ci	&damon_sysfs_kdamond_state_attr.attr,
159662306a36Sopenharmony_ci	&damon_sysfs_kdamond_pid_attr.attr,
159762306a36Sopenharmony_ci	NULL,
159862306a36Sopenharmony_ci};
159962306a36Sopenharmony_ciATTRIBUTE_GROUPS(damon_sysfs_kdamond);
160062306a36Sopenharmony_ci
160162306a36Sopenharmony_cistatic const struct kobj_type damon_sysfs_kdamond_ktype = {
160262306a36Sopenharmony_ci	.release = damon_sysfs_kdamond_release,
160362306a36Sopenharmony_ci	.sysfs_ops = &kobj_sysfs_ops,
160462306a36Sopenharmony_ci	.default_groups = damon_sysfs_kdamond_groups,
160562306a36Sopenharmony_ci};
160662306a36Sopenharmony_ci
160762306a36Sopenharmony_ci/*
160862306a36Sopenharmony_ci * kdamonds directory
160962306a36Sopenharmony_ci */
161062306a36Sopenharmony_ci
161162306a36Sopenharmony_cistruct damon_sysfs_kdamonds {
161262306a36Sopenharmony_ci	struct kobject kobj;
161362306a36Sopenharmony_ci	struct damon_sysfs_kdamond **kdamonds_arr;
161462306a36Sopenharmony_ci	int nr;
161562306a36Sopenharmony_ci};
161662306a36Sopenharmony_ci
161762306a36Sopenharmony_cistatic struct damon_sysfs_kdamonds *damon_sysfs_kdamonds_alloc(void)
161862306a36Sopenharmony_ci{
161962306a36Sopenharmony_ci	return kzalloc(sizeof(struct damon_sysfs_kdamonds), GFP_KERNEL);
162062306a36Sopenharmony_ci}
162162306a36Sopenharmony_ci
162262306a36Sopenharmony_cistatic void damon_sysfs_kdamonds_rm_dirs(struct damon_sysfs_kdamonds *kdamonds)
162362306a36Sopenharmony_ci{
162462306a36Sopenharmony_ci	struct damon_sysfs_kdamond **kdamonds_arr = kdamonds->kdamonds_arr;
162562306a36Sopenharmony_ci	int i;
162662306a36Sopenharmony_ci
162762306a36Sopenharmony_ci	for (i = 0; i < kdamonds->nr; i++) {
162862306a36Sopenharmony_ci		damon_sysfs_kdamond_rm_dirs(kdamonds_arr[i]);
162962306a36Sopenharmony_ci		kobject_put(&kdamonds_arr[i]->kobj);
163062306a36Sopenharmony_ci	}
163162306a36Sopenharmony_ci	kdamonds->nr = 0;
163262306a36Sopenharmony_ci	kfree(kdamonds_arr);
163362306a36Sopenharmony_ci	kdamonds->kdamonds_arr = NULL;
163462306a36Sopenharmony_ci}
163562306a36Sopenharmony_ci
163662306a36Sopenharmony_cistatic bool damon_sysfs_kdamonds_busy(struct damon_sysfs_kdamond **kdamonds,
163762306a36Sopenharmony_ci		int nr_kdamonds)
163862306a36Sopenharmony_ci{
163962306a36Sopenharmony_ci	int i;
164062306a36Sopenharmony_ci
164162306a36Sopenharmony_ci	for (i = 0; i < nr_kdamonds; i++) {
164262306a36Sopenharmony_ci		if (damon_sysfs_kdamond_running(kdamonds[i]) ||
164362306a36Sopenharmony_ci		    damon_sysfs_cmd_request.kdamond == kdamonds[i])
164462306a36Sopenharmony_ci			return true;
164562306a36Sopenharmony_ci	}
164662306a36Sopenharmony_ci
164762306a36Sopenharmony_ci	return false;
164862306a36Sopenharmony_ci}
164962306a36Sopenharmony_ci
165062306a36Sopenharmony_cistatic int damon_sysfs_kdamonds_add_dirs(struct damon_sysfs_kdamonds *kdamonds,
165162306a36Sopenharmony_ci		int nr_kdamonds)
165262306a36Sopenharmony_ci{
165362306a36Sopenharmony_ci	struct damon_sysfs_kdamond **kdamonds_arr, *kdamond;
165462306a36Sopenharmony_ci	int err, i;
165562306a36Sopenharmony_ci
165662306a36Sopenharmony_ci	if (damon_sysfs_kdamonds_busy(kdamonds->kdamonds_arr, kdamonds->nr))
165762306a36Sopenharmony_ci		return -EBUSY;
165862306a36Sopenharmony_ci
165962306a36Sopenharmony_ci	damon_sysfs_kdamonds_rm_dirs(kdamonds);
166062306a36Sopenharmony_ci	if (!nr_kdamonds)
166162306a36Sopenharmony_ci		return 0;
166262306a36Sopenharmony_ci
166362306a36Sopenharmony_ci	kdamonds_arr = kmalloc_array(nr_kdamonds, sizeof(*kdamonds_arr),
166462306a36Sopenharmony_ci			GFP_KERNEL | __GFP_NOWARN);
166562306a36Sopenharmony_ci	if (!kdamonds_arr)
166662306a36Sopenharmony_ci		return -ENOMEM;
166762306a36Sopenharmony_ci	kdamonds->kdamonds_arr = kdamonds_arr;
166862306a36Sopenharmony_ci
166962306a36Sopenharmony_ci	for (i = 0; i < nr_kdamonds; i++) {
167062306a36Sopenharmony_ci		kdamond = damon_sysfs_kdamond_alloc();
167162306a36Sopenharmony_ci		if (!kdamond) {
167262306a36Sopenharmony_ci			damon_sysfs_kdamonds_rm_dirs(kdamonds);
167362306a36Sopenharmony_ci			return -ENOMEM;
167462306a36Sopenharmony_ci		}
167562306a36Sopenharmony_ci
167662306a36Sopenharmony_ci		err = kobject_init_and_add(&kdamond->kobj,
167762306a36Sopenharmony_ci				&damon_sysfs_kdamond_ktype, &kdamonds->kobj,
167862306a36Sopenharmony_ci				"%d", i);
167962306a36Sopenharmony_ci		if (err)
168062306a36Sopenharmony_ci			goto out;
168162306a36Sopenharmony_ci
168262306a36Sopenharmony_ci		err = damon_sysfs_kdamond_add_dirs(kdamond);
168362306a36Sopenharmony_ci		if (err)
168462306a36Sopenharmony_ci			goto out;
168562306a36Sopenharmony_ci
168662306a36Sopenharmony_ci		kdamonds_arr[i] = kdamond;
168762306a36Sopenharmony_ci		kdamonds->nr++;
168862306a36Sopenharmony_ci	}
168962306a36Sopenharmony_ci	return 0;
169062306a36Sopenharmony_ci
169162306a36Sopenharmony_ciout:
169262306a36Sopenharmony_ci	damon_sysfs_kdamonds_rm_dirs(kdamonds);
169362306a36Sopenharmony_ci	kobject_put(&kdamond->kobj);
169462306a36Sopenharmony_ci	return err;
169562306a36Sopenharmony_ci}
169662306a36Sopenharmony_ci
169762306a36Sopenharmony_cistatic ssize_t nr_kdamonds_show(struct kobject *kobj,
169862306a36Sopenharmony_ci		struct kobj_attribute *attr, char *buf)
169962306a36Sopenharmony_ci{
170062306a36Sopenharmony_ci	struct damon_sysfs_kdamonds *kdamonds = container_of(kobj,
170162306a36Sopenharmony_ci			struct damon_sysfs_kdamonds, kobj);
170262306a36Sopenharmony_ci
170362306a36Sopenharmony_ci	return sysfs_emit(buf, "%d\n", kdamonds->nr);
170462306a36Sopenharmony_ci}
170562306a36Sopenharmony_ci
170662306a36Sopenharmony_cistatic ssize_t nr_kdamonds_store(struct kobject *kobj,
170762306a36Sopenharmony_ci		struct kobj_attribute *attr, const char *buf, size_t count)
170862306a36Sopenharmony_ci{
170962306a36Sopenharmony_ci	struct damon_sysfs_kdamonds *kdamonds;
171062306a36Sopenharmony_ci	int nr, err;
171162306a36Sopenharmony_ci
171262306a36Sopenharmony_ci	err = kstrtoint(buf, 0, &nr);
171362306a36Sopenharmony_ci	if (err)
171462306a36Sopenharmony_ci		return err;
171562306a36Sopenharmony_ci	if (nr < 0)
171662306a36Sopenharmony_ci		return -EINVAL;
171762306a36Sopenharmony_ci
171862306a36Sopenharmony_ci	kdamonds = container_of(kobj, struct damon_sysfs_kdamonds, kobj);
171962306a36Sopenharmony_ci
172062306a36Sopenharmony_ci	if (!mutex_trylock(&damon_sysfs_lock))
172162306a36Sopenharmony_ci		return -EBUSY;
172262306a36Sopenharmony_ci	err = damon_sysfs_kdamonds_add_dirs(kdamonds, nr);
172362306a36Sopenharmony_ci	mutex_unlock(&damon_sysfs_lock);
172462306a36Sopenharmony_ci	if (err)
172562306a36Sopenharmony_ci		return err;
172662306a36Sopenharmony_ci
172762306a36Sopenharmony_ci	return count;
172862306a36Sopenharmony_ci}
172962306a36Sopenharmony_ci
173062306a36Sopenharmony_cistatic void damon_sysfs_kdamonds_release(struct kobject *kobj)
173162306a36Sopenharmony_ci{
173262306a36Sopenharmony_ci	kfree(container_of(kobj, struct damon_sysfs_kdamonds, kobj));
173362306a36Sopenharmony_ci}
173462306a36Sopenharmony_ci
173562306a36Sopenharmony_cistatic struct kobj_attribute damon_sysfs_kdamonds_nr_attr =
173662306a36Sopenharmony_ci		__ATTR_RW_MODE(nr_kdamonds, 0600);
173762306a36Sopenharmony_ci
173862306a36Sopenharmony_cistatic struct attribute *damon_sysfs_kdamonds_attrs[] = {
173962306a36Sopenharmony_ci	&damon_sysfs_kdamonds_nr_attr.attr,
174062306a36Sopenharmony_ci	NULL,
174162306a36Sopenharmony_ci};
174262306a36Sopenharmony_ciATTRIBUTE_GROUPS(damon_sysfs_kdamonds);
174362306a36Sopenharmony_ci
174462306a36Sopenharmony_cistatic const struct kobj_type damon_sysfs_kdamonds_ktype = {
174562306a36Sopenharmony_ci	.release = damon_sysfs_kdamonds_release,
174662306a36Sopenharmony_ci	.sysfs_ops = &kobj_sysfs_ops,
174762306a36Sopenharmony_ci	.default_groups = damon_sysfs_kdamonds_groups,
174862306a36Sopenharmony_ci};
174962306a36Sopenharmony_ci
175062306a36Sopenharmony_ci/*
175162306a36Sopenharmony_ci * damon user interface directory
175262306a36Sopenharmony_ci */
175362306a36Sopenharmony_ci
175462306a36Sopenharmony_cistruct damon_sysfs_ui_dir {
175562306a36Sopenharmony_ci	struct kobject kobj;
175662306a36Sopenharmony_ci	struct damon_sysfs_kdamonds *kdamonds;
175762306a36Sopenharmony_ci};
175862306a36Sopenharmony_ci
175962306a36Sopenharmony_cistatic struct damon_sysfs_ui_dir *damon_sysfs_ui_dir_alloc(void)
176062306a36Sopenharmony_ci{
176162306a36Sopenharmony_ci	return kzalloc(sizeof(struct damon_sysfs_ui_dir), GFP_KERNEL);
176262306a36Sopenharmony_ci}
176362306a36Sopenharmony_ci
176462306a36Sopenharmony_cistatic int damon_sysfs_ui_dir_add_dirs(struct damon_sysfs_ui_dir *ui_dir)
176562306a36Sopenharmony_ci{
176662306a36Sopenharmony_ci	struct damon_sysfs_kdamonds *kdamonds;
176762306a36Sopenharmony_ci	int err;
176862306a36Sopenharmony_ci
176962306a36Sopenharmony_ci	kdamonds = damon_sysfs_kdamonds_alloc();
177062306a36Sopenharmony_ci	if (!kdamonds)
177162306a36Sopenharmony_ci		return -ENOMEM;
177262306a36Sopenharmony_ci
177362306a36Sopenharmony_ci	err = kobject_init_and_add(&kdamonds->kobj,
177462306a36Sopenharmony_ci			&damon_sysfs_kdamonds_ktype, &ui_dir->kobj,
177562306a36Sopenharmony_ci			"kdamonds");
177662306a36Sopenharmony_ci	if (err) {
177762306a36Sopenharmony_ci		kobject_put(&kdamonds->kobj);
177862306a36Sopenharmony_ci		return err;
177962306a36Sopenharmony_ci	}
178062306a36Sopenharmony_ci	ui_dir->kdamonds = kdamonds;
178162306a36Sopenharmony_ci	return err;
178262306a36Sopenharmony_ci}
178362306a36Sopenharmony_ci
178462306a36Sopenharmony_cistatic void damon_sysfs_ui_dir_release(struct kobject *kobj)
178562306a36Sopenharmony_ci{
178662306a36Sopenharmony_ci	kfree(container_of(kobj, struct damon_sysfs_ui_dir, kobj));
178762306a36Sopenharmony_ci}
178862306a36Sopenharmony_ci
178962306a36Sopenharmony_cistatic struct attribute *damon_sysfs_ui_dir_attrs[] = {
179062306a36Sopenharmony_ci	NULL,
179162306a36Sopenharmony_ci};
179262306a36Sopenharmony_ciATTRIBUTE_GROUPS(damon_sysfs_ui_dir);
179362306a36Sopenharmony_ci
179462306a36Sopenharmony_cistatic const struct kobj_type damon_sysfs_ui_dir_ktype = {
179562306a36Sopenharmony_ci	.release = damon_sysfs_ui_dir_release,
179662306a36Sopenharmony_ci	.sysfs_ops = &kobj_sysfs_ops,
179762306a36Sopenharmony_ci	.default_groups = damon_sysfs_ui_dir_groups,
179862306a36Sopenharmony_ci};
179962306a36Sopenharmony_ci
180062306a36Sopenharmony_cistatic int __init damon_sysfs_init(void)
180162306a36Sopenharmony_ci{
180262306a36Sopenharmony_ci	struct kobject *damon_sysfs_root;
180362306a36Sopenharmony_ci	struct damon_sysfs_ui_dir *admin;
180462306a36Sopenharmony_ci	int err;
180562306a36Sopenharmony_ci
180662306a36Sopenharmony_ci	damon_sysfs_root = kobject_create_and_add("damon", mm_kobj);
180762306a36Sopenharmony_ci	if (!damon_sysfs_root)
180862306a36Sopenharmony_ci		return -ENOMEM;
180962306a36Sopenharmony_ci
181062306a36Sopenharmony_ci	admin = damon_sysfs_ui_dir_alloc();
181162306a36Sopenharmony_ci	if (!admin) {
181262306a36Sopenharmony_ci		kobject_put(damon_sysfs_root);
181362306a36Sopenharmony_ci		return -ENOMEM;
181462306a36Sopenharmony_ci	}
181562306a36Sopenharmony_ci	err = kobject_init_and_add(&admin->kobj, &damon_sysfs_ui_dir_ktype,
181662306a36Sopenharmony_ci			damon_sysfs_root, "admin");
181762306a36Sopenharmony_ci	if (err)
181862306a36Sopenharmony_ci		goto out;
181962306a36Sopenharmony_ci	err = damon_sysfs_ui_dir_add_dirs(admin);
182062306a36Sopenharmony_ci	if (err)
182162306a36Sopenharmony_ci		goto out;
182262306a36Sopenharmony_ci	return 0;
182362306a36Sopenharmony_ci
182462306a36Sopenharmony_ciout:
182562306a36Sopenharmony_ci	kobject_put(&admin->kobj);
182662306a36Sopenharmony_ci	kobject_put(damon_sysfs_root);
182762306a36Sopenharmony_ci	return err;
182862306a36Sopenharmony_ci}
182962306a36Sopenharmony_cisubsys_initcall(damon_sysfs_init);
1830