xref: /openbmc/linux/mm/damon/dbgfs.c (revision 90bebce9)
14bc05954SSeongJae Park // SPDX-License-Identifier: GPL-2.0
24bc05954SSeongJae Park /*
34bc05954SSeongJae Park  * DAMON Debugfs Interface
44bc05954SSeongJae Park  *
54bc05954SSeongJae Park  * Author: SeongJae Park <sjpark@amazon.de>
64bc05954SSeongJae Park  */
74bc05954SSeongJae Park 
84bc05954SSeongJae Park #define pr_fmt(fmt) "damon-dbgfs: " fmt
94bc05954SSeongJae Park 
104bc05954SSeongJae Park #include <linux/damon.h>
114bc05954SSeongJae Park #include <linux/debugfs.h>
124bc05954SSeongJae Park #include <linux/file.h>
134bc05954SSeongJae Park #include <linux/mm.h>
144bc05954SSeongJae Park #include <linux/module.h>
154bc05954SSeongJae Park #include <linux/page_idle.h>
164bc05954SSeongJae Park #include <linux/slab.h>
174bc05954SSeongJae Park 
184bc05954SSeongJae Park static struct damon_ctx **dbgfs_ctxs;
194bc05954SSeongJae Park static int dbgfs_nr_ctxs;
204bc05954SSeongJae Park static struct dentry **dbgfs_dirs;
2175c1c2b5SSeongJae Park static DEFINE_MUTEX(damon_dbgfs_lock);
224bc05954SSeongJae Park 
234bc05954SSeongJae Park /*
244bc05954SSeongJae Park  * Returns non-empty string on success, negative error code otherwise.
254bc05954SSeongJae Park  */
264bc05954SSeongJae Park static char *user_input_str(const char __user *buf, size_t count, loff_t *ppos)
274bc05954SSeongJae Park {
284bc05954SSeongJae Park 	char *kbuf;
294bc05954SSeongJae Park 	ssize_t ret;
304bc05954SSeongJae Park 
314bc05954SSeongJae Park 	/* We do not accept continuous write */
324bc05954SSeongJae Park 	if (*ppos)
334bc05954SSeongJae Park 		return ERR_PTR(-EINVAL);
344bc05954SSeongJae Park 
354bc05954SSeongJae Park 	kbuf = kmalloc(count + 1, GFP_KERNEL);
364bc05954SSeongJae Park 	if (!kbuf)
374bc05954SSeongJae Park 		return ERR_PTR(-ENOMEM);
384bc05954SSeongJae Park 
394bc05954SSeongJae Park 	ret = simple_write_to_buffer(kbuf, count + 1, ppos, buf, count);
404bc05954SSeongJae Park 	if (ret != count) {
414bc05954SSeongJae Park 		kfree(kbuf);
424bc05954SSeongJae Park 		return ERR_PTR(-EIO);
434bc05954SSeongJae Park 	}
444bc05954SSeongJae Park 	kbuf[ret] = '\0';
454bc05954SSeongJae Park 
464bc05954SSeongJae Park 	return kbuf;
474bc05954SSeongJae Park }
484bc05954SSeongJae Park 
494bc05954SSeongJae Park static ssize_t dbgfs_attrs_read(struct file *file,
504bc05954SSeongJae Park 		char __user *buf, size_t count, loff_t *ppos)
514bc05954SSeongJae Park {
524bc05954SSeongJae Park 	struct damon_ctx *ctx = file->private_data;
534bc05954SSeongJae Park 	char kbuf[128];
544bc05954SSeongJae Park 	int ret;
554bc05954SSeongJae Park 
564bc05954SSeongJae Park 	mutex_lock(&ctx->kdamond_lock);
574bc05954SSeongJae Park 	ret = scnprintf(kbuf, ARRAY_SIZE(kbuf), "%lu %lu %lu %lu %lu\n",
584bc05954SSeongJae Park 			ctx->sample_interval, ctx->aggr_interval,
594bc05954SSeongJae Park 			ctx->primitive_update_interval, ctx->min_nr_regions,
604bc05954SSeongJae Park 			ctx->max_nr_regions);
614bc05954SSeongJae Park 	mutex_unlock(&ctx->kdamond_lock);
624bc05954SSeongJae Park 
634bc05954SSeongJae Park 	return simple_read_from_buffer(buf, count, ppos, kbuf, ret);
644bc05954SSeongJae Park }
654bc05954SSeongJae Park 
664bc05954SSeongJae Park static ssize_t dbgfs_attrs_write(struct file *file,
674bc05954SSeongJae Park 		const char __user *buf, size_t count, loff_t *ppos)
684bc05954SSeongJae Park {
694bc05954SSeongJae Park 	struct damon_ctx *ctx = file->private_data;
704bc05954SSeongJae Park 	unsigned long s, a, r, minr, maxr;
714bc05954SSeongJae Park 	char *kbuf;
724bc05954SSeongJae Park 	ssize_t ret = count;
734bc05954SSeongJae Park 	int err;
744bc05954SSeongJae Park 
754bc05954SSeongJae Park 	kbuf = user_input_str(buf, count, ppos);
764bc05954SSeongJae Park 	if (IS_ERR(kbuf))
774bc05954SSeongJae Park 		return PTR_ERR(kbuf);
784bc05954SSeongJae Park 
794bc05954SSeongJae Park 	if (sscanf(kbuf, "%lu %lu %lu %lu %lu",
804bc05954SSeongJae Park 				&s, &a, &r, &minr, &maxr) != 5) {
814bc05954SSeongJae Park 		ret = -EINVAL;
824bc05954SSeongJae Park 		goto out;
834bc05954SSeongJae Park 	}
844bc05954SSeongJae Park 
854bc05954SSeongJae Park 	mutex_lock(&ctx->kdamond_lock);
864bc05954SSeongJae Park 	if (ctx->kdamond) {
874bc05954SSeongJae Park 		ret = -EBUSY;
884bc05954SSeongJae Park 		goto unlock_out;
894bc05954SSeongJae Park 	}
904bc05954SSeongJae Park 
914bc05954SSeongJae Park 	err = damon_set_attrs(ctx, s, a, r, minr, maxr);
924bc05954SSeongJae Park 	if (err)
934bc05954SSeongJae Park 		ret = err;
944bc05954SSeongJae Park unlock_out:
954bc05954SSeongJae Park 	mutex_unlock(&ctx->kdamond_lock);
964bc05954SSeongJae Park out:
974bc05954SSeongJae Park 	kfree(kbuf);
984bc05954SSeongJae Park 	return ret;
994bc05954SSeongJae Park }
1004bc05954SSeongJae Park 
101af122dd8SSeongJae Park static ssize_t sprint_schemes(struct damon_ctx *c, char *buf, ssize_t len)
102af122dd8SSeongJae Park {
103af122dd8SSeongJae Park 	struct damos *s;
104af122dd8SSeongJae Park 	int written = 0;
105af122dd8SSeongJae Park 	int rc;
106af122dd8SSeongJae Park 
107af122dd8SSeongJae Park 	damon_for_each_scheme(s, c) {
108af122dd8SSeongJae Park 		rc = scnprintf(&buf[written], len - written,
1092f0b548cSSeongJae Park 				"%lu %lu %u %u %u %u %d %lu %lu\n",
110af122dd8SSeongJae Park 				s->min_sz_region, s->max_sz_region,
111af122dd8SSeongJae Park 				s->min_nr_accesses, s->max_nr_accesses,
112af122dd8SSeongJae Park 				s->min_age_region, s->max_age_region,
1132f0b548cSSeongJae Park 				s->action, s->stat_count, s->stat_sz);
114af122dd8SSeongJae Park 		if (!rc)
115af122dd8SSeongJae Park 			return -ENOMEM;
116af122dd8SSeongJae Park 
117af122dd8SSeongJae Park 		written += rc;
118af122dd8SSeongJae Park 	}
119af122dd8SSeongJae Park 	return written;
120af122dd8SSeongJae Park }
121af122dd8SSeongJae Park 
122af122dd8SSeongJae Park static ssize_t dbgfs_schemes_read(struct file *file, char __user *buf,
123af122dd8SSeongJae Park 		size_t count, loff_t *ppos)
124af122dd8SSeongJae Park {
125af122dd8SSeongJae Park 	struct damon_ctx *ctx = file->private_data;
126af122dd8SSeongJae Park 	char *kbuf;
127af122dd8SSeongJae Park 	ssize_t len;
128af122dd8SSeongJae Park 
129af122dd8SSeongJae Park 	kbuf = kmalloc(count, GFP_KERNEL);
130af122dd8SSeongJae Park 	if (!kbuf)
131af122dd8SSeongJae Park 		return -ENOMEM;
132af122dd8SSeongJae Park 
133af122dd8SSeongJae Park 	mutex_lock(&ctx->kdamond_lock);
134af122dd8SSeongJae Park 	len = sprint_schemes(ctx, kbuf, count);
135af122dd8SSeongJae Park 	mutex_unlock(&ctx->kdamond_lock);
136af122dd8SSeongJae Park 	if (len < 0)
137af122dd8SSeongJae Park 		goto out;
138af122dd8SSeongJae Park 	len = simple_read_from_buffer(buf, count, ppos, kbuf, len);
139af122dd8SSeongJae Park 
140af122dd8SSeongJae Park out:
141af122dd8SSeongJae Park 	kfree(kbuf);
142af122dd8SSeongJae Park 	return len;
143af122dd8SSeongJae Park }
144af122dd8SSeongJae Park 
145af122dd8SSeongJae Park static void free_schemes_arr(struct damos **schemes, ssize_t nr_schemes)
146af122dd8SSeongJae Park {
147af122dd8SSeongJae Park 	ssize_t i;
148af122dd8SSeongJae Park 
149af122dd8SSeongJae Park 	for (i = 0; i < nr_schemes; i++)
150af122dd8SSeongJae Park 		kfree(schemes[i]);
151af122dd8SSeongJae Park 	kfree(schemes);
152af122dd8SSeongJae Park }
153af122dd8SSeongJae Park 
154af122dd8SSeongJae Park static bool damos_action_valid(int action)
155af122dd8SSeongJae Park {
156af122dd8SSeongJae Park 	switch (action) {
157af122dd8SSeongJae Park 	case DAMOS_WILLNEED:
158af122dd8SSeongJae Park 	case DAMOS_COLD:
159af122dd8SSeongJae Park 	case DAMOS_PAGEOUT:
160af122dd8SSeongJae Park 	case DAMOS_HUGEPAGE:
161af122dd8SSeongJae Park 	case DAMOS_NOHUGEPAGE:
1622f0b548cSSeongJae Park 	case DAMOS_STAT:
163af122dd8SSeongJae Park 		return true;
164af122dd8SSeongJae Park 	default:
165af122dd8SSeongJae Park 		return false;
166af122dd8SSeongJae Park 	}
167af122dd8SSeongJae Park }
168af122dd8SSeongJae Park 
169af122dd8SSeongJae Park /*
170af122dd8SSeongJae Park  * Converts a string into an array of struct damos pointers
171af122dd8SSeongJae Park  *
172af122dd8SSeongJae Park  * Returns an array of struct damos pointers that converted if the conversion
173af122dd8SSeongJae Park  * success, or NULL otherwise.
174af122dd8SSeongJae Park  */
175af122dd8SSeongJae Park static struct damos **str_to_schemes(const char *str, ssize_t len,
176af122dd8SSeongJae Park 				ssize_t *nr_schemes)
177af122dd8SSeongJae Park {
178af122dd8SSeongJae Park 	struct damos *scheme, **schemes;
179af122dd8SSeongJae Park 	const int max_nr_schemes = 256;
180af122dd8SSeongJae Park 	int pos = 0, parsed, ret;
181af122dd8SSeongJae Park 	unsigned long min_sz, max_sz;
182af122dd8SSeongJae Park 	unsigned int min_nr_a, max_nr_a, min_age, max_age;
183af122dd8SSeongJae Park 	unsigned int action;
184af122dd8SSeongJae Park 
185af122dd8SSeongJae Park 	schemes = kmalloc_array(max_nr_schemes, sizeof(scheme),
186af122dd8SSeongJae Park 			GFP_KERNEL);
187af122dd8SSeongJae Park 	if (!schemes)
188af122dd8SSeongJae Park 		return NULL;
189af122dd8SSeongJae Park 
190af122dd8SSeongJae Park 	*nr_schemes = 0;
191af122dd8SSeongJae Park 	while (pos < len && *nr_schemes < max_nr_schemes) {
192af122dd8SSeongJae Park 		ret = sscanf(&str[pos], "%lu %lu %u %u %u %u %u%n",
193af122dd8SSeongJae Park 				&min_sz, &max_sz, &min_nr_a, &max_nr_a,
194af122dd8SSeongJae Park 				&min_age, &max_age, &action, &parsed);
195af122dd8SSeongJae Park 		if (ret != 7)
196af122dd8SSeongJae Park 			break;
197af122dd8SSeongJae Park 		if (!damos_action_valid(action)) {
198af122dd8SSeongJae Park 			pr_err("wrong action %d\n", action);
199af122dd8SSeongJae Park 			goto fail;
200af122dd8SSeongJae Park 		}
201af122dd8SSeongJae Park 
202af122dd8SSeongJae Park 		pos += parsed;
203af122dd8SSeongJae Park 		scheme = damon_new_scheme(min_sz, max_sz, min_nr_a, max_nr_a,
204af122dd8SSeongJae Park 				min_age, max_age, action);
205af122dd8SSeongJae Park 		if (!scheme)
206af122dd8SSeongJae Park 			goto fail;
207af122dd8SSeongJae Park 
208af122dd8SSeongJae Park 		schemes[*nr_schemes] = scheme;
209af122dd8SSeongJae Park 		*nr_schemes += 1;
210af122dd8SSeongJae Park 	}
211af122dd8SSeongJae Park 	return schemes;
212af122dd8SSeongJae Park fail:
213af122dd8SSeongJae Park 	free_schemes_arr(schemes, *nr_schemes);
214af122dd8SSeongJae Park 	return NULL;
215af122dd8SSeongJae Park }
216af122dd8SSeongJae Park 
217af122dd8SSeongJae Park static ssize_t dbgfs_schemes_write(struct file *file, const char __user *buf,
218af122dd8SSeongJae Park 		size_t count, loff_t *ppos)
219af122dd8SSeongJae Park {
220af122dd8SSeongJae Park 	struct damon_ctx *ctx = file->private_data;
221af122dd8SSeongJae Park 	char *kbuf;
222af122dd8SSeongJae Park 	struct damos **schemes;
223af122dd8SSeongJae Park 	ssize_t nr_schemes = 0, ret = count;
224af122dd8SSeongJae Park 	int err;
225af122dd8SSeongJae Park 
226af122dd8SSeongJae Park 	kbuf = user_input_str(buf, count, ppos);
227af122dd8SSeongJae Park 	if (IS_ERR(kbuf))
228af122dd8SSeongJae Park 		return PTR_ERR(kbuf);
229af122dd8SSeongJae Park 
230af122dd8SSeongJae Park 	schemes = str_to_schemes(kbuf, ret, &nr_schemes);
231af122dd8SSeongJae Park 	if (!schemes) {
232af122dd8SSeongJae Park 		ret = -EINVAL;
233af122dd8SSeongJae Park 		goto out;
234af122dd8SSeongJae Park 	}
235af122dd8SSeongJae Park 
236af122dd8SSeongJae Park 	mutex_lock(&ctx->kdamond_lock);
237af122dd8SSeongJae Park 	if (ctx->kdamond) {
238af122dd8SSeongJae Park 		ret = -EBUSY;
239af122dd8SSeongJae Park 		goto unlock_out;
240af122dd8SSeongJae Park 	}
241af122dd8SSeongJae Park 
242af122dd8SSeongJae Park 	err = damon_set_schemes(ctx, schemes, nr_schemes);
243af122dd8SSeongJae Park 	if (err)
244af122dd8SSeongJae Park 		ret = err;
245af122dd8SSeongJae Park 	else
246af122dd8SSeongJae Park 		nr_schemes = 0;
247af122dd8SSeongJae Park unlock_out:
248af122dd8SSeongJae Park 	mutex_unlock(&ctx->kdamond_lock);
249af122dd8SSeongJae Park 	free_schemes_arr(schemes, nr_schemes);
250af122dd8SSeongJae Park out:
251af122dd8SSeongJae Park 	kfree(kbuf);
252af122dd8SSeongJae Park 	return ret;
253af122dd8SSeongJae Park }
254af122dd8SSeongJae Park 
2554bc05954SSeongJae Park static inline bool targetid_is_pid(const struct damon_ctx *ctx)
2564bc05954SSeongJae Park {
2574bc05954SSeongJae Park 	return ctx->primitive.target_valid == damon_va_target_valid;
2584bc05954SSeongJae Park }
2594bc05954SSeongJae Park 
2604bc05954SSeongJae Park static ssize_t sprint_target_ids(struct damon_ctx *ctx, char *buf, ssize_t len)
2614bc05954SSeongJae Park {
2624bc05954SSeongJae Park 	struct damon_target *t;
2634bc05954SSeongJae Park 	unsigned long id;
2644bc05954SSeongJae Park 	int written = 0;
2654bc05954SSeongJae Park 	int rc;
2664bc05954SSeongJae Park 
2674bc05954SSeongJae Park 	damon_for_each_target(t, ctx) {
2684bc05954SSeongJae Park 		id = t->id;
2694bc05954SSeongJae Park 		if (targetid_is_pid(ctx))
2704bc05954SSeongJae Park 			/* Show pid numbers to debugfs users */
2714bc05954SSeongJae Park 			id = (unsigned long)pid_vnr((struct pid *)id);
2724bc05954SSeongJae Park 
2734bc05954SSeongJae Park 		rc = scnprintf(&buf[written], len - written, "%lu ", id);
2744bc05954SSeongJae Park 		if (!rc)
2754bc05954SSeongJae Park 			return -ENOMEM;
2764bc05954SSeongJae Park 		written += rc;
2774bc05954SSeongJae Park 	}
2784bc05954SSeongJae Park 	if (written)
2794bc05954SSeongJae Park 		written -= 1;
2804bc05954SSeongJae Park 	written += scnprintf(&buf[written], len - written, "\n");
2814bc05954SSeongJae Park 	return written;
2824bc05954SSeongJae Park }
2834bc05954SSeongJae Park 
2844bc05954SSeongJae Park static ssize_t dbgfs_target_ids_read(struct file *file,
2854bc05954SSeongJae Park 		char __user *buf, size_t count, loff_t *ppos)
2864bc05954SSeongJae Park {
2874bc05954SSeongJae Park 	struct damon_ctx *ctx = file->private_data;
2884bc05954SSeongJae Park 	ssize_t len;
2894bc05954SSeongJae Park 	char ids_buf[320];
2904bc05954SSeongJae Park 
2914bc05954SSeongJae Park 	mutex_lock(&ctx->kdamond_lock);
2924bc05954SSeongJae Park 	len = sprint_target_ids(ctx, ids_buf, 320);
2934bc05954SSeongJae Park 	mutex_unlock(&ctx->kdamond_lock);
2944bc05954SSeongJae Park 	if (len < 0)
2954bc05954SSeongJae Park 		return len;
2964bc05954SSeongJae Park 
2974bc05954SSeongJae Park 	return simple_read_from_buffer(buf, count, ppos, ids_buf, len);
2984bc05954SSeongJae Park }
2994bc05954SSeongJae Park 
3004bc05954SSeongJae Park /*
3014bc05954SSeongJae Park  * Converts a string into an array of unsigned long integers
3024bc05954SSeongJae Park  *
3034bc05954SSeongJae Park  * Returns an array of unsigned long integers if the conversion success, or
3044bc05954SSeongJae Park  * NULL otherwise.
3054bc05954SSeongJae Park  */
3064bc05954SSeongJae Park static unsigned long *str_to_target_ids(const char *str, ssize_t len,
3074bc05954SSeongJae Park 					ssize_t *nr_ids)
3084bc05954SSeongJae Park {
3094bc05954SSeongJae Park 	unsigned long *ids;
3104bc05954SSeongJae Park 	const int max_nr_ids = 32;
3114bc05954SSeongJae Park 	unsigned long id;
3124bc05954SSeongJae Park 	int pos = 0, parsed, ret;
3134bc05954SSeongJae Park 
3144bc05954SSeongJae Park 	*nr_ids = 0;
3154bc05954SSeongJae Park 	ids = kmalloc_array(max_nr_ids, sizeof(id), GFP_KERNEL);
3164bc05954SSeongJae Park 	if (!ids)
3174bc05954SSeongJae Park 		return NULL;
3184bc05954SSeongJae Park 	while (*nr_ids < max_nr_ids && pos < len) {
3194bc05954SSeongJae Park 		ret = sscanf(&str[pos], "%lu%n", &id, &parsed);
3204bc05954SSeongJae Park 		pos += parsed;
3214bc05954SSeongJae Park 		if (ret != 1)
3224bc05954SSeongJae Park 			break;
3234bc05954SSeongJae Park 		ids[*nr_ids] = id;
3244bc05954SSeongJae Park 		*nr_ids += 1;
3254bc05954SSeongJae Park 	}
3264bc05954SSeongJae Park 
3274bc05954SSeongJae Park 	return ids;
3284bc05954SSeongJae Park }
3294bc05954SSeongJae Park 
3304bc05954SSeongJae Park static void dbgfs_put_pids(unsigned long *ids, int nr_ids)
3314bc05954SSeongJae Park {
3324bc05954SSeongJae Park 	int i;
3334bc05954SSeongJae Park 
3344bc05954SSeongJae Park 	for (i = 0; i < nr_ids; i++)
3354bc05954SSeongJae Park 		put_pid((struct pid *)ids[i]);
3364bc05954SSeongJae Park }
3374bc05954SSeongJae Park 
3384bc05954SSeongJae Park static ssize_t dbgfs_target_ids_write(struct file *file,
3394bc05954SSeongJae Park 		const char __user *buf, size_t count, loff_t *ppos)
3404bc05954SSeongJae Park {
3414bc05954SSeongJae Park 	struct damon_ctx *ctx = file->private_data;
3424bc05954SSeongJae Park 	char *kbuf, *nrs;
3434bc05954SSeongJae Park 	unsigned long *targets;
3444bc05954SSeongJae Park 	ssize_t nr_targets;
3454bc05954SSeongJae Park 	ssize_t ret = count;
3464bc05954SSeongJae Park 	int i;
3474bc05954SSeongJae Park 	int err;
3484bc05954SSeongJae Park 
3494bc05954SSeongJae Park 	kbuf = user_input_str(buf, count, ppos);
3504bc05954SSeongJae Park 	if (IS_ERR(kbuf))
3514bc05954SSeongJae Park 		return PTR_ERR(kbuf);
3524bc05954SSeongJae Park 
3534bc05954SSeongJae Park 	nrs = kbuf;
3544bc05954SSeongJae Park 
3554bc05954SSeongJae Park 	targets = str_to_target_ids(nrs, ret, &nr_targets);
3564bc05954SSeongJae Park 	if (!targets) {
3574bc05954SSeongJae Park 		ret = -ENOMEM;
3584bc05954SSeongJae Park 		goto out;
3594bc05954SSeongJae Park 	}
3604bc05954SSeongJae Park 
3614bc05954SSeongJae Park 	if (targetid_is_pid(ctx)) {
3624bc05954SSeongJae Park 		for (i = 0; i < nr_targets; i++) {
3634bc05954SSeongJae Park 			targets[i] = (unsigned long)find_get_pid(
3644bc05954SSeongJae Park 					(int)targets[i]);
3654bc05954SSeongJae Park 			if (!targets[i]) {
3664bc05954SSeongJae Park 				dbgfs_put_pids(targets, i);
3674bc05954SSeongJae Park 				ret = -EINVAL;
3684bc05954SSeongJae Park 				goto free_targets_out;
3694bc05954SSeongJae Park 			}
3704bc05954SSeongJae Park 		}
3714bc05954SSeongJae Park 	}
3724bc05954SSeongJae Park 
3734bc05954SSeongJae Park 	mutex_lock(&ctx->kdamond_lock);
3744bc05954SSeongJae Park 	if (ctx->kdamond) {
3754bc05954SSeongJae Park 		if (targetid_is_pid(ctx))
3764bc05954SSeongJae Park 			dbgfs_put_pids(targets, nr_targets);
3774bc05954SSeongJae Park 		ret = -EBUSY;
3784bc05954SSeongJae Park 		goto unlock_out;
3794bc05954SSeongJae Park 	}
3804bc05954SSeongJae Park 
3814bc05954SSeongJae Park 	err = damon_set_targets(ctx, targets, nr_targets);
3824bc05954SSeongJae Park 	if (err) {
3834bc05954SSeongJae Park 		if (targetid_is_pid(ctx))
3844bc05954SSeongJae Park 			dbgfs_put_pids(targets, nr_targets);
3854bc05954SSeongJae Park 		ret = err;
3864bc05954SSeongJae Park 	}
3874bc05954SSeongJae Park 
3884bc05954SSeongJae Park unlock_out:
3894bc05954SSeongJae Park 	mutex_unlock(&ctx->kdamond_lock);
3904bc05954SSeongJae Park free_targets_out:
3914bc05954SSeongJae Park 	kfree(targets);
3924bc05954SSeongJae Park out:
3934bc05954SSeongJae Park 	kfree(kbuf);
3944bc05954SSeongJae Park 	return ret;
3954bc05954SSeongJae Park }
3964bc05954SSeongJae Park 
397*90bebce9SSeongJae Park static ssize_t sprint_init_regions(struct damon_ctx *c, char *buf, ssize_t len)
398*90bebce9SSeongJae Park {
399*90bebce9SSeongJae Park 	struct damon_target *t;
400*90bebce9SSeongJae Park 	struct damon_region *r;
401*90bebce9SSeongJae Park 	int written = 0;
402*90bebce9SSeongJae Park 	int rc;
403*90bebce9SSeongJae Park 
404*90bebce9SSeongJae Park 	damon_for_each_target(t, c) {
405*90bebce9SSeongJae Park 		damon_for_each_region(r, t) {
406*90bebce9SSeongJae Park 			rc = scnprintf(&buf[written], len - written,
407*90bebce9SSeongJae Park 					"%lu %lu %lu\n",
408*90bebce9SSeongJae Park 					t->id, r->ar.start, r->ar.end);
409*90bebce9SSeongJae Park 			if (!rc)
410*90bebce9SSeongJae Park 				return -ENOMEM;
411*90bebce9SSeongJae Park 			written += rc;
412*90bebce9SSeongJae Park 		}
413*90bebce9SSeongJae Park 	}
414*90bebce9SSeongJae Park 	return written;
415*90bebce9SSeongJae Park }
416*90bebce9SSeongJae Park 
417*90bebce9SSeongJae Park static ssize_t dbgfs_init_regions_read(struct file *file, char __user *buf,
418*90bebce9SSeongJae Park 		size_t count, loff_t *ppos)
419*90bebce9SSeongJae Park {
420*90bebce9SSeongJae Park 	struct damon_ctx *ctx = file->private_data;
421*90bebce9SSeongJae Park 	char *kbuf;
422*90bebce9SSeongJae Park 	ssize_t len;
423*90bebce9SSeongJae Park 
424*90bebce9SSeongJae Park 	kbuf = kmalloc(count, GFP_KERNEL);
425*90bebce9SSeongJae Park 	if (!kbuf)
426*90bebce9SSeongJae Park 		return -ENOMEM;
427*90bebce9SSeongJae Park 
428*90bebce9SSeongJae Park 	mutex_lock(&ctx->kdamond_lock);
429*90bebce9SSeongJae Park 	if (ctx->kdamond) {
430*90bebce9SSeongJae Park 		mutex_unlock(&ctx->kdamond_lock);
431*90bebce9SSeongJae Park 		len = -EBUSY;
432*90bebce9SSeongJae Park 		goto out;
433*90bebce9SSeongJae Park 	}
434*90bebce9SSeongJae Park 
435*90bebce9SSeongJae Park 	len = sprint_init_regions(ctx, kbuf, count);
436*90bebce9SSeongJae Park 	mutex_unlock(&ctx->kdamond_lock);
437*90bebce9SSeongJae Park 	if (len < 0)
438*90bebce9SSeongJae Park 		goto out;
439*90bebce9SSeongJae Park 	len = simple_read_from_buffer(buf, count, ppos, kbuf, len);
440*90bebce9SSeongJae Park 
441*90bebce9SSeongJae Park out:
442*90bebce9SSeongJae Park 	kfree(kbuf);
443*90bebce9SSeongJae Park 	return len;
444*90bebce9SSeongJae Park }
445*90bebce9SSeongJae Park 
446*90bebce9SSeongJae Park static int add_init_region(struct damon_ctx *c,
447*90bebce9SSeongJae Park 			 unsigned long target_id, struct damon_addr_range *ar)
448*90bebce9SSeongJae Park {
449*90bebce9SSeongJae Park 	struct damon_target *t;
450*90bebce9SSeongJae Park 	struct damon_region *r, *prev;
451*90bebce9SSeongJae Park 	unsigned long id;
452*90bebce9SSeongJae Park 	int rc = -EINVAL;
453*90bebce9SSeongJae Park 
454*90bebce9SSeongJae Park 	if (ar->start >= ar->end)
455*90bebce9SSeongJae Park 		return -EINVAL;
456*90bebce9SSeongJae Park 
457*90bebce9SSeongJae Park 	damon_for_each_target(t, c) {
458*90bebce9SSeongJae Park 		id = t->id;
459*90bebce9SSeongJae Park 		if (targetid_is_pid(c))
460*90bebce9SSeongJae Park 			id = (unsigned long)pid_vnr((struct pid *)id);
461*90bebce9SSeongJae Park 		if (id == target_id) {
462*90bebce9SSeongJae Park 			r = damon_new_region(ar->start, ar->end);
463*90bebce9SSeongJae Park 			if (!r)
464*90bebce9SSeongJae Park 				return -ENOMEM;
465*90bebce9SSeongJae Park 			damon_add_region(r, t);
466*90bebce9SSeongJae Park 			if (damon_nr_regions(t) > 1) {
467*90bebce9SSeongJae Park 				prev = damon_prev_region(r);
468*90bebce9SSeongJae Park 				if (prev->ar.end > r->ar.start) {
469*90bebce9SSeongJae Park 					damon_destroy_region(r, t);
470*90bebce9SSeongJae Park 					return -EINVAL;
471*90bebce9SSeongJae Park 				}
472*90bebce9SSeongJae Park 			}
473*90bebce9SSeongJae Park 			rc = 0;
474*90bebce9SSeongJae Park 		}
475*90bebce9SSeongJae Park 	}
476*90bebce9SSeongJae Park 	return rc;
477*90bebce9SSeongJae Park }
478*90bebce9SSeongJae Park 
479*90bebce9SSeongJae Park static int set_init_regions(struct damon_ctx *c, const char *str, ssize_t len)
480*90bebce9SSeongJae Park {
481*90bebce9SSeongJae Park 	struct damon_target *t;
482*90bebce9SSeongJae Park 	struct damon_region *r, *next;
483*90bebce9SSeongJae Park 	int pos = 0, parsed, ret;
484*90bebce9SSeongJae Park 	unsigned long target_id;
485*90bebce9SSeongJae Park 	struct damon_addr_range ar;
486*90bebce9SSeongJae Park 	int err;
487*90bebce9SSeongJae Park 
488*90bebce9SSeongJae Park 	damon_for_each_target(t, c) {
489*90bebce9SSeongJae Park 		damon_for_each_region_safe(r, next, t)
490*90bebce9SSeongJae Park 			damon_destroy_region(r, t);
491*90bebce9SSeongJae Park 	}
492*90bebce9SSeongJae Park 
493*90bebce9SSeongJae Park 	while (pos < len) {
494*90bebce9SSeongJae Park 		ret = sscanf(&str[pos], "%lu %lu %lu%n",
495*90bebce9SSeongJae Park 				&target_id, &ar.start, &ar.end, &parsed);
496*90bebce9SSeongJae Park 		if (ret != 3)
497*90bebce9SSeongJae Park 			break;
498*90bebce9SSeongJae Park 		err = add_init_region(c, target_id, &ar);
499*90bebce9SSeongJae Park 		if (err)
500*90bebce9SSeongJae Park 			goto fail;
501*90bebce9SSeongJae Park 		pos += parsed;
502*90bebce9SSeongJae Park 	}
503*90bebce9SSeongJae Park 
504*90bebce9SSeongJae Park 	return 0;
505*90bebce9SSeongJae Park 
506*90bebce9SSeongJae Park fail:
507*90bebce9SSeongJae Park 	damon_for_each_target(t, c) {
508*90bebce9SSeongJae Park 		damon_for_each_region_safe(r, next, t)
509*90bebce9SSeongJae Park 			damon_destroy_region(r, t);
510*90bebce9SSeongJae Park 	}
511*90bebce9SSeongJae Park 	return err;
512*90bebce9SSeongJae Park }
513*90bebce9SSeongJae Park 
514*90bebce9SSeongJae Park static ssize_t dbgfs_init_regions_write(struct file *file,
515*90bebce9SSeongJae Park 					  const char __user *buf, size_t count,
516*90bebce9SSeongJae Park 					  loff_t *ppos)
517*90bebce9SSeongJae Park {
518*90bebce9SSeongJae Park 	struct damon_ctx *ctx = file->private_data;
519*90bebce9SSeongJae Park 	char *kbuf;
520*90bebce9SSeongJae Park 	ssize_t ret = count;
521*90bebce9SSeongJae Park 	int err;
522*90bebce9SSeongJae Park 
523*90bebce9SSeongJae Park 	kbuf = user_input_str(buf, count, ppos);
524*90bebce9SSeongJae Park 	if (IS_ERR(kbuf))
525*90bebce9SSeongJae Park 		return PTR_ERR(kbuf);
526*90bebce9SSeongJae Park 
527*90bebce9SSeongJae Park 	mutex_lock(&ctx->kdamond_lock);
528*90bebce9SSeongJae Park 	if (ctx->kdamond) {
529*90bebce9SSeongJae Park 		ret = -EBUSY;
530*90bebce9SSeongJae Park 		goto unlock_out;
531*90bebce9SSeongJae Park 	}
532*90bebce9SSeongJae Park 
533*90bebce9SSeongJae Park 	err = set_init_regions(ctx, kbuf, ret);
534*90bebce9SSeongJae Park 	if (err)
535*90bebce9SSeongJae Park 		ret = err;
536*90bebce9SSeongJae Park 
537*90bebce9SSeongJae Park unlock_out:
538*90bebce9SSeongJae Park 	mutex_unlock(&ctx->kdamond_lock);
539*90bebce9SSeongJae Park 	kfree(kbuf);
540*90bebce9SSeongJae Park 	return ret;
541*90bebce9SSeongJae Park }
542*90bebce9SSeongJae Park 
543429538e8SSeongJae Park static ssize_t dbgfs_kdamond_pid_read(struct file *file,
544429538e8SSeongJae Park 		char __user *buf, size_t count, loff_t *ppos)
545429538e8SSeongJae Park {
546429538e8SSeongJae Park 	struct damon_ctx *ctx = file->private_data;
547429538e8SSeongJae Park 	char *kbuf;
548429538e8SSeongJae Park 	ssize_t len;
549429538e8SSeongJae Park 
550429538e8SSeongJae Park 	kbuf = kmalloc(count, GFP_KERNEL);
551429538e8SSeongJae Park 	if (!kbuf)
552429538e8SSeongJae Park 		return -ENOMEM;
553429538e8SSeongJae Park 
554429538e8SSeongJae Park 	mutex_lock(&ctx->kdamond_lock);
555429538e8SSeongJae Park 	if (ctx->kdamond)
556429538e8SSeongJae Park 		len = scnprintf(kbuf, count, "%d\n", ctx->kdamond->pid);
557429538e8SSeongJae Park 	else
558429538e8SSeongJae Park 		len = scnprintf(kbuf, count, "none\n");
559429538e8SSeongJae Park 	mutex_unlock(&ctx->kdamond_lock);
560429538e8SSeongJae Park 	if (!len)
561429538e8SSeongJae Park 		goto out;
562429538e8SSeongJae Park 	len = simple_read_from_buffer(buf, count, ppos, kbuf, len);
563429538e8SSeongJae Park 
564429538e8SSeongJae Park out:
565429538e8SSeongJae Park 	kfree(kbuf);
566429538e8SSeongJae Park 	return len;
567429538e8SSeongJae Park }
568429538e8SSeongJae Park 
5694bc05954SSeongJae Park static int damon_dbgfs_open(struct inode *inode, struct file *file)
5704bc05954SSeongJae Park {
5714bc05954SSeongJae Park 	file->private_data = inode->i_private;
5724bc05954SSeongJae Park 
5734bc05954SSeongJae Park 	return nonseekable_open(inode, file);
5744bc05954SSeongJae Park }
5754bc05954SSeongJae Park 
5764bc05954SSeongJae Park static const struct file_operations attrs_fops = {
5774bc05954SSeongJae Park 	.open = damon_dbgfs_open,
5784bc05954SSeongJae Park 	.read = dbgfs_attrs_read,
5794bc05954SSeongJae Park 	.write = dbgfs_attrs_write,
5804bc05954SSeongJae Park };
5814bc05954SSeongJae Park 
582af122dd8SSeongJae Park static const struct file_operations schemes_fops = {
583af122dd8SSeongJae Park 	.open = damon_dbgfs_open,
584af122dd8SSeongJae Park 	.read = dbgfs_schemes_read,
585af122dd8SSeongJae Park 	.write = dbgfs_schemes_write,
586af122dd8SSeongJae Park };
587af122dd8SSeongJae Park 
5884bc05954SSeongJae Park static const struct file_operations target_ids_fops = {
5894bc05954SSeongJae Park 	.open = damon_dbgfs_open,
5904bc05954SSeongJae Park 	.read = dbgfs_target_ids_read,
5914bc05954SSeongJae Park 	.write = dbgfs_target_ids_write,
5924bc05954SSeongJae Park };
5934bc05954SSeongJae Park 
594*90bebce9SSeongJae Park static const struct file_operations init_regions_fops = {
595*90bebce9SSeongJae Park 	.open = damon_dbgfs_open,
596*90bebce9SSeongJae Park 	.read = dbgfs_init_regions_read,
597*90bebce9SSeongJae Park 	.write = dbgfs_init_regions_write,
598*90bebce9SSeongJae Park };
599*90bebce9SSeongJae Park 
600429538e8SSeongJae Park static const struct file_operations kdamond_pid_fops = {
601429538e8SSeongJae Park 	.open = damon_dbgfs_open,
602429538e8SSeongJae Park 	.read = dbgfs_kdamond_pid_read,
603429538e8SSeongJae Park };
604429538e8SSeongJae Park 
6054bc05954SSeongJae Park static void dbgfs_fill_ctx_dir(struct dentry *dir, struct damon_ctx *ctx)
6064bc05954SSeongJae Park {
607af122dd8SSeongJae Park 	const char * const file_names[] = {"attrs", "schemes", "target_ids",
608*90bebce9SSeongJae Park 		"init_regions", "kdamond_pid"};
609af122dd8SSeongJae Park 	const struct file_operations *fops[] = {&attrs_fops, &schemes_fops,
610*90bebce9SSeongJae Park 		&target_ids_fops, &init_regions_fops, &kdamond_pid_fops};
6114bc05954SSeongJae Park 	int i;
6124bc05954SSeongJae Park 
6134bc05954SSeongJae Park 	for (i = 0; i < ARRAY_SIZE(file_names); i++)
6144bc05954SSeongJae Park 		debugfs_create_file(file_names[i], 0600, dir, ctx, fops[i]);
6154bc05954SSeongJae Park }
6164bc05954SSeongJae Park 
6174bc05954SSeongJae Park static int dbgfs_before_terminate(struct damon_ctx *ctx)
6184bc05954SSeongJae Park {
6194bc05954SSeongJae Park 	struct damon_target *t, *next;
6204bc05954SSeongJae Park 
6214bc05954SSeongJae Park 	if (!targetid_is_pid(ctx))
6224bc05954SSeongJae Park 		return 0;
6234bc05954SSeongJae Park 
6244bc05954SSeongJae Park 	damon_for_each_target_safe(t, next, ctx) {
6254bc05954SSeongJae Park 		put_pid((struct pid *)t->id);
6264bc05954SSeongJae Park 		damon_destroy_target(t);
6274bc05954SSeongJae Park 	}
6284bc05954SSeongJae Park 	return 0;
6294bc05954SSeongJae Park }
6304bc05954SSeongJae Park 
6314bc05954SSeongJae Park static struct damon_ctx *dbgfs_new_ctx(void)
6324bc05954SSeongJae Park {
6334bc05954SSeongJae Park 	struct damon_ctx *ctx;
6344bc05954SSeongJae Park 
6354bc05954SSeongJae Park 	ctx = damon_new_ctx();
6364bc05954SSeongJae Park 	if (!ctx)
6374bc05954SSeongJae Park 		return NULL;
6384bc05954SSeongJae Park 
6394bc05954SSeongJae Park 	damon_va_set_primitives(ctx);
6404bc05954SSeongJae Park 	ctx->callback.before_terminate = dbgfs_before_terminate;
6414bc05954SSeongJae Park 	return ctx;
6424bc05954SSeongJae Park }
6434bc05954SSeongJae Park 
64475c1c2b5SSeongJae Park static void dbgfs_destroy_ctx(struct damon_ctx *ctx)
64575c1c2b5SSeongJae Park {
64675c1c2b5SSeongJae Park 	damon_destroy_ctx(ctx);
64775c1c2b5SSeongJae Park }
64875c1c2b5SSeongJae Park 
64975c1c2b5SSeongJae Park /*
65075c1c2b5SSeongJae Park  * Make a context of @name and create a debugfs directory for it.
65175c1c2b5SSeongJae Park  *
65275c1c2b5SSeongJae Park  * This function should be called while holding damon_dbgfs_lock.
65375c1c2b5SSeongJae Park  *
65475c1c2b5SSeongJae Park  * Returns 0 on success, negative error code otherwise.
65575c1c2b5SSeongJae Park  */
65675c1c2b5SSeongJae Park static int dbgfs_mk_context(char *name)
65775c1c2b5SSeongJae Park {
65875c1c2b5SSeongJae Park 	struct dentry *root, **new_dirs, *new_dir;
65975c1c2b5SSeongJae Park 	struct damon_ctx **new_ctxs, *new_ctx;
66075c1c2b5SSeongJae Park 
66175c1c2b5SSeongJae Park 	if (damon_nr_running_ctxs())
66275c1c2b5SSeongJae Park 		return -EBUSY;
66375c1c2b5SSeongJae Park 
66475c1c2b5SSeongJae Park 	new_ctxs = krealloc(dbgfs_ctxs, sizeof(*dbgfs_ctxs) *
66575c1c2b5SSeongJae Park 			(dbgfs_nr_ctxs + 1), GFP_KERNEL);
66675c1c2b5SSeongJae Park 	if (!new_ctxs)
66775c1c2b5SSeongJae Park 		return -ENOMEM;
66875c1c2b5SSeongJae Park 	dbgfs_ctxs = new_ctxs;
66975c1c2b5SSeongJae Park 
67075c1c2b5SSeongJae Park 	new_dirs = krealloc(dbgfs_dirs, sizeof(*dbgfs_dirs) *
67175c1c2b5SSeongJae Park 			(dbgfs_nr_ctxs + 1), GFP_KERNEL);
67275c1c2b5SSeongJae Park 	if (!new_dirs)
67375c1c2b5SSeongJae Park 		return -ENOMEM;
67475c1c2b5SSeongJae Park 	dbgfs_dirs = new_dirs;
67575c1c2b5SSeongJae Park 
67675c1c2b5SSeongJae Park 	root = dbgfs_dirs[0];
67775c1c2b5SSeongJae Park 	if (!root)
67875c1c2b5SSeongJae Park 		return -ENOENT;
67975c1c2b5SSeongJae Park 
68075c1c2b5SSeongJae Park 	new_dir = debugfs_create_dir(name, root);
68175c1c2b5SSeongJae Park 	dbgfs_dirs[dbgfs_nr_ctxs] = new_dir;
68275c1c2b5SSeongJae Park 
68375c1c2b5SSeongJae Park 	new_ctx = dbgfs_new_ctx();
68475c1c2b5SSeongJae Park 	if (!new_ctx) {
68575c1c2b5SSeongJae Park 		debugfs_remove(new_dir);
68675c1c2b5SSeongJae Park 		dbgfs_dirs[dbgfs_nr_ctxs] = NULL;
68775c1c2b5SSeongJae Park 		return -ENOMEM;
68875c1c2b5SSeongJae Park 	}
68975c1c2b5SSeongJae Park 
69075c1c2b5SSeongJae Park 	dbgfs_ctxs[dbgfs_nr_ctxs] = new_ctx;
69175c1c2b5SSeongJae Park 	dbgfs_fill_ctx_dir(dbgfs_dirs[dbgfs_nr_ctxs],
69275c1c2b5SSeongJae Park 			dbgfs_ctxs[dbgfs_nr_ctxs]);
69375c1c2b5SSeongJae Park 	dbgfs_nr_ctxs++;
69475c1c2b5SSeongJae Park 
69575c1c2b5SSeongJae Park 	return 0;
69675c1c2b5SSeongJae Park }
69775c1c2b5SSeongJae Park 
69875c1c2b5SSeongJae Park static ssize_t dbgfs_mk_context_write(struct file *file,
69975c1c2b5SSeongJae Park 		const char __user *buf, size_t count, loff_t *ppos)
70075c1c2b5SSeongJae Park {
70175c1c2b5SSeongJae Park 	char *kbuf;
70275c1c2b5SSeongJae Park 	char *ctx_name;
70375c1c2b5SSeongJae Park 	ssize_t ret = count;
70475c1c2b5SSeongJae Park 	int err;
70575c1c2b5SSeongJae Park 
70675c1c2b5SSeongJae Park 	kbuf = user_input_str(buf, count, ppos);
70775c1c2b5SSeongJae Park 	if (IS_ERR(kbuf))
70875c1c2b5SSeongJae Park 		return PTR_ERR(kbuf);
70975c1c2b5SSeongJae Park 	ctx_name = kmalloc(count + 1, GFP_KERNEL);
71075c1c2b5SSeongJae Park 	if (!ctx_name) {
71175c1c2b5SSeongJae Park 		kfree(kbuf);
71275c1c2b5SSeongJae Park 		return -ENOMEM;
71375c1c2b5SSeongJae Park 	}
71475c1c2b5SSeongJae Park 
71575c1c2b5SSeongJae Park 	/* Trim white space */
71675c1c2b5SSeongJae Park 	if (sscanf(kbuf, "%s", ctx_name) != 1) {
71775c1c2b5SSeongJae Park 		ret = -EINVAL;
71875c1c2b5SSeongJae Park 		goto out;
71975c1c2b5SSeongJae Park 	}
72075c1c2b5SSeongJae Park 
72175c1c2b5SSeongJae Park 	mutex_lock(&damon_dbgfs_lock);
72275c1c2b5SSeongJae Park 	err = dbgfs_mk_context(ctx_name);
72375c1c2b5SSeongJae Park 	if (err)
72475c1c2b5SSeongJae Park 		ret = err;
72575c1c2b5SSeongJae Park 	mutex_unlock(&damon_dbgfs_lock);
72675c1c2b5SSeongJae Park 
72775c1c2b5SSeongJae Park out:
72875c1c2b5SSeongJae Park 	kfree(kbuf);
72975c1c2b5SSeongJae Park 	kfree(ctx_name);
73075c1c2b5SSeongJae Park 	return ret;
73175c1c2b5SSeongJae Park }
73275c1c2b5SSeongJae Park 
73375c1c2b5SSeongJae Park /*
73475c1c2b5SSeongJae Park  * Remove a context of @name and its debugfs directory.
73575c1c2b5SSeongJae Park  *
73675c1c2b5SSeongJae Park  * This function should be called while holding damon_dbgfs_lock.
73775c1c2b5SSeongJae Park  *
73875c1c2b5SSeongJae Park  * Return 0 on success, negative error code otherwise.
73975c1c2b5SSeongJae Park  */
74075c1c2b5SSeongJae Park static int dbgfs_rm_context(char *name)
74175c1c2b5SSeongJae Park {
74275c1c2b5SSeongJae Park 	struct dentry *root, *dir, **new_dirs;
74375c1c2b5SSeongJae Park 	struct damon_ctx **new_ctxs;
74475c1c2b5SSeongJae Park 	int i, j;
74575c1c2b5SSeongJae Park 
74675c1c2b5SSeongJae Park 	if (damon_nr_running_ctxs())
74775c1c2b5SSeongJae Park 		return -EBUSY;
74875c1c2b5SSeongJae Park 
74975c1c2b5SSeongJae Park 	root = dbgfs_dirs[0];
75075c1c2b5SSeongJae Park 	if (!root)
75175c1c2b5SSeongJae Park 		return -ENOENT;
75275c1c2b5SSeongJae Park 
75375c1c2b5SSeongJae Park 	dir = debugfs_lookup(name, root);
75475c1c2b5SSeongJae Park 	if (!dir)
75575c1c2b5SSeongJae Park 		return -ENOENT;
75675c1c2b5SSeongJae Park 
75775c1c2b5SSeongJae Park 	new_dirs = kmalloc_array(dbgfs_nr_ctxs - 1, sizeof(*dbgfs_dirs),
75875c1c2b5SSeongJae Park 			GFP_KERNEL);
75975c1c2b5SSeongJae Park 	if (!new_dirs)
76075c1c2b5SSeongJae Park 		return -ENOMEM;
76175c1c2b5SSeongJae Park 
76275c1c2b5SSeongJae Park 	new_ctxs = kmalloc_array(dbgfs_nr_ctxs - 1, sizeof(*dbgfs_ctxs),
76375c1c2b5SSeongJae Park 			GFP_KERNEL);
76475c1c2b5SSeongJae Park 	if (!new_ctxs) {
76575c1c2b5SSeongJae Park 		kfree(new_dirs);
76675c1c2b5SSeongJae Park 		return -ENOMEM;
76775c1c2b5SSeongJae Park 	}
76875c1c2b5SSeongJae Park 
76975c1c2b5SSeongJae Park 	for (i = 0, j = 0; i < dbgfs_nr_ctxs; i++) {
77075c1c2b5SSeongJae Park 		if (dbgfs_dirs[i] == dir) {
77175c1c2b5SSeongJae Park 			debugfs_remove(dbgfs_dirs[i]);
77275c1c2b5SSeongJae Park 			dbgfs_destroy_ctx(dbgfs_ctxs[i]);
77375c1c2b5SSeongJae Park 			continue;
77475c1c2b5SSeongJae Park 		}
77575c1c2b5SSeongJae Park 		new_dirs[j] = dbgfs_dirs[i];
77675c1c2b5SSeongJae Park 		new_ctxs[j++] = dbgfs_ctxs[i];
77775c1c2b5SSeongJae Park 	}
77875c1c2b5SSeongJae Park 
77975c1c2b5SSeongJae Park 	kfree(dbgfs_dirs);
78075c1c2b5SSeongJae Park 	kfree(dbgfs_ctxs);
78175c1c2b5SSeongJae Park 
78275c1c2b5SSeongJae Park 	dbgfs_dirs = new_dirs;
78375c1c2b5SSeongJae Park 	dbgfs_ctxs = new_ctxs;
78475c1c2b5SSeongJae Park 	dbgfs_nr_ctxs--;
78575c1c2b5SSeongJae Park 
78675c1c2b5SSeongJae Park 	return 0;
78775c1c2b5SSeongJae Park }
78875c1c2b5SSeongJae Park 
78975c1c2b5SSeongJae Park static ssize_t dbgfs_rm_context_write(struct file *file,
79075c1c2b5SSeongJae Park 		const char __user *buf, size_t count, loff_t *ppos)
79175c1c2b5SSeongJae Park {
79275c1c2b5SSeongJae Park 	char *kbuf;
79375c1c2b5SSeongJae Park 	ssize_t ret = count;
79475c1c2b5SSeongJae Park 	int err;
79575c1c2b5SSeongJae Park 	char *ctx_name;
79675c1c2b5SSeongJae Park 
79775c1c2b5SSeongJae Park 	kbuf = user_input_str(buf, count, ppos);
79875c1c2b5SSeongJae Park 	if (IS_ERR(kbuf))
79975c1c2b5SSeongJae Park 		return PTR_ERR(kbuf);
80075c1c2b5SSeongJae Park 	ctx_name = kmalloc(count + 1, GFP_KERNEL);
80175c1c2b5SSeongJae Park 	if (!ctx_name) {
80275c1c2b5SSeongJae Park 		kfree(kbuf);
80375c1c2b5SSeongJae Park 		return -ENOMEM;
80475c1c2b5SSeongJae Park 	}
80575c1c2b5SSeongJae Park 
80675c1c2b5SSeongJae Park 	/* Trim white space */
80775c1c2b5SSeongJae Park 	if (sscanf(kbuf, "%s", ctx_name) != 1) {
80875c1c2b5SSeongJae Park 		ret = -EINVAL;
80975c1c2b5SSeongJae Park 		goto out;
81075c1c2b5SSeongJae Park 	}
81175c1c2b5SSeongJae Park 
81275c1c2b5SSeongJae Park 	mutex_lock(&damon_dbgfs_lock);
81375c1c2b5SSeongJae Park 	err = dbgfs_rm_context(ctx_name);
81475c1c2b5SSeongJae Park 	if (err)
81575c1c2b5SSeongJae Park 		ret = err;
81675c1c2b5SSeongJae Park 	mutex_unlock(&damon_dbgfs_lock);
81775c1c2b5SSeongJae Park 
81875c1c2b5SSeongJae Park out:
81975c1c2b5SSeongJae Park 	kfree(kbuf);
82075c1c2b5SSeongJae Park 	kfree(ctx_name);
82175c1c2b5SSeongJae Park 	return ret;
82275c1c2b5SSeongJae Park }
82375c1c2b5SSeongJae Park 
8244bc05954SSeongJae Park static ssize_t dbgfs_monitor_on_read(struct file *file,
8254bc05954SSeongJae Park 		char __user *buf, size_t count, loff_t *ppos)
8264bc05954SSeongJae Park {
8274bc05954SSeongJae Park 	char monitor_on_buf[5];
8284bc05954SSeongJae Park 	bool monitor_on = damon_nr_running_ctxs() != 0;
8294bc05954SSeongJae Park 	int len;
8304bc05954SSeongJae Park 
8314bc05954SSeongJae Park 	len = scnprintf(monitor_on_buf, 5, monitor_on ? "on\n" : "off\n");
8324bc05954SSeongJae Park 
8334bc05954SSeongJae Park 	return simple_read_from_buffer(buf, count, ppos, monitor_on_buf, len);
8344bc05954SSeongJae Park }
8354bc05954SSeongJae Park 
8364bc05954SSeongJae Park static ssize_t dbgfs_monitor_on_write(struct file *file,
8374bc05954SSeongJae Park 		const char __user *buf, size_t count, loff_t *ppos)
8384bc05954SSeongJae Park {
8394bc05954SSeongJae Park 	ssize_t ret = count;
8404bc05954SSeongJae Park 	char *kbuf;
8414bc05954SSeongJae Park 	int err;
8424bc05954SSeongJae Park 
8434bc05954SSeongJae Park 	kbuf = user_input_str(buf, count, ppos);
8444bc05954SSeongJae Park 	if (IS_ERR(kbuf))
8454bc05954SSeongJae Park 		return PTR_ERR(kbuf);
8464bc05954SSeongJae Park 
8474bc05954SSeongJae Park 	/* Remove white space */
8484bc05954SSeongJae Park 	if (sscanf(kbuf, "%s", kbuf) != 1) {
8494bc05954SSeongJae Park 		kfree(kbuf);
8504bc05954SSeongJae Park 		return -EINVAL;
8514bc05954SSeongJae Park 	}
8524bc05954SSeongJae Park 
8534bc05954SSeongJae Park 	if (!strncmp(kbuf, "on", count))
8544bc05954SSeongJae Park 		err = damon_start(dbgfs_ctxs, dbgfs_nr_ctxs);
8554bc05954SSeongJae Park 	else if (!strncmp(kbuf, "off", count))
8564bc05954SSeongJae Park 		err = damon_stop(dbgfs_ctxs, dbgfs_nr_ctxs);
8574bc05954SSeongJae Park 	else
8584bc05954SSeongJae Park 		err = -EINVAL;
8594bc05954SSeongJae Park 
8604bc05954SSeongJae Park 	if (err)
8614bc05954SSeongJae Park 		ret = err;
8624bc05954SSeongJae Park 	kfree(kbuf);
8634bc05954SSeongJae Park 	return ret;
8644bc05954SSeongJae Park }
8654bc05954SSeongJae Park 
86675c1c2b5SSeongJae Park static const struct file_operations mk_contexts_fops = {
86775c1c2b5SSeongJae Park 	.write = dbgfs_mk_context_write,
86875c1c2b5SSeongJae Park };
86975c1c2b5SSeongJae Park 
87075c1c2b5SSeongJae Park static const struct file_operations rm_contexts_fops = {
87175c1c2b5SSeongJae Park 	.write = dbgfs_rm_context_write,
87275c1c2b5SSeongJae Park };
87375c1c2b5SSeongJae Park 
8744bc05954SSeongJae Park static const struct file_operations monitor_on_fops = {
8754bc05954SSeongJae Park 	.read = dbgfs_monitor_on_read,
8764bc05954SSeongJae Park 	.write = dbgfs_monitor_on_write,
8774bc05954SSeongJae Park };
8784bc05954SSeongJae Park 
8794bc05954SSeongJae Park static int __init __damon_dbgfs_init(void)
8804bc05954SSeongJae Park {
8814bc05954SSeongJae Park 	struct dentry *dbgfs_root;
88275c1c2b5SSeongJae Park 	const char * const file_names[] = {"mk_contexts", "rm_contexts",
88375c1c2b5SSeongJae Park 		"monitor_on"};
88475c1c2b5SSeongJae Park 	const struct file_operations *fops[] = {&mk_contexts_fops,
88575c1c2b5SSeongJae Park 		&rm_contexts_fops, &monitor_on_fops};
8864bc05954SSeongJae Park 	int i;
8874bc05954SSeongJae Park 
8884bc05954SSeongJae Park 	dbgfs_root = debugfs_create_dir("damon", NULL);
8894bc05954SSeongJae Park 
8904bc05954SSeongJae Park 	for (i = 0; i < ARRAY_SIZE(file_names); i++)
8914bc05954SSeongJae Park 		debugfs_create_file(file_names[i], 0600, dbgfs_root, NULL,
8924bc05954SSeongJae Park 				fops[i]);
8934bc05954SSeongJae Park 	dbgfs_fill_ctx_dir(dbgfs_root, dbgfs_ctxs[0]);
8944bc05954SSeongJae Park 
8954bc05954SSeongJae Park 	dbgfs_dirs = kmalloc_array(1, sizeof(dbgfs_root), GFP_KERNEL);
8964bc05954SSeongJae Park 	if (!dbgfs_dirs) {
8974bc05954SSeongJae Park 		debugfs_remove(dbgfs_root);
8984bc05954SSeongJae Park 		return -ENOMEM;
8994bc05954SSeongJae Park 	}
9004bc05954SSeongJae Park 	dbgfs_dirs[0] = dbgfs_root;
9014bc05954SSeongJae Park 
9024bc05954SSeongJae Park 	return 0;
9034bc05954SSeongJae Park }
9044bc05954SSeongJae Park 
9054bc05954SSeongJae Park /*
9064bc05954SSeongJae Park  * Functions for the initialization
9074bc05954SSeongJae Park  */
9084bc05954SSeongJae Park 
9094bc05954SSeongJae Park static int __init damon_dbgfs_init(void)
9104bc05954SSeongJae Park {
9114bc05954SSeongJae Park 	int rc;
9124bc05954SSeongJae Park 
9134bc05954SSeongJae Park 	dbgfs_ctxs = kmalloc(sizeof(*dbgfs_ctxs), GFP_KERNEL);
9144bc05954SSeongJae Park 	if (!dbgfs_ctxs)
9154bc05954SSeongJae Park 		return -ENOMEM;
9164bc05954SSeongJae Park 	dbgfs_ctxs[0] = dbgfs_new_ctx();
9174bc05954SSeongJae Park 	if (!dbgfs_ctxs[0]) {
9184bc05954SSeongJae Park 		kfree(dbgfs_ctxs);
9194bc05954SSeongJae Park 		return -ENOMEM;
9204bc05954SSeongJae Park 	}
9214bc05954SSeongJae Park 	dbgfs_nr_ctxs = 1;
9224bc05954SSeongJae Park 
9234bc05954SSeongJae Park 	rc = __damon_dbgfs_init();
9244bc05954SSeongJae Park 	if (rc) {
9254bc05954SSeongJae Park 		kfree(dbgfs_ctxs[0]);
9264bc05954SSeongJae Park 		kfree(dbgfs_ctxs);
9274bc05954SSeongJae Park 		pr_err("%s: dbgfs init failed\n", __func__);
9284bc05954SSeongJae Park 	}
9294bc05954SSeongJae Park 
9304bc05954SSeongJae Park 	return rc;
9314bc05954SSeongJae Park }
9324bc05954SSeongJae Park 
9334bc05954SSeongJae Park module_init(damon_dbgfs_init);
93417ccae8bSSeongJae Park 
93517ccae8bSSeongJae Park #include "dbgfs-test.h"
936