1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * DAMON Debugfs Interface
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Author: SeongJae Park <sjpark@amazon.de>
6*4882a593Smuzhiyun */
7*4882a593Smuzhiyun
8*4882a593Smuzhiyun #define pr_fmt(fmt) "damon-dbgfs: " fmt
9*4882a593Smuzhiyun
10*4882a593Smuzhiyun #include <linux/damon.h>
11*4882a593Smuzhiyun #include <linux/debugfs.h>
12*4882a593Smuzhiyun #include <linux/file.h>
13*4882a593Smuzhiyun #include <linux/mm.h>
14*4882a593Smuzhiyun #include <linux/module.h>
15*4882a593Smuzhiyun #include <linux/page_idle.h>
16*4882a593Smuzhiyun #include <linux/slab.h>
17*4882a593Smuzhiyun
18*4882a593Smuzhiyun static struct damon_ctx **dbgfs_ctxs;
19*4882a593Smuzhiyun static int dbgfs_nr_ctxs;
20*4882a593Smuzhiyun static struct dentry **dbgfs_dirs;
21*4882a593Smuzhiyun static DEFINE_MUTEX(damon_dbgfs_lock);
22*4882a593Smuzhiyun
23*4882a593Smuzhiyun /*
24*4882a593Smuzhiyun * Returns non-empty string on success, negative error code otherwise.
25*4882a593Smuzhiyun */
user_input_str(const char __user * buf,size_t count,loff_t * ppos)26*4882a593Smuzhiyun static char *user_input_str(const char __user *buf, size_t count, loff_t *ppos)
27*4882a593Smuzhiyun {
28*4882a593Smuzhiyun char *kbuf;
29*4882a593Smuzhiyun ssize_t ret;
30*4882a593Smuzhiyun
31*4882a593Smuzhiyun /* We do not accept continuous write */
32*4882a593Smuzhiyun if (*ppos)
33*4882a593Smuzhiyun return ERR_PTR(-EINVAL);
34*4882a593Smuzhiyun
35*4882a593Smuzhiyun kbuf = kmalloc(count + 1, GFP_KERNEL | __GFP_NOWARN);
36*4882a593Smuzhiyun if (!kbuf)
37*4882a593Smuzhiyun return ERR_PTR(-ENOMEM);
38*4882a593Smuzhiyun
39*4882a593Smuzhiyun ret = simple_write_to_buffer(kbuf, count + 1, ppos, buf, count);
40*4882a593Smuzhiyun if (ret != count) {
41*4882a593Smuzhiyun kfree(kbuf);
42*4882a593Smuzhiyun return ERR_PTR(-EIO);
43*4882a593Smuzhiyun }
44*4882a593Smuzhiyun kbuf[ret] = '\0';
45*4882a593Smuzhiyun
46*4882a593Smuzhiyun return kbuf;
47*4882a593Smuzhiyun }
48*4882a593Smuzhiyun
dbgfs_attrs_read(struct file * file,char __user * buf,size_t count,loff_t * ppos)49*4882a593Smuzhiyun static ssize_t dbgfs_attrs_read(struct file *file,
50*4882a593Smuzhiyun char __user *buf, size_t count, loff_t *ppos)
51*4882a593Smuzhiyun {
52*4882a593Smuzhiyun struct damon_ctx *ctx = file->private_data;
53*4882a593Smuzhiyun char kbuf[128];
54*4882a593Smuzhiyun int ret;
55*4882a593Smuzhiyun
56*4882a593Smuzhiyun mutex_lock(&ctx->kdamond_lock);
57*4882a593Smuzhiyun ret = scnprintf(kbuf, ARRAY_SIZE(kbuf), "%lu %lu %lu %lu %lu\n",
58*4882a593Smuzhiyun ctx->sample_interval, ctx->aggr_interval,
59*4882a593Smuzhiyun ctx->primitive_update_interval, ctx->min_nr_regions,
60*4882a593Smuzhiyun ctx->max_nr_regions);
61*4882a593Smuzhiyun mutex_unlock(&ctx->kdamond_lock);
62*4882a593Smuzhiyun
63*4882a593Smuzhiyun return simple_read_from_buffer(buf, count, ppos, kbuf, ret);
64*4882a593Smuzhiyun }
65*4882a593Smuzhiyun
dbgfs_attrs_write(struct file * file,const char __user * buf,size_t count,loff_t * ppos)66*4882a593Smuzhiyun static ssize_t dbgfs_attrs_write(struct file *file,
67*4882a593Smuzhiyun const char __user *buf, size_t count, loff_t *ppos)
68*4882a593Smuzhiyun {
69*4882a593Smuzhiyun struct damon_ctx *ctx = file->private_data;
70*4882a593Smuzhiyun unsigned long s, a, r, minr, maxr;
71*4882a593Smuzhiyun char *kbuf;
72*4882a593Smuzhiyun ssize_t ret;
73*4882a593Smuzhiyun
74*4882a593Smuzhiyun kbuf = user_input_str(buf, count, ppos);
75*4882a593Smuzhiyun if (IS_ERR(kbuf))
76*4882a593Smuzhiyun return PTR_ERR(kbuf);
77*4882a593Smuzhiyun
78*4882a593Smuzhiyun if (sscanf(kbuf, "%lu %lu %lu %lu %lu",
79*4882a593Smuzhiyun &s, &a, &r, &minr, &maxr) != 5) {
80*4882a593Smuzhiyun ret = -EINVAL;
81*4882a593Smuzhiyun goto out;
82*4882a593Smuzhiyun }
83*4882a593Smuzhiyun
84*4882a593Smuzhiyun mutex_lock(&ctx->kdamond_lock);
85*4882a593Smuzhiyun if (ctx->kdamond) {
86*4882a593Smuzhiyun ret = -EBUSY;
87*4882a593Smuzhiyun goto unlock_out;
88*4882a593Smuzhiyun }
89*4882a593Smuzhiyun
90*4882a593Smuzhiyun ret = damon_set_attrs(ctx, s, a, r, minr, maxr);
91*4882a593Smuzhiyun if (!ret)
92*4882a593Smuzhiyun ret = count;
93*4882a593Smuzhiyun unlock_out:
94*4882a593Smuzhiyun mutex_unlock(&ctx->kdamond_lock);
95*4882a593Smuzhiyun out:
96*4882a593Smuzhiyun kfree(kbuf);
97*4882a593Smuzhiyun return ret;
98*4882a593Smuzhiyun }
99*4882a593Smuzhiyun
sprint_schemes(struct damon_ctx * c,char * buf,ssize_t len)100*4882a593Smuzhiyun static ssize_t sprint_schemes(struct damon_ctx *c, char *buf, ssize_t len)
101*4882a593Smuzhiyun {
102*4882a593Smuzhiyun struct damos *s;
103*4882a593Smuzhiyun int written = 0;
104*4882a593Smuzhiyun int rc;
105*4882a593Smuzhiyun
106*4882a593Smuzhiyun damon_for_each_scheme(s, c) {
107*4882a593Smuzhiyun rc = scnprintf(&buf[written], len - written,
108*4882a593Smuzhiyun "%lu %lu %u %u %u %u %d %lu %lu %lu %u %u %u %d %lu %lu %lu %lu %lu %lu %lu %lu %lu\n",
109*4882a593Smuzhiyun s->min_sz_region, s->max_sz_region,
110*4882a593Smuzhiyun s->min_nr_accesses, s->max_nr_accesses,
111*4882a593Smuzhiyun s->min_age_region, s->max_age_region,
112*4882a593Smuzhiyun s->action,
113*4882a593Smuzhiyun s->quota.ms, s->quota.sz,
114*4882a593Smuzhiyun s->quota.reset_interval,
115*4882a593Smuzhiyun s->quota.weight_sz,
116*4882a593Smuzhiyun s->quota.weight_nr_accesses,
117*4882a593Smuzhiyun s->quota.weight_age,
118*4882a593Smuzhiyun s->wmarks.metric, s->wmarks.interval,
119*4882a593Smuzhiyun s->wmarks.high, s->wmarks.mid, s->wmarks.low,
120*4882a593Smuzhiyun s->stat.nr_tried, s->stat.sz_tried,
121*4882a593Smuzhiyun s->stat.nr_applied, s->stat.sz_applied,
122*4882a593Smuzhiyun s->stat.qt_exceeds);
123*4882a593Smuzhiyun if (!rc)
124*4882a593Smuzhiyun return -ENOMEM;
125*4882a593Smuzhiyun
126*4882a593Smuzhiyun written += rc;
127*4882a593Smuzhiyun }
128*4882a593Smuzhiyun return written;
129*4882a593Smuzhiyun }
130*4882a593Smuzhiyun
dbgfs_schemes_read(struct file * file,char __user * buf,size_t count,loff_t * ppos)131*4882a593Smuzhiyun static ssize_t dbgfs_schemes_read(struct file *file, char __user *buf,
132*4882a593Smuzhiyun size_t count, loff_t *ppos)
133*4882a593Smuzhiyun {
134*4882a593Smuzhiyun struct damon_ctx *ctx = file->private_data;
135*4882a593Smuzhiyun char *kbuf;
136*4882a593Smuzhiyun ssize_t len;
137*4882a593Smuzhiyun
138*4882a593Smuzhiyun kbuf = kmalloc(count, GFP_KERNEL | __GFP_NOWARN);
139*4882a593Smuzhiyun if (!kbuf)
140*4882a593Smuzhiyun return -ENOMEM;
141*4882a593Smuzhiyun
142*4882a593Smuzhiyun mutex_lock(&ctx->kdamond_lock);
143*4882a593Smuzhiyun len = sprint_schemes(ctx, kbuf, count);
144*4882a593Smuzhiyun mutex_unlock(&ctx->kdamond_lock);
145*4882a593Smuzhiyun if (len < 0)
146*4882a593Smuzhiyun goto out;
147*4882a593Smuzhiyun len = simple_read_from_buffer(buf, count, ppos, kbuf, len);
148*4882a593Smuzhiyun
149*4882a593Smuzhiyun out:
150*4882a593Smuzhiyun kfree(kbuf);
151*4882a593Smuzhiyun return len;
152*4882a593Smuzhiyun }
153*4882a593Smuzhiyun
free_schemes_arr(struct damos ** schemes,ssize_t nr_schemes)154*4882a593Smuzhiyun static void free_schemes_arr(struct damos **schemes, ssize_t nr_schemes)
155*4882a593Smuzhiyun {
156*4882a593Smuzhiyun ssize_t i;
157*4882a593Smuzhiyun
158*4882a593Smuzhiyun for (i = 0; i < nr_schemes; i++)
159*4882a593Smuzhiyun kfree(schemes[i]);
160*4882a593Smuzhiyun kfree(schemes);
161*4882a593Smuzhiyun }
162*4882a593Smuzhiyun
damos_action_valid(int action)163*4882a593Smuzhiyun static bool damos_action_valid(int action)
164*4882a593Smuzhiyun {
165*4882a593Smuzhiyun switch (action) {
166*4882a593Smuzhiyun case DAMOS_WILLNEED:
167*4882a593Smuzhiyun case DAMOS_COLD:
168*4882a593Smuzhiyun case DAMOS_PAGEOUT:
169*4882a593Smuzhiyun case DAMOS_HUGEPAGE:
170*4882a593Smuzhiyun case DAMOS_NOHUGEPAGE:
171*4882a593Smuzhiyun case DAMOS_STAT:
172*4882a593Smuzhiyun return true;
173*4882a593Smuzhiyun default:
174*4882a593Smuzhiyun return false;
175*4882a593Smuzhiyun }
176*4882a593Smuzhiyun }
177*4882a593Smuzhiyun
178*4882a593Smuzhiyun /*
179*4882a593Smuzhiyun * Converts a string into an array of struct damos pointers
180*4882a593Smuzhiyun *
181*4882a593Smuzhiyun * Returns an array of struct damos pointers that converted if the conversion
182*4882a593Smuzhiyun * success, or NULL otherwise.
183*4882a593Smuzhiyun */
str_to_schemes(const char * str,ssize_t len,ssize_t * nr_schemes)184*4882a593Smuzhiyun static struct damos **str_to_schemes(const char *str, ssize_t len,
185*4882a593Smuzhiyun ssize_t *nr_schemes)
186*4882a593Smuzhiyun {
187*4882a593Smuzhiyun struct damos *scheme, **schemes;
188*4882a593Smuzhiyun const int max_nr_schemes = 256;
189*4882a593Smuzhiyun int pos = 0, parsed, ret;
190*4882a593Smuzhiyun unsigned long min_sz, max_sz;
191*4882a593Smuzhiyun unsigned int min_nr_a, max_nr_a, min_age, max_age;
192*4882a593Smuzhiyun unsigned int action;
193*4882a593Smuzhiyun
194*4882a593Smuzhiyun schemes = kmalloc_array(max_nr_schemes, sizeof(scheme),
195*4882a593Smuzhiyun GFP_KERNEL);
196*4882a593Smuzhiyun if (!schemes)
197*4882a593Smuzhiyun return NULL;
198*4882a593Smuzhiyun
199*4882a593Smuzhiyun *nr_schemes = 0;
200*4882a593Smuzhiyun while (pos < len && *nr_schemes < max_nr_schemes) {
201*4882a593Smuzhiyun struct damos_quota quota = {};
202*4882a593Smuzhiyun struct damos_watermarks wmarks;
203*4882a593Smuzhiyun
204*4882a593Smuzhiyun ret = sscanf(&str[pos],
205*4882a593Smuzhiyun "%lu %lu %u %u %u %u %u %lu %lu %lu %u %u %u %u %lu %lu %lu %lu%n",
206*4882a593Smuzhiyun &min_sz, &max_sz, &min_nr_a, &max_nr_a,
207*4882a593Smuzhiyun &min_age, &max_age, &action, "a.ms,
208*4882a593Smuzhiyun "a.sz, "a.reset_interval,
209*4882a593Smuzhiyun "a.weight_sz, "a.weight_nr_accesses,
210*4882a593Smuzhiyun "a.weight_age, &wmarks.metric,
211*4882a593Smuzhiyun &wmarks.interval, &wmarks.high, &wmarks.mid,
212*4882a593Smuzhiyun &wmarks.low, &parsed);
213*4882a593Smuzhiyun if (ret != 18)
214*4882a593Smuzhiyun break;
215*4882a593Smuzhiyun if (!damos_action_valid(action))
216*4882a593Smuzhiyun goto fail;
217*4882a593Smuzhiyun
218*4882a593Smuzhiyun if (min_sz > max_sz || min_nr_a > max_nr_a || min_age > max_age)
219*4882a593Smuzhiyun goto fail;
220*4882a593Smuzhiyun
221*4882a593Smuzhiyun if (wmarks.high < wmarks.mid || wmarks.high < wmarks.low ||
222*4882a593Smuzhiyun wmarks.mid < wmarks.low)
223*4882a593Smuzhiyun goto fail;
224*4882a593Smuzhiyun
225*4882a593Smuzhiyun pos += parsed;
226*4882a593Smuzhiyun scheme = damon_new_scheme(min_sz, max_sz, min_nr_a, max_nr_a,
227*4882a593Smuzhiyun min_age, max_age, action, "a, &wmarks);
228*4882a593Smuzhiyun if (!scheme)
229*4882a593Smuzhiyun goto fail;
230*4882a593Smuzhiyun
231*4882a593Smuzhiyun schemes[*nr_schemes] = scheme;
232*4882a593Smuzhiyun *nr_schemes += 1;
233*4882a593Smuzhiyun }
234*4882a593Smuzhiyun return schemes;
235*4882a593Smuzhiyun fail:
236*4882a593Smuzhiyun free_schemes_arr(schemes, *nr_schemes);
237*4882a593Smuzhiyun return NULL;
238*4882a593Smuzhiyun }
239*4882a593Smuzhiyun
dbgfs_schemes_write(struct file * file,const char __user * buf,size_t count,loff_t * ppos)240*4882a593Smuzhiyun static ssize_t dbgfs_schemes_write(struct file *file, const char __user *buf,
241*4882a593Smuzhiyun size_t count, loff_t *ppos)
242*4882a593Smuzhiyun {
243*4882a593Smuzhiyun struct damon_ctx *ctx = file->private_data;
244*4882a593Smuzhiyun char *kbuf;
245*4882a593Smuzhiyun struct damos **schemes;
246*4882a593Smuzhiyun ssize_t nr_schemes = 0, ret;
247*4882a593Smuzhiyun
248*4882a593Smuzhiyun kbuf = user_input_str(buf, count, ppos);
249*4882a593Smuzhiyun if (IS_ERR(kbuf))
250*4882a593Smuzhiyun return PTR_ERR(kbuf);
251*4882a593Smuzhiyun
252*4882a593Smuzhiyun schemes = str_to_schemes(kbuf, count, &nr_schemes);
253*4882a593Smuzhiyun if (!schemes) {
254*4882a593Smuzhiyun ret = -EINVAL;
255*4882a593Smuzhiyun goto out;
256*4882a593Smuzhiyun }
257*4882a593Smuzhiyun
258*4882a593Smuzhiyun mutex_lock(&ctx->kdamond_lock);
259*4882a593Smuzhiyun if (ctx->kdamond) {
260*4882a593Smuzhiyun ret = -EBUSY;
261*4882a593Smuzhiyun goto unlock_out;
262*4882a593Smuzhiyun }
263*4882a593Smuzhiyun
264*4882a593Smuzhiyun ret = damon_set_schemes(ctx, schemes, nr_schemes);
265*4882a593Smuzhiyun if (!ret) {
266*4882a593Smuzhiyun ret = count;
267*4882a593Smuzhiyun nr_schemes = 0;
268*4882a593Smuzhiyun }
269*4882a593Smuzhiyun
270*4882a593Smuzhiyun unlock_out:
271*4882a593Smuzhiyun mutex_unlock(&ctx->kdamond_lock);
272*4882a593Smuzhiyun free_schemes_arr(schemes, nr_schemes);
273*4882a593Smuzhiyun out:
274*4882a593Smuzhiyun kfree(kbuf);
275*4882a593Smuzhiyun return ret;
276*4882a593Smuzhiyun }
277*4882a593Smuzhiyun
targetid_is_pid(const struct damon_ctx * ctx)278*4882a593Smuzhiyun static inline bool targetid_is_pid(const struct damon_ctx *ctx)
279*4882a593Smuzhiyun {
280*4882a593Smuzhiyun return ctx->primitive.target_valid == damon_va_target_valid;
281*4882a593Smuzhiyun }
282*4882a593Smuzhiyun
sprint_target_ids(struct damon_ctx * ctx,char * buf,ssize_t len)283*4882a593Smuzhiyun static ssize_t sprint_target_ids(struct damon_ctx *ctx, char *buf, ssize_t len)
284*4882a593Smuzhiyun {
285*4882a593Smuzhiyun struct damon_target *t;
286*4882a593Smuzhiyun unsigned long id;
287*4882a593Smuzhiyun int written = 0;
288*4882a593Smuzhiyun int rc;
289*4882a593Smuzhiyun
290*4882a593Smuzhiyun damon_for_each_target(t, ctx) {
291*4882a593Smuzhiyun id = t->id;
292*4882a593Smuzhiyun if (targetid_is_pid(ctx))
293*4882a593Smuzhiyun /* Show pid numbers to debugfs users */
294*4882a593Smuzhiyun id = (unsigned long)pid_vnr((struct pid *)id);
295*4882a593Smuzhiyun
296*4882a593Smuzhiyun rc = scnprintf(&buf[written], len - written, "%lu ", id);
297*4882a593Smuzhiyun if (!rc)
298*4882a593Smuzhiyun return -ENOMEM;
299*4882a593Smuzhiyun written += rc;
300*4882a593Smuzhiyun }
301*4882a593Smuzhiyun if (written)
302*4882a593Smuzhiyun written -= 1;
303*4882a593Smuzhiyun written += scnprintf(&buf[written], len - written, "\n");
304*4882a593Smuzhiyun return written;
305*4882a593Smuzhiyun }
306*4882a593Smuzhiyun
dbgfs_target_ids_read(struct file * file,char __user * buf,size_t count,loff_t * ppos)307*4882a593Smuzhiyun static ssize_t dbgfs_target_ids_read(struct file *file,
308*4882a593Smuzhiyun char __user *buf, size_t count, loff_t *ppos)
309*4882a593Smuzhiyun {
310*4882a593Smuzhiyun struct damon_ctx *ctx = file->private_data;
311*4882a593Smuzhiyun ssize_t len;
312*4882a593Smuzhiyun char ids_buf[320];
313*4882a593Smuzhiyun
314*4882a593Smuzhiyun mutex_lock(&ctx->kdamond_lock);
315*4882a593Smuzhiyun len = sprint_target_ids(ctx, ids_buf, 320);
316*4882a593Smuzhiyun mutex_unlock(&ctx->kdamond_lock);
317*4882a593Smuzhiyun if (len < 0)
318*4882a593Smuzhiyun return len;
319*4882a593Smuzhiyun
320*4882a593Smuzhiyun return simple_read_from_buffer(buf, count, ppos, ids_buf, len);
321*4882a593Smuzhiyun }
322*4882a593Smuzhiyun
323*4882a593Smuzhiyun /*
324*4882a593Smuzhiyun * Converts a string into an array of unsigned long integers
325*4882a593Smuzhiyun *
326*4882a593Smuzhiyun * Returns an array of unsigned long integers if the conversion success, or
327*4882a593Smuzhiyun * NULL otherwise.
328*4882a593Smuzhiyun */
str_to_target_ids(const char * str,ssize_t len,ssize_t * nr_ids)329*4882a593Smuzhiyun static unsigned long *str_to_target_ids(const char *str, ssize_t len,
330*4882a593Smuzhiyun ssize_t *nr_ids)
331*4882a593Smuzhiyun {
332*4882a593Smuzhiyun unsigned long *ids;
333*4882a593Smuzhiyun const int max_nr_ids = 32;
334*4882a593Smuzhiyun unsigned long id;
335*4882a593Smuzhiyun int pos = 0, parsed, ret;
336*4882a593Smuzhiyun
337*4882a593Smuzhiyun *nr_ids = 0;
338*4882a593Smuzhiyun ids = kmalloc_array(max_nr_ids, sizeof(id), GFP_KERNEL);
339*4882a593Smuzhiyun if (!ids)
340*4882a593Smuzhiyun return NULL;
341*4882a593Smuzhiyun while (*nr_ids < max_nr_ids && pos < len) {
342*4882a593Smuzhiyun ret = sscanf(&str[pos], "%lu%n", &id, &parsed);
343*4882a593Smuzhiyun pos += parsed;
344*4882a593Smuzhiyun if (ret != 1)
345*4882a593Smuzhiyun break;
346*4882a593Smuzhiyun ids[*nr_ids] = id;
347*4882a593Smuzhiyun *nr_ids += 1;
348*4882a593Smuzhiyun }
349*4882a593Smuzhiyun
350*4882a593Smuzhiyun return ids;
351*4882a593Smuzhiyun }
352*4882a593Smuzhiyun
dbgfs_put_pids(unsigned long * ids,int nr_ids)353*4882a593Smuzhiyun static void dbgfs_put_pids(unsigned long *ids, int nr_ids)
354*4882a593Smuzhiyun {
355*4882a593Smuzhiyun int i;
356*4882a593Smuzhiyun
357*4882a593Smuzhiyun for (i = 0; i < nr_ids; i++)
358*4882a593Smuzhiyun put_pid((struct pid *)ids[i]);
359*4882a593Smuzhiyun }
360*4882a593Smuzhiyun
dbgfs_target_ids_write(struct file * file,const char __user * buf,size_t count,loff_t * ppos)361*4882a593Smuzhiyun static ssize_t dbgfs_target_ids_write(struct file *file,
362*4882a593Smuzhiyun const char __user *buf, size_t count, loff_t *ppos)
363*4882a593Smuzhiyun {
364*4882a593Smuzhiyun struct damon_ctx *ctx = file->private_data;
365*4882a593Smuzhiyun struct damon_target *t, *next_t;
366*4882a593Smuzhiyun bool id_is_pid = true;
367*4882a593Smuzhiyun char *kbuf;
368*4882a593Smuzhiyun unsigned long *targets;
369*4882a593Smuzhiyun ssize_t nr_targets;
370*4882a593Smuzhiyun ssize_t ret;
371*4882a593Smuzhiyun int i;
372*4882a593Smuzhiyun
373*4882a593Smuzhiyun kbuf = user_input_str(buf, count, ppos);
374*4882a593Smuzhiyun if (IS_ERR(kbuf))
375*4882a593Smuzhiyun return PTR_ERR(kbuf);
376*4882a593Smuzhiyun
377*4882a593Smuzhiyun if (!strncmp(kbuf, "paddr\n", count)) {
378*4882a593Smuzhiyun id_is_pid = false;
379*4882a593Smuzhiyun /* target id is meaningless here, but we set it just for fun */
380*4882a593Smuzhiyun scnprintf(kbuf, count, "42 ");
381*4882a593Smuzhiyun }
382*4882a593Smuzhiyun
383*4882a593Smuzhiyun targets = str_to_target_ids(kbuf, count, &nr_targets);
384*4882a593Smuzhiyun if (!targets) {
385*4882a593Smuzhiyun ret = -ENOMEM;
386*4882a593Smuzhiyun goto out;
387*4882a593Smuzhiyun }
388*4882a593Smuzhiyun
389*4882a593Smuzhiyun if (id_is_pid) {
390*4882a593Smuzhiyun for (i = 0; i < nr_targets; i++) {
391*4882a593Smuzhiyun targets[i] = (unsigned long)find_get_pid(
392*4882a593Smuzhiyun (int)targets[i]);
393*4882a593Smuzhiyun if (!targets[i]) {
394*4882a593Smuzhiyun dbgfs_put_pids(targets, i);
395*4882a593Smuzhiyun ret = -EINVAL;
396*4882a593Smuzhiyun goto free_targets_out;
397*4882a593Smuzhiyun }
398*4882a593Smuzhiyun }
399*4882a593Smuzhiyun }
400*4882a593Smuzhiyun
401*4882a593Smuzhiyun mutex_lock(&ctx->kdamond_lock);
402*4882a593Smuzhiyun if (ctx->kdamond) {
403*4882a593Smuzhiyun if (id_is_pid)
404*4882a593Smuzhiyun dbgfs_put_pids(targets, nr_targets);
405*4882a593Smuzhiyun ret = -EBUSY;
406*4882a593Smuzhiyun goto unlock_out;
407*4882a593Smuzhiyun }
408*4882a593Smuzhiyun
409*4882a593Smuzhiyun /* remove previously set targets */
410*4882a593Smuzhiyun damon_for_each_target_safe(t, next_t, ctx) {
411*4882a593Smuzhiyun if (targetid_is_pid(ctx))
412*4882a593Smuzhiyun put_pid((struct pid *)t->id);
413*4882a593Smuzhiyun damon_destroy_target(t);
414*4882a593Smuzhiyun }
415*4882a593Smuzhiyun
416*4882a593Smuzhiyun /* Configure the context for the address space type */
417*4882a593Smuzhiyun if (id_is_pid)
418*4882a593Smuzhiyun damon_va_set_primitives(ctx);
419*4882a593Smuzhiyun else
420*4882a593Smuzhiyun damon_pa_set_primitives(ctx);
421*4882a593Smuzhiyun
422*4882a593Smuzhiyun ret = damon_set_targets(ctx, targets, nr_targets);
423*4882a593Smuzhiyun if (ret) {
424*4882a593Smuzhiyun if (id_is_pid)
425*4882a593Smuzhiyun dbgfs_put_pids(targets, nr_targets);
426*4882a593Smuzhiyun } else {
427*4882a593Smuzhiyun ret = count;
428*4882a593Smuzhiyun }
429*4882a593Smuzhiyun
430*4882a593Smuzhiyun unlock_out:
431*4882a593Smuzhiyun mutex_unlock(&ctx->kdamond_lock);
432*4882a593Smuzhiyun free_targets_out:
433*4882a593Smuzhiyun kfree(targets);
434*4882a593Smuzhiyun out:
435*4882a593Smuzhiyun kfree(kbuf);
436*4882a593Smuzhiyun return ret;
437*4882a593Smuzhiyun }
438*4882a593Smuzhiyun
sprint_init_regions(struct damon_ctx * c,char * buf,ssize_t len)439*4882a593Smuzhiyun static ssize_t sprint_init_regions(struct damon_ctx *c, char *buf, ssize_t len)
440*4882a593Smuzhiyun {
441*4882a593Smuzhiyun struct damon_target *t;
442*4882a593Smuzhiyun struct damon_region *r;
443*4882a593Smuzhiyun int written = 0;
444*4882a593Smuzhiyun int rc;
445*4882a593Smuzhiyun
446*4882a593Smuzhiyun damon_for_each_target(t, c) {
447*4882a593Smuzhiyun damon_for_each_region(r, t) {
448*4882a593Smuzhiyun rc = scnprintf(&buf[written], len - written,
449*4882a593Smuzhiyun "%lu %lu %lu\n",
450*4882a593Smuzhiyun t->id, r->ar.start, r->ar.end);
451*4882a593Smuzhiyun if (!rc)
452*4882a593Smuzhiyun return -ENOMEM;
453*4882a593Smuzhiyun written += rc;
454*4882a593Smuzhiyun }
455*4882a593Smuzhiyun }
456*4882a593Smuzhiyun return written;
457*4882a593Smuzhiyun }
458*4882a593Smuzhiyun
dbgfs_init_regions_read(struct file * file,char __user * buf,size_t count,loff_t * ppos)459*4882a593Smuzhiyun static ssize_t dbgfs_init_regions_read(struct file *file, char __user *buf,
460*4882a593Smuzhiyun size_t count, loff_t *ppos)
461*4882a593Smuzhiyun {
462*4882a593Smuzhiyun struct damon_ctx *ctx = file->private_data;
463*4882a593Smuzhiyun char *kbuf;
464*4882a593Smuzhiyun ssize_t len;
465*4882a593Smuzhiyun
466*4882a593Smuzhiyun kbuf = kmalloc(count, GFP_KERNEL | __GFP_NOWARN);
467*4882a593Smuzhiyun if (!kbuf)
468*4882a593Smuzhiyun return -ENOMEM;
469*4882a593Smuzhiyun
470*4882a593Smuzhiyun mutex_lock(&ctx->kdamond_lock);
471*4882a593Smuzhiyun if (ctx->kdamond) {
472*4882a593Smuzhiyun mutex_unlock(&ctx->kdamond_lock);
473*4882a593Smuzhiyun len = -EBUSY;
474*4882a593Smuzhiyun goto out;
475*4882a593Smuzhiyun }
476*4882a593Smuzhiyun
477*4882a593Smuzhiyun len = sprint_init_regions(ctx, kbuf, count);
478*4882a593Smuzhiyun mutex_unlock(&ctx->kdamond_lock);
479*4882a593Smuzhiyun if (len < 0)
480*4882a593Smuzhiyun goto out;
481*4882a593Smuzhiyun len = simple_read_from_buffer(buf, count, ppos, kbuf, len);
482*4882a593Smuzhiyun
483*4882a593Smuzhiyun out:
484*4882a593Smuzhiyun kfree(kbuf);
485*4882a593Smuzhiyun return len;
486*4882a593Smuzhiyun }
487*4882a593Smuzhiyun
add_init_region(struct damon_ctx * c,unsigned long target_id,struct damon_addr_range * ar)488*4882a593Smuzhiyun static int add_init_region(struct damon_ctx *c,
489*4882a593Smuzhiyun unsigned long target_id, struct damon_addr_range *ar)
490*4882a593Smuzhiyun {
491*4882a593Smuzhiyun struct damon_target *t;
492*4882a593Smuzhiyun struct damon_region *r, *prev;
493*4882a593Smuzhiyun unsigned long id;
494*4882a593Smuzhiyun int rc = -EINVAL;
495*4882a593Smuzhiyun
496*4882a593Smuzhiyun if (ar->start >= ar->end)
497*4882a593Smuzhiyun return -EINVAL;
498*4882a593Smuzhiyun
499*4882a593Smuzhiyun damon_for_each_target(t, c) {
500*4882a593Smuzhiyun id = t->id;
501*4882a593Smuzhiyun if (targetid_is_pid(c))
502*4882a593Smuzhiyun id = (unsigned long)pid_vnr((struct pid *)id);
503*4882a593Smuzhiyun if (id == target_id) {
504*4882a593Smuzhiyun r = damon_new_region(ar->start, ar->end);
505*4882a593Smuzhiyun if (!r)
506*4882a593Smuzhiyun return -ENOMEM;
507*4882a593Smuzhiyun damon_add_region(r, t);
508*4882a593Smuzhiyun if (damon_nr_regions(t) > 1) {
509*4882a593Smuzhiyun prev = damon_prev_region(r);
510*4882a593Smuzhiyun if (prev->ar.end > r->ar.start) {
511*4882a593Smuzhiyun damon_destroy_region(r, t);
512*4882a593Smuzhiyun return -EINVAL;
513*4882a593Smuzhiyun }
514*4882a593Smuzhiyun }
515*4882a593Smuzhiyun rc = 0;
516*4882a593Smuzhiyun }
517*4882a593Smuzhiyun }
518*4882a593Smuzhiyun return rc;
519*4882a593Smuzhiyun }
520*4882a593Smuzhiyun
set_init_regions(struct damon_ctx * c,const char * str,ssize_t len)521*4882a593Smuzhiyun static int set_init_regions(struct damon_ctx *c, const char *str, ssize_t len)
522*4882a593Smuzhiyun {
523*4882a593Smuzhiyun struct damon_target *t;
524*4882a593Smuzhiyun struct damon_region *r, *next;
525*4882a593Smuzhiyun int pos = 0, parsed, ret;
526*4882a593Smuzhiyun unsigned long target_id;
527*4882a593Smuzhiyun struct damon_addr_range ar;
528*4882a593Smuzhiyun int err;
529*4882a593Smuzhiyun
530*4882a593Smuzhiyun damon_for_each_target(t, c) {
531*4882a593Smuzhiyun damon_for_each_region_safe(r, next, t)
532*4882a593Smuzhiyun damon_destroy_region(r, t);
533*4882a593Smuzhiyun }
534*4882a593Smuzhiyun
535*4882a593Smuzhiyun while (pos < len) {
536*4882a593Smuzhiyun ret = sscanf(&str[pos], "%lu %lu %lu%n",
537*4882a593Smuzhiyun &target_id, &ar.start, &ar.end, &parsed);
538*4882a593Smuzhiyun if (ret != 3)
539*4882a593Smuzhiyun break;
540*4882a593Smuzhiyun err = add_init_region(c, target_id, &ar);
541*4882a593Smuzhiyun if (err)
542*4882a593Smuzhiyun goto fail;
543*4882a593Smuzhiyun pos += parsed;
544*4882a593Smuzhiyun }
545*4882a593Smuzhiyun
546*4882a593Smuzhiyun return 0;
547*4882a593Smuzhiyun
548*4882a593Smuzhiyun fail:
549*4882a593Smuzhiyun damon_for_each_target(t, c) {
550*4882a593Smuzhiyun damon_for_each_region_safe(r, next, t)
551*4882a593Smuzhiyun damon_destroy_region(r, t);
552*4882a593Smuzhiyun }
553*4882a593Smuzhiyun return err;
554*4882a593Smuzhiyun }
555*4882a593Smuzhiyun
dbgfs_init_regions_write(struct file * file,const char __user * buf,size_t count,loff_t * ppos)556*4882a593Smuzhiyun static ssize_t dbgfs_init_regions_write(struct file *file,
557*4882a593Smuzhiyun const char __user *buf, size_t count,
558*4882a593Smuzhiyun loff_t *ppos)
559*4882a593Smuzhiyun {
560*4882a593Smuzhiyun struct damon_ctx *ctx = file->private_data;
561*4882a593Smuzhiyun char *kbuf;
562*4882a593Smuzhiyun ssize_t ret = count;
563*4882a593Smuzhiyun int err;
564*4882a593Smuzhiyun
565*4882a593Smuzhiyun kbuf = user_input_str(buf, count, ppos);
566*4882a593Smuzhiyun if (IS_ERR(kbuf))
567*4882a593Smuzhiyun return PTR_ERR(kbuf);
568*4882a593Smuzhiyun
569*4882a593Smuzhiyun mutex_lock(&ctx->kdamond_lock);
570*4882a593Smuzhiyun if (ctx->kdamond) {
571*4882a593Smuzhiyun ret = -EBUSY;
572*4882a593Smuzhiyun goto unlock_out;
573*4882a593Smuzhiyun }
574*4882a593Smuzhiyun
575*4882a593Smuzhiyun err = set_init_regions(ctx, kbuf, ret);
576*4882a593Smuzhiyun if (err)
577*4882a593Smuzhiyun ret = err;
578*4882a593Smuzhiyun
579*4882a593Smuzhiyun unlock_out:
580*4882a593Smuzhiyun mutex_unlock(&ctx->kdamond_lock);
581*4882a593Smuzhiyun kfree(kbuf);
582*4882a593Smuzhiyun return ret;
583*4882a593Smuzhiyun }
584*4882a593Smuzhiyun
dbgfs_kdamond_pid_read(struct file * file,char __user * buf,size_t count,loff_t * ppos)585*4882a593Smuzhiyun static ssize_t dbgfs_kdamond_pid_read(struct file *file,
586*4882a593Smuzhiyun char __user *buf, size_t count, loff_t *ppos)
587*4882a593Smuzhiyun {
588*4882a593Smuzhiyun struct damon_ctx *ctx = file->private_data;
589*4882a593Smuzhiyun char *kbuf;
590*4882a593Smuzhiyun ssize_t len;
591*4882a593Smuzhiyun
592*4882a593Smuzhiyun kbuf = kmalloc(count, GFP_KERNEL | __GFP_NOWARN);
593*4882a593Smuzhiyun if (!kbuf)
594*4882a593Smuzhiyun return -ENOMEM;
595*4882a593Smuzhiyun
596*4882a593Smuzhiyun mutex_lock(&ctx->kdamond_lock);
597*4882a593Smuzhiyun if (ctx->kdamond)
598*4882a593Smuzhiyun len = scnprintf(kbuf, count, "%d\n", ctx->kdamond->pid);
599*4882a593Smuzhiyun else
600*4882a593Smuzhiyun len = scnprintf(kbuf, count, "none\n");
601*4882a593Smuzhiyun mutex_unlock(&ctx->kdamond_lock);
602*4882a593Smuzhiyun if (!len)
603*4882a593Smuzhiyun goto out;
604*4882a593Smuzhiyun len = simple_read_from_buffer(buf, count, ppos, kbuf, len);
605*4882a593Smuzhiyun
606*4882a593Smuzhiyun out:
607*4882a593Smuzhiyun kfree(kbuf);
608*4882a593Smuzhiyun return len;
609*4882a593Smuzhiyun }
610*4882a593Smuzhiyun
damon_dbgfs_open(struct inode * inode,struct file * file)611*4882a593Smuzhiyun static int damon_dbgfs_open(struct inode *inode, struct file *file)
612*4882a593Smuzhiyun {
613*4882a593Smuzhiyun file->private_data = inode->i_private;
614*4882a593Smuzhiyun
615*4882a593Smuzhiyun return nonseekable_open(inode, file);
616*4882a593Smuzhiyun }
617*4882a593Smuzhiyun
618*4882a593Smuzhiyun static const struct file_operations attrs_fops = {
619*4882a593Smuzhiyun .open = damon_dbgfs_open,
620*4882a593Smuzhiyun .read = dbgfs_attrs_read,
621*4882a593Smuzhiyun .write = dbgfs_attrs_write,
622*4882a593Smuzhiyun };
623*4882a593Smuzhiyun
624*4882a593Smuzhiyun static const struct file_operations schemes_fops = {
625*4882a593Smuzhiyun .open = damon_dbgfs_open,
626*4882a593Smuzhiyun .read = dbgfs_schemes_read,
627*4882a593Smuzhiyun .write = dbgfs_schemes_write,
628*4882a593Smuzhiyun };
629*4882a593Smuzhiyun
630*4882a593Smuzhiyun static const struct file_operations target_ids_fops = {
631*4882a593Smuzhiyun .open = damon_dbgfs_open,
632*4882a593Smuzhiyun .read = dbgfs_target_ids_read,
633*4882a593Smuzhiyun .write = dbgfs_target_ids_write,
634*4882a593Smuzhiyun };
635*4882a593Smuzhiyun
636*4882a593Smuzhiyun static const struct file_operations init_regions_fops = {
637*4882a593Smuzhiyun .open = damon_dbgfs_open,
638*4882a593Smuzhiyun .read = dbgfs_init_regions_read,
639*4882a593Smuzhiyun .write = dbgfs_init_regions_write,
640*4882a593Smuzhiyun };
641*4882a593Smuzhiyun
642*4882a593Smuzhiyun static const struct file_operations kdamond_pid_fops = {
643*4882a593Smuzhiyun .open = damon_dbgfs_open,
644*4882a593Smuzhiyun .read = dbgfs_kdamond_pid_read,
645*4882a593Smuzhiyun };
646*4882a593Smuzhiyun
dbgfs_fill_ctx_dir(struct dentry * dir,struct damon_ctx * ctx)647*4882a593Smuzhiyun static void dbgfs_fill_ctx_dir(struct dentry *dir, struct damon_ctx *ctx)
648*4882a593Smuzhiyun {
649*4882a593Smuzhiyun const char * const file_names[] = {"attrs", "schemes", "target_ids",
650*4882a593Smuzhiyun "init_regions", "kdamond_pid"};
651*4882a593Smuzhiyun const struct file_operations *fops[] = {&attrs_fops, &schemes_fops,
652*4882a593Smuzhiyun &target_ids_fops, &init_regions_fops, &kdamond_pid_fops};
653*4882a593Smuzhiyun int i;
654*4882a593Smuzhiyun
655*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(file_names); i++)
656*4882a593Smuzhiyun debugfs_create_file(file_names[i], 0600, dir, ctx, fops[i]);
657*4882a593Smuzhiyun }
658*4882a593Smuzhiyun
dbgfs_before_terminate(struct damon_ctx * ctx)659*4882a593Smuzhiyun static void dbgfs_before_terminate(struct damon_ctx *ctx)
660*4882a593Smuzhiyun {
661*4882a593Smuzhiyun struct damon_target *t, *next;
662*4882a593Smuzhiyun
663*4882a593Smuzhiyun if (!targetid_is_pid(ctx))
664*4882a593Smuzhiyun return;
665*4882a593Smuzhiyun
666*4882a593Smuzhiyun mutex_lock(&ctx->kdamond_lock);
667*4882a593Smuzhiyun damon_for_each_target_safe(t, next, ctx) {
668*4882a593Smuzhiyun put_pid((struct pid *)t->id);
669*4882a593Smuzhiyun damon_destroy_target(t);
670*4882a593Smuzhiyun }
671*4882a593Smuzhiyun mutex_unlock(&ctx->kdamond_lock);
672*4882a593Smuzhiyun }
673*4882a593Smuzhiyun
dbgfs_new_ctx(void)674*4882a593Smuzhiyun static struct damon_ctx *dbgfs_new_ctx(void)
675*4882a593Smuzhiyun {
676*4882a593Smuzhiyun struct damon_ctx *ctx;
677*4882a593Smuzhiyun
678*4882a593Smuzhiyun ctx = damon_new_ctx();
679*4882a593Smuzhiyun if (!ctx)
680*4882a593Smuzhiyun return NULL;
681*4882a593Smuzhiyun
682*4882a593Smuzhiyun damon_va_set_primitives(ctx);
683*4882a593Smuzhiyun ctx->callback.before_terminate = dbgfs_before_terminate;
684*4882a593Smuzhiyun return ctx;
685*4882a593Smuzhiyun }
686*4882a593Smuzhiyun
dbgfs_destroy_ctx(struct damon_ctx * ctx)687*4882a593Smuzhiyun static void dbgfs_destroy_ctx(struct damon_ctx *ctx)
688*4882a593Smuzhiyun {
689*4882a593Smuzhiyun damon_destroy_ctx(ctx);
690*4882a593Smuzhiyun }
691*4882a593Smuzhiyun
692*4882a593Smuzhiyun /*
693*4882a593Smuzhiyun * Make a context of @name and create a debugfs directory for it.
694*4882a593Smuzhiyun *
695*4882a593Smuzhiyun * This function should be called while holding damon_dbgfs_lock.
696*4882a593Smuzhiyun *
697*4882a593Smuzhiyun * Returns 0 on success, negative error code otherwise.
698*4882a593Smuzhiyun */
dbgfs_mk_context(char * name)699*4882a593Smuzhiyun static int dbgfs_mk_context(char *name)
700*4882a593Smuzhiyun {
701*4882a593Smuzhiyun struct dentry *root, **new_dirs, *new_dir;
702*4882a593Smuzhiyun struct damon_ctx **new_ctxs, *new_ctx;
703*4882a593Smuzhiyun
704*4882a593Smuzhiyun if (damon_nr_running_ctxs())
705*4882a593Smuzhiyun return -EBUSY;
706*4882a593Smuzhiyun
707*4882a593Smuzhiyun new_ctxs = krealloc(dbgfs_ctxs, sizeof(*dbgfs_ctxs) *
708*4882a593Smuzhiyun (dbgfs_nr_ctxs + 1), GFP_KERNEL);
709*4882a593Smuzhiyun if (!new_ctxs)
710*4882a593Smuzhiyun return -ENOMEM;
711*4882a593Smuzhiyun dbgfs_ctxs = new_ctxs;
712*4882a593Smuzhiyun
713*4882a593Smuzhiyun new_dirs = krealloc(dbgfs_dirs, sizeof(*dbgfs_dirs) *
714*4882a593Smuzhiyun (dbgfs_nr_ctxs + 1), GFP_KERNEL);
715*4882a593Smuzhiyun if (!new_dirs)
716*4882a593Smuzhiyun return -ENOMEM;
717*4882a593Smuzhiyun dbgfs_dirs = new_dirs;
718*4882a593Smuzhiyun
719*4882a593Smuzhiyun root = dbgfs_dirs[0];
720*4882a593Smuzhiyun if (!root)
721*4882a593Smuzhiyun return -ENOENT;
722*4882a593Smuzhiyun
723*4882a593Smuzhiyun new_dir = debugfs_create_dir(name, root);
724*4882a593Smuzhiyun /* Below check is required for a potential duplicated name case */
725*4882a593Smuzhiyun if (IS_ERR(new_dir))
726*4882a593Smuzhiyun return PTR_ERR(new_dir);
727*4882a593Smuzhiyun dbgfs_dirs[dbgfs_nr_ctxs] = new_dir;
728*4882a593Smuzhiyun
729*4882a593Smuzhiyun new_ctx = dbgfs_new_ctx();
730*4882a593Smuzhiyun if (!new_ctx) {
731*4882a593Smuzhiyun debugfs_remove(new_dir);
732*4882a593Smuzhiyun dbgfs_dirs[dbgfs_nr_ctxs] = NULL;
733*4882a593Smuzhiyun return -ENOMEM;
734*4882a593Smuzhiyun }
735*4882a593Smuzhiyun
736*4882a593Smuzhiyun dbgfs_ctxs[dbgfs_nr_ctxs] = new_ctx;
737*4882a593Smuzhiyun dbgfs_fill_ctx_dir(dbgfs_dirs[dbgfs_nr_ctxs],
738*4882a593Smuzhiyun dbgfs_ctxs[dbgfs_nr_ctxs]);
739*4882a593Smuzhiyun dbgfs_nr_ctxs++;
740*4882a593Smuzhiyun
741*4882a593Smuzhiyun return 0;
742*4882a593Smuzhiyun }
743*4882a593Smuzhiyun
dbgfs_mk_context_write(struct file * file,const char __user * buf,size_t count,loff_t * ppos)744*4882a593Smuzhiyun static ssize_t dbgfs_mk_context_write(struct file *file,
745*4882a593Smuzhiyun const char __user *buf, size_t count, loff_t *ppos)
746*4882a593Smuzhiyun {
747*4882a593Smuzhiyun char *kbuf;
748*4882a593Smuzhiyun char *ctx_name;
749*4882a593Smuzhiyun ssize_t ret;
750*4882a593Smuzhiyun
751*4882a593Smuzhiyun kbuf = user_input_str(buf, count, ppos);
752*4882a593Smuzhiyun if (IS_ERR(kbuf))
753*4882a593Smuzhiyun return PTR_ERR(kbuf);
754*4882a593Smuzhiyun ctx_name = kmalloc(count + 1, GFP_KERNEL);
755*4882a593Smuzhiyun if (!ctx_name) {
756*4882a593Smuzhiyun kfree(kbuf);
757*4882a593Smuzhiyun return -ENOMEM;
758*4882a593Smuzhiyun }
759*4882a593Smuzhiyun
760*4882a593Smuzhiyun /* Trim white space */
761*4882a593Smuzhiyun if (sscanf(kbuf, "%s", ctx_name) != 1) {
762*4882a593Smuzhiyun ret = -EINVAL;
763*4882a593Smuzhiyun goto out;
764*4882a593Smuzhiyun }
765*4882a593Smuzhiyun
766*4882a593Smuzhiyun mutex_lock(&damon_dbgfs_lock);
767*4882a593Smuzhiyun ret = dbgfs_mk_context(ctx_name);
768*4882a593Smuzhiyun if (!ret)
769*4882a593Smuzhiyun ret = count;
770*4882a593Smuzhiyun mutex_unlock(&damon_dbgfs_lock);
771*4882a593Smuzhiyun
772*4882a593Smuzhiyun out:
773*4882a593Smuzhiyun kfree(kbuf);
774*4882a593Smuzhiyun kfree(ctx_name);
775*4882a593Smuzhiyun return ret;
776*4882a593Smuzhiyun }
777*4882a593Smuzhiyun
778*4882a593Smuzhiyun /*
779*4882a593Smuzhiyun * Remove a context of @name and its debugfs directory.
780*4882a593Smuzhiyun *
781*4882a593Smuzhiyun * This function should be called while holding damon_dbgfs_lock.
782*4882a593Smuzhiyun *
783*4882a593Smuzhiyun * Return 0 on success, negative error code otherwise.
784*4882a593Smuzhiyun */
dbgfs_rm_context(char * name)785*4882a593Smuzhiyun static int dbgfs_rm_context(char *name)
786*4882a593Smuzhiyun {
787*4882a593Smuzhiyun struct dentry *root, *dir, **new_dirs;
788*4882a593Smuzhiyun struct damon_ctx **new_ctxs;
789*4882a593Smuzhiyun int i, j;
790*4882a593Smuzhiyun int ret = 0;
791*4882a593Smuzhiyun
792*4882a593Smuzhiyun if (damon_nr_running_ctxs())
793*4882a593Smuzhiyun return -EBUSY;
794*4882a593Smuzhiyun
795*4882a593Smuzhiyun root = dbgfs_dirs[0];
796*4882a593Smuzhiyun if (!root)
797*4882a593Smuzhiyun return -ENOENT;
798*4882a593Smuzhiyun
799*4882a593Smuzhiyun dir = debugfs_lookup(name, root);
800*4882a593Smuzhiyun if (!dir)
801*4882a593Smuzhiyun return -ENOENT;
802*4882a593Smuzhiyun
803*4882a593Smuzhiyun new_dirs = kmalloc_array(dbgfs_nr_ctxs - 1, sizeof(*dbgfs_dirs),
804*4882a593Smuzhiyun GFP_KERNEL);
805*4882a593Smuzhiyun if (!new_dirs) {
806*4882a593Smuzhiyun ret = -ENOMEM;
807*4882a593Smuzhiyun goto out_dput;
808*4882a593Smuzhiyun }
809*4882a593Smuzhiyun
810*4882a593Smuzhiyun new_ctxs = kmalloc_array(dbgfs_nr_ctxs - 1, sizeof(*dbgfs_ctxs),
811*4882a593Smuzhiyun GFP_KERNEL);
812*4882a593Smuzhiyun if (!new_ctxs) {
813*4882a593Smuzhiyun ret = -ENOMEM;
814*4882a593Smuzhiyun goto out_new_dirs;
815*4882a593Smuzhiyun }
816*4882a593Smuzhiyun
817*4882a593Smuzhiyun for (i = 0, j = 0; i < dbgfs_nr_ctxs; i++) {
818*4882a593Smuzhiyun if (dbgfs_dirs[i] == dir) {
819*4882a593Smuzhiyun debugfs_remove(dbgfs_dirs[i]);
820*4882a593Smuzhiyun dbgfs_destroy_ctx(dbgfs_ctxs[i]);
821*4882a593Smuzhiyun continue;
822*4882a593Smuzhiyun }
823*4882a593Smuzhiyun new_dirs[j] = dbgfs_dirs[i];
824*4882a593Smuzhiyun new_ctxs[j++] = dbgfs_ctxs[i];
825*4882a593Smuzhiyun }
826*4882a593Smuzhiyun
827*4882a593Smuzhiyun kfree(dbgfs_dirs);
828*4882a593Smuzhiyun kfree(dbgfs_ctxs);
829*4882a593Smuzhiyun
830*4882a593Smuzhiyun dbgfs_dirs = new_dirs;
831*4882a593Smuzhiyun dbgfs_ctxs = new_ctxs;
832*4882a593Smuzhiyun dbgfs_nr_ctxs--;
833*4882a593Smuzhiyun
834*4882a593Smuzhiyun goto out_dput;
835*4882a593Smuzhiyun
836*4882a593Smuzhiyun out_new_dirs:
837*4882a593Smuzhiyun kfree(new_dirs);
838*4882a593Smuzhiyun out_dput:
839*4882a593Smuzhiyun dput(dir);
840*4882a593Smuzhiyun return ret;
841*4882a593Smuzhiyun }
842*4882a593Smuzhiyun
dbgfs_rm_context_write(struct file * file,const char __user * buf,size_t count,loff_t * ppos)843*4882a593Smuzhiyun static ssize_t dbgfs_rm_context_write(struct file *file,
844*4882a593Smuzhiyun const char __user *buf, size_t count, loff_t *ppos)
845*4882a593Smuzhiyun {
846*4882a593Smuzhiyun char *kbuf;
847*4882a593Smuzhiyun ssize_t ret;
848*4882a593Smuzhiyun char *ctx_name;
849*4882a593Smuzhiyun
850*4882a593Smuzhiyun kbuf = user_input_str(buf, count, ppos);
851*4882a593Smuzhiyun if (IS_ERR(kbuf))
852*4882a593Smuzhiyun return PTR_ERR(kbuf);
853*4882a593Smuzhiyun ctx_name = kmalloc(count + 1, GFP_KERNEL);
854*4882a593Smuzhiyun if (!ctx_name) {
855*4882a593Smuzhiyun kfree(kbuf);
856*4882a593Smuzhiyun return -ENOMEM;
857*4882a593Smuzhiyun }
858*4882a593Smuzhiyun
859*4882a593Smuzhiyun /* Trim white space */
860*4882a593Smuzhiyun if (sscanf(kbuf, "%s", ctx_name) != 1) {
861*4882a593Smuzhiyun ret = -EINVAL;
862*4882a593Smuzhiyun goto out;
863*4882a593Smuzhiyun }
864*4882a593Smuzhiyun
865*4882a593Smuzhiyun mutex_lock(&damon_dbgfs_lock);
866*4882a593Smuzhiyun ret = dbgfs_rm_context(ctx_name);
867*4882a593Smuzhiyun if (!ret)
868*4882a593Smuzhiyun ret = count;
869*4882a593Smuzhiyun mutex_unlock(&damon_dbgfs_lock);
870*4882a593Smuzhiyun
871*4882a593Smuzhiyun out:
872*4882a593Smuzhiyun kfree(kbuf);
873*4882a593Smuzhiyun kfree(ctx_name);
874*4882a593Smuzhiyun return ret;
875*4882a593Smuzhiyun }
876*4882a593Smuzhiyun
dbgfs_monitor_on_read(struct file * file,char __user * buf,size_t count,loff_t * ppos)877*4882a593Smuzhiyun static ssize_t dbgfs_monitor_on_read(struct file *file,
878*4882a593Smuzhiyun char __user *buf, size_t count, loff_t *ppos)
879*4882a593Smuzhiyun {
880*4882a593Smuzhiyun char monitor_on_buf[5];
881*4882a593Smuzhiyun bool monitor_on = damon_nr_running_ctxs() != 0;
882*4882a593Smuzhiyun int len;
883*4882a593Smuzhiyun
884*4882a593Smuzhiyun len = scnprintf(monitor_on_buf, 5, monitor_on ? "on\n" : "off\n");
885*4882a593Smuzhiyun
886*4882a593Smuzhiyun return simple_read_from_buffer(buf, count, ppos, monitor_on_buf, len);
887*4882a593Smuzhiyun }
888*4882a593Smuzhiyun
dbgfs_monitor_on_write(struct file * file,const char __user * buf,size_t count,loff_t * ppos)889*4882a593Smuzhiyun static ssize_t dbgfs_monitor_on_write(struct file *file,
890*4882a593Smuzhiyun const char __user *buf, size_t count, loff_t *ppos)
891*4882a593Smuzhiyun {
892*4882a593Smuzhiyun ssize_t ret;
893*4882a593Smuzhiyun char *kbuf;
894*4882a593Smuzhiyun
895*4882a593Smuzhiyun kbuf = user_input_str(buf, count, ppos);
896*4882a593Smuzhiyun if (IS_ERR(kbuf))
897*4882a593Smuzhiyun return PTR_ERR(kbuf);
898*4882a593Smuzhiyun
899*4882a593Smuzhiyun /* Remove white space */
900*4882a593Smuzhiyun if (sscanf(kbuf, "%s", kbuf) != 1) {
901*4882a593Smuzhiyun kfree(kbuf);
902*4882a593Smuzhiyun return -EINVAL;
903*4882a593Smuzhiyun }
904*4882a593Smuzhiyun
905*4882a593Smuzhiyun mutex_lock(&damon_dbgfs_lock);
906*4882a593Smuzhiyun if (!strncmp(kbuf, "on", count)) {
907*4882a593Smuzhiyun int i;
908*4882a593Smuzhiyun
909*4882a593Smuzhiyun for (i = 0; i < dbgfs_nr_ctxs; i++) {
910*4882a593Smuzhiyun if (damon_targets_empty(dbgfs_ctxs[i])) {
911*4882a593Smuzhiyun kfree(kbuf);
912*4882a593Smuzhiyun mutex_unlock(&damon_dbgfs_lock);
913*4882a593Smuzhiyun return -EINVAL;
914*4882a593Smuzhiyun }
915*4882a593Smuzhiyun }
916*4882a593Smuzhiyun ret = damon_start(dbgfs_ctxs, dbgfs_nr_ctxs);
917*4882a593Smuzhiyun } else if (!strncmp(kbuf, "off", count)) {
918*4882a593Smuzhiyun ret = damon_stop(dbgfs_ctxs, dbgfs_nr_ctxs);
919*4882a593Smuzhiyun } else {
920*4882a593Smuzhiyun ret = -EINVAL;
921*4882a593Smuzhiyun }
922*4882a593Smuzhiyun mutex_unlock(&damon_dbgfs_lock);
923*4882a593Smuzhiyun
924*4882a593Smuzhiyun if (!ret)
925*4882a593Smuzhiyun ret = count;
926*4882a593Smuzhiyun kfree(kbuf);
927*4882a593Smuzhiyun return ret;
928*4882a593Smuzhiyun }
929*4882a593Smuzhiyun
930*4882a593Smuzhiyun static const struct file_operations mk_contexts_fops = {
931*4882a593Smuzhiyun .write = dbgfs_mk_context_write,
932*4882a593Smuzhiyun };
933*4882a593Smuzhiyun
934*4882a593Smuzhiyun static const struct file_operations rm_contexts_fops = {
935*4882a593Smuzhiyun .write = dbgfs_rm_context_write,
936*4882a593Smuzhiyun };
937*4882a593Smuzhiyun
938*4882a593Smuzhiyun static const struct file_operations monitor_on_fops = {
939*4882a593Smuzhiyun .read = dbgfs_monitor_on_read,
940*4882a593Smuzhiyun .write = dbgfs_monitor_on_write,
941*4882a593Smuzhiyun };
942*4882a593Smuzhiyun
__damon_dbgfs_init(void)943*4882a593Smuzhiyun static int __init __damon_dbgfs_init(void)
944*4882a593Smuzhiyun {
945*4882a593Smuzhiyun struct dentry *dbgfs_root;
946*4882a593Smuzhiyun const char * const file_names[] = {"mk_contexts", "rm_contexts",
947*4882a593Smuzhiyun "monitor_on"};
948*4882a593Smuzhiyun const struct file_operations *fops[] = {&mk_contexts_fops,
949*4882a593Smuzhiyun &rm_contexts_fops, &monitor_on_fops};
950*4882a593Smuzhiyun int i;
951*4882a593Smuzhiyun
952*4882a593Smuzhiyun dbgfs_root = debugfs_create_dir("damon", NULL);
953*4882a593Smuzhiyun
954*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(file_names); i++)
955*4882a593Smuzhiyun debugfs_create_file(file_names[i], 0600, dbgfs_root, NULL,
956*4882a593Smuzhiyun fops[i]);
957*4882a593Smuzhiyun dbgfs_fill_ctx_dir(dbgfs_root, dbgfs_ctxs[0]);
958*4882a593Smuzhiyun
959*4882a593Smuzhiyun dbgfs_dirs = kmalloc_array(1, sizeof(dbgfs_root), GFP_KERNEL);
960*4882a593Smuzhiyun if (!dbgfs_dirs) {
961*4882a593Smuzhiyun debugfs_remove(dbgfs_root);
962*4882a593Smuzhiyun return -ENOMEM;
963*4882a593Smuzhiyun }
964*4882a593Smuzhiyun dbgfs_dirs[0] = dbgfs_root;
965*4882a593Smuzhiyun
966*4882a593Smuzhiyun return 0;
967*4882a593Smuzhiyun }
968*4882a593Smuzhiyun
969*4882a593Smuzhiyun /*
970*4882a593Smuzhiyun * Functions for the initialization
971*4882a593Smuzhiyun */
972*4882a593Smuzhiyun
damon_dbgfs_init(void)973*4882a593Smuzhiyun static int __init damon_dbgfs_init(void)
974*4882a593Smuzhiyun {
975*4882a593Smuzhiyun int rc = -ENOMEM;
976*4882a593Smuzhiyun
977*4882a593Smuzhiyun mutex_lock(&damon_dbgfs_lock);
978*4882a593Smuzhiyun dbgfs_ctxs = kmalloc(sizeof(*dbgfs_ctxs), GFP_KERNEL);
979*4882a593Smuzhiyun if (!dbgfs_ctxs)
980*4882a593Smuzhiyun goto out;
981*4882a593Smuzhiyun dbgfs_ctxs[0] = dbgfs_new_ctx();
982*4882a593Smuzhiyun if (!dbgfs_ctxs[0]) {
983*4882a593Smuzhiyun kfree(dbgfs_ctxs);
984*4882a593Smuzhiyun goto out;
985*4882a593Smuzhiyun }
986*4882a593Smuzhiyun dbgfs_nr_ctxs = 1;
987*4882a593Smuzhiyun
988*4882a593Smuzhiyun rc = __damon_dbgfs_init();
989*4882a593Smuzhiyun if (rc) {
990*4882a593Smuzhiyun kfree(dbgfs_ctxs[0]);
991*4882a593Smuzhiyun kfree(dbgfs_ctxs);
992*4882a593Smuzhiyun pr_err("%s: dbgfs init failed\n", __func__);
993*4882a593Smuzhiyun }
994*4882a593Smuzhiyun
995*4882a593Smuzhiyun out:
996*4882a593Smuzhiyun mutex_unlock(&damon_dbgfs_lock);
997*4882a593Smuzhiyun return rc;
998*4882a593Smuzhiyun }
999*4882a593Smuzhiyun
1000*4882a593Smuzhiyun module_init(damon_dbgfs_init);
1001*4882a593Smuzhiyun
1002*4882a593Smuzhiyun #include "dbgfs-test.h"
1003