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 101*af122dd8SSeongJae Park static ssize_t sprint_schemes(struct damon_ctx *c, char *buf, ssize_t len) 102*af122dd8SSeongJae Park { 103*af122dd8SSeongJae Park struct damos *s; 104*af122dd8SSeongJae Park int written = 0; 105*af122dd8SSeongJae Park int rc; 106*af122dd8SSeongJae Park 107*af122dd8SSeongJae Park damon_for_each_scheme(s, c) { 108*af122dd8SSeongJae Park rc = scnprintf(&buf[written], len - written, 109*af122dd8SSeongJae Park "%lu %lu %u %u %u %u %d\n", 110*af122dd8SSeongJae Park s->min_sz_region, s->max_sz_region, 111*af122dd8SSeongJae Park s->min_nr_accesses, s->max_nr_accesses, 112*af122dd8SSeongJae Park s->min_age_region, s->max_age_region, 113*af122dd8SSeongJae Park s->action); 114*af122dd8SSeongJae Park if (!rc) 115*af122dd8SSeongJae Park return -ENOMEM; 116*af122dd8SSeongJae Park 117*af122dd8SSeongJae Park written += rc; 118*af122dd8SSeongJae Park } 119*af122dd8SSeongJae Park return written; 120*af122dd8SSeongJae Park } 121*af122dd8SSeongJae Park 122*af122dd8SSeongJae Park static ssize_t dbgfs_schemes_read(struct file *file, char __user *buf, 123*af122dd8SSeongJae Park size_t count, loff_t *ppos) 124*af122dd8SSeongJae Park { 125*af122dd8SSeongJae Park struct damon_ctx *ctx = file->private_data; 126*af122dd8SSeongJae Park char *kbuf; 127*af122dd8SSeongJae Park ssize_t len; 128*af122dd8SSeongJae Park 129*af122dd8SSeongJae Park kbuf = kmalloc(count, GFP_KERNEL); 130*af122dd8SSeongJae Park if (!kbuf) 131*af122dd8SSeongJae Park return -ENOMEM; 132*af122dd8SSeongJae Park 133*af122dd8SSeongJae Park mutex_lock(&ctx->kdamond_lock); 134*af122dd8SSeongJae Park len = sprint_schemes(ctx, kbuf, count); 135*af122dd8SSeongJae Park mutex_unlock(&ctx->kdamond_lock); 136*af122dd8SSeongJae Park if (len < 0) 137*af122dd8SSeongJae Park goto out; 138*af122dd8SSeongJae Park len = simple_read_from_buffer(buf, count, ppos, kbuf, len); 139*af122dd8SSeongJae Park 140*af122dd8SSeongJae Park out: 141*af122dd8SSeongJae Park kfree(kbuf); 142*af122dd8SSeongJae Park return len; 143*af122dd8SSeongJae Park } 144*af122dd8SSeongJae Park 145*af122dd8SSeongJae Park static void free_schemes_arr(struct damos **schemes, ssize_t nr_schemes) 146*af122dd8SSeongJae Park { 147*af122dd8SSeongJae Park ssize_t i; 148*af122dd8SSeongJae Park 149*af122dd8SSeongJae Park for (i = 0; i < nr_schemes; i++) 150*af122dd8SSeongJae Park kfree(schemes[i]); 151*af122dd8SSeongJae Park kfree(schemes); 152*af122dd8SSeongJae Park } 153*af122dd8SSeongJae Park 154*af122dd8SSeongJae Park static bool damos_action_valid(int action) 155*af122dd8SSeongJae Park { 156*af122dd8SSeongJae Park switch (action) { 157*af122dd8SSeongJae Park case DAMOS_WILLNEED: 158*af122dd8SSeongJae Park case DAMOS_COLD: 159*af122dd8SSeongJae Park case DAMOS_PAGEOUT: 160*af122dd8SSeongJae Park case DAMOS_HUGEPAGE: 161*af122dd8SSeongJae Park case DAMOS_NOHUGEPAGE: 162*af122dd8SSeongJae Park return true; 163*af122dd8SSeongJae Park default: 164*af122dd8SSeongJae Park return false; 165*af122dd8SSeongJae Park } 166*af122dd8SSeongJae Park } 167*af122dd8SSeongJae Park 168*af122dd8SSeongJae Park /* 169*af122dd8SSeongJae Park * Converts a string into an array of struct damos pointers 170*af122dd8SSeongJae Park * 171*af122dd8SSeongJae Park * Returns an array of struct damos pointers that converted if the conversion 172*af122dd8SSeongJae Park * success, or NULL otherwise. 173*af122dd8SSeongJae Park */ 174*af122dd8SSeongJae Park static struct damos **str_to_schemes(const char *str, ssize_t len, 175*af122dd8SSeongJae Park ssize_t *nr_schemes) 176*af122dd8SSeongJae Park { 177*af122dd8SSeongJae Park struct damos *scheme, **schemes; 178*af122dd8SSeongJae Park const int max_nr_schemes = 256; 179*af122dd8SSeongJae Park int pos = 0, parsed, ret; 180*af122dd8SSeongJae Park unsigned long min_sz, max_sz; 181*af122dd8SSeongJae Park unsigned int min_nr_a, max_nr_a, min_age, max_age; 182*af122dd8SSeongJae Park unsigned int action; 183*af122dd8SSeongJae Park 184*af122dd8SSeongJae Park schemes = kmalloc_array(max_nr_schemes, sizeof(scheme), 185*af122dd8SSeongJae Park GFP_KERNEL); 186*af122dd8SSeongJae Park if (!schemes) 187*af122dd8SSeongJae Park return NULL; 188*af122dd8SSeongJae Park 189*af122dd8SSeongJae Park *nr_schemes = 0; 190*af122dd8SSeongJae Park while (pos < len && *nr_schemes < max_nr_schemes) { 191*af122dd8SSeongJae Park ret = sscanf(&str[pos], "%lu %lu %u %u %u %u %u%n", 192*af122dd8SSeongJae Park &min_sz, &max_sz, &min_nr_a, &max_nr_a, 193*af122dd8SSeongJae Park &min_age, &max_age, &action, &parsed); 194*af122dd8SSeongJae Park if (ret != 7) 195*af122dd8SSeongJae Park break; 196*af122dd8SSeongJae Park if (!damos_action_valid(action)) { 197*af122dd8SSeongJae Park pr_err("wrong action %d\n", action); 198*af122dd8SSeongJae Park goto fail; 199*af122dd8SSeongJae Park } 200*af122dd8SSeongJae Park 201*af122dd8SSeongJae Park pos += parsed; 202*af122dd8SSeongJae Park scheme = damon_new_scheme(min_sz, max_sz, min_nr_a, max_nr_a, 203*af122dd8SSeongJae Park min_age, max_age, action); 204*af122dd8SSeongJae Park if (!scheme) 205*af122dd8SSeongJae Park goto fail; 206*af122dd8SSeongJae Park 207*af122dd8SSeongJae Park schemes[*nr_schemes] = scheme; 208*af122dd8SSeongJae Park *nr_schemes += 1; 209*af122dd8SSeongJae Park } 210*af122dd8SSeongJae Park return schemes; 211*af122dd8SSeongJae Park fail: 212*af122dd8SSeongJae Park free_schemes_arr(schemes, *nr_schemes); 213*af122dd8SSeongJae Park return NULL; 214*af122dd8SSeongJae Park } 215*af122dd8SSeongJae Park 216*af122dd8SSeongJae Park static ssize_t dbgfs_schemes_write(struct file *file, const char __user *buf, 217*af122dd8SSeongJae Park size_t count, loff_t *ppos) 218*af122dd8SSeongJae Park { 219*af122dd8SSeongJae Park struct damon_ctx *ctx = file->private_data; 220*af122dd8SSeongJae Park char *kbuf; 221*af122dd8SSeongJae Park struct damos **schemes; 222*af122dd8SSeongJae Park ssize_t nr_schemes = 0, ret = count; 223*af122dd8SSeongJae Park int err; 224*af122dd8SSeongJae Park 225*af122dd8SSeongJae Park kbuf = user_input_str(buf, count, ppos); 226*af122dd8SSeongJae Park if (IS_ERR(kbuf)) 227*af122dd8SSeongJae Park return PTR_ERR(kbuf); 228*af122dd8SSeongJae Park 229*af122dd8SSeongJae Park schemes = str_to_schemes(kbuf, ret, &nr_schemes); 230*af122dd8SSeongJae Park if (!schemes) { 231*af122dd8SSeongJae Park ret = -EINVAL; 232*af122dd8SSeongJae Park goto out; 233*af122dd8SSeongJae Park } 234*af122dd8SSeongJae Park 235*af122dd8SSeongJae Park mutex_lock(&ctx->kdamond_lock); 236*af122dd8SSeongJae Park if (ctx->kdamond) { 237*af122dd8SSeongJae Park ret = -EBUSY; 238*af122dd8SSeongJae Park goto unlock_out; 239*af122dd8SSeongJae Park } 240*af122dd8SSeongJae Park 241*af122dd8SSeongJae Park err = damon_set_schemes(ctx, schemes, nr_schemes); 242*af122dd8SSeongJae Park if (err) 243*af122dd8SSeongJae Park ret = err; 244*af122dd8SSeongJae Park else 245*af122dd8SSeongJae Park nr_schemes = 0; 246*af122dd8SSeongJae Park unlock_out: 247*af122dd8SSeongJae Park mutex_unlock(&ctx->kdamond_lock); 248*af122dd8SSeongJae Park free_schemes_arr(schemes, nr_schemes); 249*af122dd8SSeongJae Park out: 250*af122dd8SSeongJae Park kfree(kbuf); 251*af122dd8SSeongJae Park return ret; 252*af122dd8SSeongJae Park } 253*af122dd8SSeongJae Park 2544bc05954SSeongJae Park static inline bool targetid_is_pid(const struct damon_ctx *ctx) 2554bc05954SSeongJae Park { 2564bc05954SSeongJae Park return ctx->primitive.target_valid == damon_va_target_valid; 2574bc05954SSeongJae Park } 2584bc05954SSeongJae Park 2594bc05954SSeongJae Park static ssize_t sprint_target_ids(struct damon_ctx *ctx, char *buf, ssize_t len) 2604bc05954SSeongJae Park { 2614bc05954SSeongJae Park struct damon_target *t; 2624bc05954SSeongJae Park unsigned long id; 2634bc05954SSeongJae Park int written = 0; 2644bc05954SSeongJae Park int rc; 2654bc05954SSeongJae Park 2664bc05954SSeongJae Park damon_for_each_target(t, ctx) { 2674bc05954SSeongJae Park id = t->id; 2684bc05954SSeongJae Park if (targetid_is_pid(ctx)) 2694bc05954SSeongJae Park /* Show pid numbers to debugfs users */ 2704bc05954SSeongJae Park id = (unsigned long)pid_vnr((struct pid *)id); 2714bc05954SSeongJae Park 2724bc05954SSeongJae Park rc = scnprintf(&buf[written], len - written, "%lu ", id); 2734bc05954SSeongJae Park if (!rc) 2744bc05954SSeongJae Park return -ENOMEM; 2754bc05954SSeongJae Park written += rc; 2764bc05954SSeongJae Park } 2774bc05954SSeongJae Park if (written) 2784bc05954SSeongJae Park written -= 1; 2794bc05954SSeongJae Park written += scnprintf(&buf[written], len - written, "\n"); 2804bc05954SSeongJae Park return written; 2814bc05954SSeongJae Park } 2824bc05954SSeongJae Park 2834bc05954SSeongJae Park static ssize_t dbgfs_target_ids_read(struct file *file, 2844bc05954SSeongJae Park char __user *buf, size_t count, loff_t *ppos) 2854bc05954SSeongJae Park { 2864bc05954SSeongJae Park struct damon_ctx *ctx = file->private_data; 2874bc05954SSeongJae Park ssize_t len; 2884bc05954SSeongJae Park char ids_buf[320]; 2894bc05954SSeongJae Park 2904bc05954SSeongJae Park mutex_lock(&ctx->kdamond_lock); 2914bc05954SSeongJae Park len = sprint_target_ids(ctx, ids_buf, 320); 2924bc05954SSeongJae Park mutex_unlock(&ctx->kdamond_lock); 2934bc05954SSeongJae Park if (len < 0) 2944bc05954SSeongJae Park return len; 2954bc05954SSeongJae Park 2964bc05954SSeongJae Park return simple_read_from_buffer(buf, count, ppos, ids_buf, len); 2974bc05954SSeongJae Park } 2984bc05954SSeongJae Park 2994bc05954SSeongJae Park /* 3004bc05954SSeongJae Park * Converts a string into an array of unsigned long integers 3014bc05954SSeongJae Park * 3024bc05954SSeongJae Park * Returns an array of unsigned long integers if the conversion success, or 3034bc05954SSeongJae Park * NULL otherwise. 3044bc05954SSeongJae Park */ 3054bc05954SSeongJae Park static unsigned long *str_to_target_ids(const char *str, ssize_t len, 3064bc05954SSeongJae Park ssize_t *nr_ids) 3074bc05954SSeongJae Park { 3084bc05954SSeongJae Park unsigned long *ids; 3094bc05954SSeongJae Park const int max_nr_ids = 32; 3104bc05954SSeongJae Park unsigned long id; 3114bc05954SSeongJae Park int pos = 0, parsed, ret; 3124bc05954SSeongJae Park 3134bc05954SSeongJae Park *nr_ids = 0; 3144bc05954SSeongJae Park ids = kmalloc_array(max_nr_ids, sizeof(id), GFP_KERNEL); 3154bc05954SSeongJae Park if (!ids) 3164bc05954SSeongJae Park return NULL; 3174bc05954SSeongJae Park while (*nr_ids < max_nr_ids && pos < len) { 3184bc05954SSeongJae Park ret = sscanf(&str[pos], "%lu%n", &id, &parsed); 3194bc05954SSeongJae Park pos += parsed; 3204bc05954SSeongJae Park if (ret != 1) 3214bc05954SSeongJae Park break; 3224bc05954SSeongJae Park ids[*nr_ids] = id; 3234bc05954SSeongJae Park *nr_ids += 1; 3244bc05954SSeongJae Park } 3254bc05954SSeongJae Park 3264bc05954SSeongJae Park return ids; 3274bc05954SSeongJae Park } 3284bc05954SSeongJae Park 3294bc05954SSeongJae Park static void dbgfs_put_pids(unsigned long *ids, int nr_ids) 3304bc05954SSeongJae Park { 3314bc05954SSeongJae Park int i; 3324bc05954SSeongJae Park 3334bc05954SSeongJae Park for (i = 0; i < nr_ids; i++) 3344bc05954SSeongJae Park put_pid((struct pid *)ids[i]); 3354bc05954SSeongJae Park } 3364bc05954SSeongJae Park 3374bc05954SSeongJae Park static ssize_t dbgfs_target_ids_write(struct file *file, 3384bc05954SSeongJae Park const char __user *buf, size_t count, loff_t *ppos) 3394bc05954SSeongJae Park { 3404bc05954SSeongJae Park struct damon_ctx *ctx = file->private_data; 3414bc05954SSeongJae Park char *kbuf, *nrs; 3424bc05954SSeongJae Park unsigned long *targets; 3434bc05954SSeongJae Park ssize_t nr_targets; 3444bc05954SSeongJae Park ssize_t ret = count; 3454bc05954SSeongJae Park int i; 3464bc05954SSeongJae Park int err; 3474bc05954SSeongJae Park 3484bc05954SSeongJae Park kbuf = user_input_str(buf, count, ppos); 3494bc05954SSeongJae Park if (IS_ERR(kbuf)) 3504bc05954SSeongJae Park return PTR_ERR(kbuf); 3514bc05954SSeongJae Park 3524bc05954SSeongJae Park nrs = kbuf; 3534bc05954SSeongJae Park 3544bc05954SSeongJae Park targets = str_to_target_ids(nrs, ret, &nr_targets); 3554bc05954SSeongJae Park if (!targets) { 3564bc05954SSeongJae Park ret = -ENOMEM; 3574bc05954SSeongJae Park goto out; 3584bc05954SSeongJae Park } 3594bc05954SSeongJae Park 3604bc05954SSeongJae Park if (targetid_is_pid(ctx)) { 3614bc05954SSeongJae Park for (i = 0; i < nr_targets; i++) { 3624bc05954SSeongJae Park targets[i] = (unsigned long)find_get_pid( 3634bc05954SSeongJae Park (int)targets[i]); 3644bc05954SSeongJae Park if (!targets[i]) { 3654bc05954SSeongJae Park dbgfs_put_pids(targets, i); 3664bc05954SSeongJae Park ret = -EINVAL; 3674bc05954SSeongJae Park goto free_targets_out; 3684bc05954SSeongJae Park } 3694bc05954SSeongJae Park } 3704bc05954SSeongJae Park } 3714bc05954SSeongJae Park 3724bc05954SSeongJae Park mutex_lock(&ctx->kdamond_lock); 3734bc05954SSeongJae Park if (ctx->kdamond) { 3744bc05954SSeongJae Park if (targetid_is_pid(ctx)) 3754bc05954SSeongJae Park dbgfs_put_pids(targets, nr_targets); 3764bc05954SSeongJae Park ret = -EBUSY; 3774bc05954SSeongJae Park goto unlock_out; 3784bc05954SSeongJae Park } 3794bc05954SSeongJae Park 3804bc05954SSeongJae Park err = damon_set_targets(ctx, targets, nr_targets); 3814bc05954SSeongJae Park if (err) { 3824bc05954SSeongJae Park if (targetid_is_pid(ctx)) 3834bc05954SSeongJae Park dbgfs_put_pids(targets, nr_targets); 3844bc05954SSeongJae Park ret = err; 3854bc05954SSeongJae Park } 3864bc05954SSeongJae Park 3874bc05954SSeongJae Park unlock_out: 3884bc05954SSeongJae Park mutex_unlock(&ctx->kdamond_lock); 3894bc05954SSeongJae Park free_targets_out: 3904bc05954SSeongJae Park kfree(targets); 3914bc05954SSeongJae Park out: 3924bc05954SSeongJae Park kfree(kbuf); 3934bc05954SSeongJae Park return ret; 3944bc05954SSeongJae Park } 3954bc05954SSeongJae Park 396429538e8SSeongJae Park static ssize_t dbgfs_kdamond_pid_read(struct file *file, 397429538e8SSeongJae Park char __user *buf, size_t count, loff_t *ppos) 398429538e8SSeongJae Park { 399429538e8SSeongJae Park struct damon_ctx *ctx = file->private_data; 400429538e8SSeongJae Park char *kbuf; 401429538e8SSeongJae Park ssize_t len; 402429538e8SSeongJae Park 403429538e8SSeongJae Park kbuf = kmalloc(count, GFP_KERNEL); 404429538e8SSeongJae Park if (!kbuf) 405429538e8SSeongJae Park return -ENOMEM; 406429538e8SSeongJae Park 407429538e8SSeongJae Park mutex_lock(&ctx->kdamond_lock); 408429538e8SSeongJae Park if (ctx->kdamond) 409429538e8SSeongJae Park len = scnprintf(kbuf, count, "%d\n", ctx->kdamond->pid); 410429538e8SSeongJae Park else 411429538e8SSeongJae Park len = scnprintf(kbuf, count, "none\n"); 412429538e8SSeongJae Park mutex_unlock(&ctx->kdamond_lock); 413429538e8SSeongJae Park if (!len) 414429538e8SSeongJae Park goto out; 415429538e8SSeongJae Park len = simple_read_from_buffer(buf, count, ppos, kbuf, len); 416429538e8SSeongJae Park 417429538e8SSeongJae Park out: 418429538e8SSeongJae Park kfree(kbuf); 419429538e8SSeongJae Park return len; 420429538e8SSeongJae Park } 421429538e8SSeongJae Park 4224bc05954SSeongJae Park static int damon_dbgfs_open(struct inode *inode, struct file *file) 4234bc05954SSeongJae Park { 4244bc05954SSeongJae Park file->private_data = inode->i_private; 4254bc05954SSeongJae Park 4264bc05954SSeongJae Park return nonseekable_open(inode, file); 4274bc05954SSeongJae Park } 4284bc05954SSeongJae Park 4294bc05954SSeongJae Park static const struct file_operations attrs_fops = { 4304bc05954SSeongJae Park .open = damon_dbgfs_open, 4314bc05954SSeongJae Park .read = dbgfs_attrs_read, 4324bc05954SSeongJae Park .write = dbgfs_attrs_write, 4334bc05954SSeongJae Park }; 4344bc05954SSeongJae Park 435*af122dd8SSeongJae Park static const struct file_operations schemes_fops = { 436*af122dd8SSeongJae Park .open = damon_dbgfs_open, 437*af122dd8SSeongJae Park .read = dbgfs_schemes_read, 438*af122dd8SSeongJae Park .write = dbgfs_schemes_write, 439*af122dd8SSeongJae Park }; 440*af122dd8SSeongJae Park 4414bc05954SSeongJae Park static const struct file_operations target_ids_fops = { 4424bc05954SSeongJae Park .open = damon_dbgfs_open, 4434bc05954SSeongJae Park .read = dbgfs_target_ids_read, 4444bc05954SSeongJae Park .write = dbgfs_target_ids_write, 4454bc05954SSeongJae Park }; 4464bc05954SSeongJae Park 447429538e8SSeongJae Park static const struct file_operations kdamond_pid_fops = { 448429538e8SSeongJae Park .open = damon_dbgfs_open, 449429538e8SSeongJae Park .read = dbgfs_kdamond_pid_read, 450429538e8SSeongJae Park }; 451429538e8SSeongJae Park 4524bc05954SSeongJae Park static void dbgfs_fill_ctx_dir(struct dentry *dir, struct damon_ctx *ctx) 4534bc05954SSeongJae Park { 454*af122dd8SSeongJae Park const char * const file_names[] = {"attrs", "schemes", "target_ids", 455429538e8SSeongJae Park "kdamond_pid"}; 456*af122dd8SSeongJae Park const struct file_operations *fops[] = {&attrs_fops, &schemes_fops, 457*af122dd8SSeongJae Park &target_ids_fops, &kdamond_pid_fops}; 4584bc05954SSeongJae Park int i; 4594bc05954SSeongJae Park 4604bc05954SSeongJae Park for (i = 0; i < ARRAY_SIZE(file_names); i++) 4614bc05954SSeongJae Park debugfs_create_file(file_names[i], 0600, dir, ctx, fops[i]); 4624bc05954SSeongJae Park } 4634bc05954SSeongJae Park 4644bc05954SSeongJae Park static int dbgfs_before_terminate(struct damon_ctx *ctx) 4654bc05954SSeongJae Park { 4664bc05954SSeongJae Park struct damon_target *t, *next; 4674bc05954SSeongJae Park 4684bc05954SSeongJae Park if (!targetid_is_pid(ctx)) 4694bc05954SSeongJae Park return 0; 4704bc05954SSeongJae Park 4714bc05954SSeongJae Park damon_for_each_target_safe(t, next, ctx) { 4724bc05954SSeongJae Park put_pid((struct pid *)t->id); 4734bc05954SSeongJae Park damon_destroy_target(t); 4744bc05954SSeongJae Park } 4754bc05954SSeongJae Park return 0; 4764bc05954SSeongJae Park } 4774bc05954SSeongJae Park 4784bc05954SSeongJae Park static struct damon_ctx *dbgfs_new_ctx(void) 4794bc05954SSeongJae Park { 4804bc05954SSeongJae Park struct damon_ctx *ctx; 4814bc05954SSeongJae Park 4824bc05954SSeongJae Park ctx = damon_new_ctx(); 4834bc05954SSeongJae Park if (!ctx) 4844bc05954SSeongJae Park return NULL; 4854bc05954SSeongJae Park 4864bc05954SSeongJae Park damon_va_set_primitives(ctx); 4874bc05954SSeongJae Park ctx->callback.before_terminate = dbgfs_before_terminate; 4884bc05954SSeongJae Park return ctx; 4894bc05954SSeongJae Park } 4904bc05954SSeongJae Park 49175c1c2b5SSeongJae Park static void dbgfs_destroy_ctx(struct damon_ctx *ctx) 49275c1c2b5SSeongJae Park { 49375c1c2b5SSeongJae Park damon_destroy_ctx(ctx); 49475c1c2b5SSeongJae Park } 49575c1c2b5SSeongJae Park 49675c1c2b5SSeongJae Park /* 49775c1c2b5SSeongJae Park * Make a context of @name and create a debugfs directory for it. 49875c1c2b5SSeongJae Park * 49975c1c2b5SSeongJae Park * This function should be called while holding damon_dbgfs_lock. 50075c1c2b5SSeongJae Park * 50175c1c2b5SSeongJae Park * Returns 0 on success, negative error code otherwise. 50275c1c2b5SSeongJae Park */ 50375c1c2b5SSeongJae Park static int dbgfs_mk_context(char *name) 50475c1c2b5SSeongJae Park { 50575c1c2b5SSeongJae Park struct dentry *root, **new_dirs, *new_dir; 50675c1c2b5SSeongJae Park struct damon_ctx **new_ctxs, *new_ctx; 50775c1c2b5SSeongJae Park 50875c1c2b5SSeongJae Park if (damon_nr_running_ctxs()) 50975c1c2b5SSeongJae Park return -EBUSY; 51075c1c2b5SSeongJae Park 51175c1c2b5SSeongJae Park new_ctxs = krealloc(dbgfs_ctxs, sizeof(*dbgfs_ctxs) * 51275c1c2b5SSeongJae Park (dbgfs_nr_ctxs + 1), GFP_KERNEL); 51375c1c2b5SSeongJae Park if (!new_ctxs) 51475c1c2b5SSeongJae Park return -ENOMEM; 51575c1c2b5SSeongJae Park dbgfs_ctxs = new_ctxs; 51675c1c2b5SSeongJae Park 51775c1c2b5SSeongJae Park new_dirs = krealloc(dbgfs_dirs, sizeof(*dbgfs_dirs) * 51875c1c2b5SSeongJae Park (dbgfs_nr_ctxs + 1), GFP_KERNEL); 51975c1c2b5SSeongJae Park if (!new_dirs) 52075c1c2b5SSeongJae Park return -ENOMEM; 52175c1c2b5SSeongJae Park dbgfs_dirs = new_dirs; 52275c1c2b5SSeongJae Park 52375c1c2b5SSeongJae Park root = dbgfs_dirs[0]; 52475c1c2b5SSeongJae Park if (!root) 52575c1c2b5SSeongJae Park return -ENOENT; 52675c1c2b5SSeongJae Park 52775c1c2b5SSeongJae Park new_dir = debugfs_create_dir(name, root); 52875c1c2b5SSeongJae Park dbgfs_dirs[dbgfs_nr_ctxs] = new_dir; 52975c1c2b5SSeongJae Park 53075c1c2b5SSeongJae Park new_ctx = dbgfs_new_ctx(); 53175c1c2b5SSeongJae Park if (!new_ctx) { 53275c1c2b5SSeongJae Park debugfs_remove(new_dir); 53375c1c2b5SSeongJae Park dbgfs_dirs[dbgfs_nr_ctxs] = NULL; 53475c1c2b5SSeongJae Park return -ENOMEM; 53575c1c2b5SSeongJae Park } 53675c1c2b5SSeongJae Park 53775c1c2b5SSeongJae Park dbgfs_ctxs[dbgfs_nr_ctxs] = new_ctx; 53875c1c2b5SSeongJae Park dbgfs_fill_ctx_dir(dbgfs_dirs[dbgfs_nr_ctxs], 53975c1c2b5SSeongJae Park dbgfs_ctxs[dbgfs_nr_ctxs]); 54075c1c2b5SSeongJae Park dbgfs_nr_ctxs++; 54175c1c2b5SSeongJae Park 54275c1c2b5SSeongJae Park return 0; 54375c1c2b5SSeongJae Park } 54475c1c2b5SSeongJae Park 54575c1c2b5SSeongJae Park static ssize_t dbgfs_mk_context_write(struct file *file, 54675c1c2b5SSeongJae Park const char __user *buf, size_t count, loff_t *ppos) 54775c1c2b5SSeongJae Park { 54875c1c2b5SSeongJae Park char *kbuf; 54975c1c2b5SSeongJae Park char *ctx_name; 55075c1c2b5SSeongJae Park ssize_t ret = count; 55175c1c2b5SSeongJae Park int err; 55275c1c2b5SSeongJae Park 55375c1c2b5SSeongJae Park kbuf = user_input_str(buf, count, ppos); 55475c1c2b5SSeongJae Park if (IS_ERR(kbuf)) 55575c1c2b5SSeongJae Park return PTR_ERR(kbuf); 55675c1c2b5SSeongJae Park ctx_name = kmalloc(count + 1, GFP_KERNEL); 55775c1c2b5SSeongJae Park if (!ctx_name) { 55875c1c2b5SSeongJae Park kfree(kbuf); 55975c1c2b5SSeongJae Park return -ENOMEM; 56075c1c2b5SSeongJae Park } 56175c1c2b5SSeongJae Park 56275c1c2b5SSeongJae Park /* Trim white space */ 56375c1c2b5SSeongJae Park if (sscanf(kbuf, "%s", ctx_name) != 1) { 56475c1c2b5SSeongJae Park ret = -EINVAL; 56575c1c2b5SSeongJae Park goto out; 56675c1c2b5SSeongJae Park } 56775c1c2b5SSeongJae Park 56875c1c2b5SSeongJae Park mutex_lock(&damon_dbgfs_lock); 56975c1c2b5SSeongJae Park err = dbgfs_mk_context(ctx_name); 57075c1c2b5SSeongJae Park if (err) 57175c1c2b5SSeongJae Park ret = err; 57275c1c2b5SSeongJae Park mutex_unlock(&damon_dbgfs_lock); 57375c1c2b5SSeongJae Park 57475c1c2b5SSeongJae Park out: 57575c1c2b5SSeongJae Park kfree(kbuf); 57675c1c2b5SSeongJae Park kfree(ctx_name); 57775c1c2b5SSeongJae Park return ret; 57875c1c2b5SSeongJae Park } 57975c1c2b5SSeongJae Park 58075c1c2b5SSeongJae Park /* 58175c1c2b5SSeongJae Park * Remove a context of @name and its debugfs directory. 58275c1c2b5SSeongJae Park * 58375c1c2b5SSeongJae Park * This function should be called while holding damon_dbgfs_lock. 58475c1c2b5SSeongJae Park * 58575c1c2b5SSeongJae Park * Return 0 on success, negative error code otherwise. 58675c1c2b5SSeongJae Park */ 58775c1c2b5SSeongJae Park static int dbgfs_rm_context(char *name) 58875c1c2b5SSeongJae Park { 58975c1c2b5SSeongJae Park struct dentry *root, *dir, **new_dirs; 59075c1c2b5SSeongJae Park struct damon_ctx **new_ctxs; 59175c1c2b5SSeongJae Park int i, j; 59275c1c2b5SSeongJae Park 59375c1c2b5SSeongJae Park if (damon_nr_running_ctxs()) 59475c1c2b5SSeongJae Park return -EBUSY; 59575c1c2b5SSeongJae Park 59675c1c2b5SSeongJae Park root = dbgfs_dirs[0]; 59775c1c2b5SSeongJae Park if (!root) 59875c1c2b5SSeongJae Park return -ENOENT; 59975c1c2b5SSeongJae Park 60075c1c2b5SSeongJae Park dir = debugfs_lookup(name, root); 60175c1c2b5SSeongJae Park if (!dir) 60275c1c2b5SSeongJae Park return -ENOENT; 60375c1c2b5SSeongJae Park 60475c1c2b5SSeongJae Park new_dirs = kmalloc_array(dbgfs_nr_ctxs - 1, sizeof(*dbgfs_dirs), 60575c1c2b5SSeongJae Park GFP_KERNEL); 60675c1c2b5SSeongJae Park if (!new_dirs) 60775c1c2b5SSeongJae Park return -ENOMEM; 60875c1c2b5SSeongJae Park 60975c1c2b5SSeongJae Park new_ctxs = kmalloc_array(dbgfs_nr_ctxs - 1, sizeof(*dbgfs_ctxs), 61075c1c2b5SSeongJae Park GFP_KERNEL); 61175c1c2b5SSeongJae Park if (!new_ctxs) { 61275c1c2b5SSeongJae Park kfree(new_dirs); 61375c1c2b5SSeongJae Park return -ENOMEM; 61475c1c2b5SSeongJae Park } 61575c1c2b5SSeongJae Park 61675c1c2b5SSeongJae Park for (i = 0, j = 0; i < dbgfs_nr_ctxs; i++) { 61775c1c2b5SSeongJae Park if (dbgfs_dirs[i] == dir) { 61875c1c2b5SSeongJae Park debugfs_remove(dbgfs_dirs[i]); 61975c1c2b5SSeongJae Park dbgfs_destroy_ctx(dbgfs_ctxs[i]); 62075c1c2b5SSeongJae Park continue; 62175c1c2b5SSeongJae Park } 62275c1c2b5SSeongJae Park new_dirs[j] = dbgfs_dirs[i]; 62375c1c2b5SSeongJae Park new_ctxs[j++] = dbgfs_ctxs[i]; 62475c1c2b5SSeongJae Park } 62575c1c2b5SSeongJae Park 62675c1c2b5SSeongJae Park kfree(dbgfs_dirs); 62775c1c2b5SSeongJae Park kfree(dbgfs_ctxs); 62875c1c2b5SSeongJae Park 62975c1c2b5SSeongJae Park dbgfs_dirs = new_dirs; 63075c1c2b5SSeongJae Park dbgfs_ctxs = new_ctxs; 63175c1c2b5SSeongJae Park dbgfs_nr_ctxs--; 63275c1c2b5SSeongJae Park 63375c1c2b5SSeongJae Park return 0; 63475c1c2b5SSeongJae Park } 63575c1c2b5SSeongJae Park 63675c1c2b5SSeongJae Park static ssize_t dbgfs_rm_context_write(struct file *file, 63775c1c2b5SSeongJae Park const char __user *buf, size_t count, loff_t *ppos) 63875c1c2b5SSeongJae Park { 63975c1c2b5SSeongJae Park char *kbuf; 64075c1c2b5SSeongJae Park ssize_t ret = count; 64175c1c2b5SSeongJae Park int err; 64275c1c2b5SSeongJae Park char *ctx_name; 64375c1c2b5SSeongJae Park 64475c1c2b5SSeongJae Park kbuf = user_input_str(buf, count, ppos); 64575c1c2b5SSeongJae Park if (IS_ERR(kbuf)) 64675c1c2b5SSeongJae Park return PTR_ERR(kbuf); 64775c1c2b5SSeongJae Park ctx_name = kmalloc(count + 1, GFP_KERNEL); 64875c1c2b5SSeongJae Park if (!ctx_name) { 64975c1c2b5SSeongJae Park kfree(kbuf); 65075c1c2b5SSeongJae Park return -ENOMEM; 65175c1c2b5SSeongJae Park } 65275c1c2b5SSeongJae Park 65375c1c2b5SSeongJae Park /* Trim white space */ 65475c1c2b5SSeongJae Park if (sscanf(kbuf, "%s", ctx_name) != 1) { 65575c1c2b5SSeongJae Park ret = -EINVAL; 65675c1c2b5SSeongJae Park goto out; 65775c1c2b5SSeongJae Park } 65875c1c2b5SSeongJae Park 65975c1c2b5SSeongJae Park mutex_lock(&damon_dbgfs_lock); 66075c1c2b5SSeongJae Park err = dbgfs_rm_context(ctx_name); 66175c1c2b5SSeongJae Park if (err) 66275c1c2b5SSeongJae Park ret = err; 66375c1c2b5SSeongJae Park mutex_unlock(&damon_dbgfs_lock); 66475c1c2b5SSeongJae Park 66575c1c2b5SSeongJae Park out: 66675c1c2b5SSeongJae Park kfree(kbuf); 66775c1c2b5SSeongJae Park kfree(ctx_name); 66875c1c2b5SSeongJae Park return ret; 66975c1c2b5SSeongJae Park } 67075c1c2b5SSeongJae Park 6714bc05954SSeongJae Park static ssize_t dbgfs_monitor_on_read(struct file *file, 6724bc05954SSeongJae Park char __user *buf, size_t count, loff_t *ppos) 6734bc05954SSeongJae Park { 6744bc05954SSeongJae Park char monitor_on_buf[5]; 6754bc05954SSeongJae Park bool monitor_on = damon_nr_running_ctxs() != 0; 6764bc05954SSeongJae Park int len; 6774bc05954SSeongJae Park 6784bc05954SSeongJae Park len = scnprintf(monitor_on_buf, 5, monitor_on ? "on\n" : "off\n"); 6794bc05954SSeongJae Park 6804bc05954SSeongJae Park return simple_read_from_buffer(buf, count, ppos, monitor_on_buf, len); 6814bc05954SSeongJae Park } 6824bc05954SSeongJae Park 6834bc05954SSeongJae Park static ssize_t dbgfs_monitor_on_write(struct file *file, 6844bc05954SSeongJae Park const char __user *buf, size_t count, loff_t *ppos) 6854bc05954SSeongJae Park { 6864bc05954SSeongJae Park ssize_t ret = count; 6874bc05954SSeongJae Park char *kbuf; 6884bc05954SSeongJae Park int err; 6894bc05954SSeongJae Park 6904bc05954SSeongJae Park kbuf = user_input_str(buf, count, ppos); 6914bc05954SSeongJae Park if (IS_ERR(kbuf)) 6924bc05954SSeongJae Park return PTR_ERR(kbuf); 6934bc05954SSeongJae Park 6944bc05954SSeongJae Park /* Remove white space */ 6954bc05954SSeongJae Park if (sscanf(kbuf, "%s", kbuf) != 1) { 6964bc05954SSeongJae Park kfree(kbuf); 6974bc05954SSeongJae Park return -EINVAL; 6984bc05954SSeongJae Park } 6994bc05954SSeongJae Park 7004bc05954SSeongJae Park if (!strncmp(kbuf, "on", count)) 7014bc05954SSeongJae Park err = damon_start(dbgfs_ctxs, dbgfs_nr_ctxs); 7024bc05954SSeongJae Park else if (!strncmp(kbuf, "off", count)) 7034bc05954SSeongJae Park err = damon_stop(dbgfs_ctxs, dbgfs_nr_ctxs); 7044bc05954SSeongJae Park else 7054bc05954SSeongJae Park err = -EINVAL; 7064bc05954SSeongJae Park 7074bc05954SSeongJae Park if (err) 7084bc05954SSeongJae Park ret = err; 7094bc05954SSeongJae Park kfree(kbuf); 7104bc05954SSeongJae Park return ret; 7114bc05954SSeongJae Park } 7124bc05954SSeongJae Park 71375c1c2b5SSeongJae Park static const struct file_operations mk_contexts_fops = { 71475c1c2b5SSeongJae Park .write = dbgfs_mk_context_write, 71575c1c2b5SSeongJae Park }; 71675c1c2b5SSeongJae Park 71775c1c2b5SSeongJae Park static const struct file_operations rm_contexts_fops = { 71875c1c2b5SSeongJae Park .write = dbgfs_rm_context_write, 71975c1c2b5SSeongJae Park }; 72075c1c2b5SSeongJae Park 7214bc05954SSeongJae Park static const struct file_operations monitor_on_fops = { 7224bc05954SSeongJae Park .read = dbgfs_monitor_on_read, 7234bc05954SSeongJae Park .write = dbgfs_monitor_on_write, 7244bc05954SSeongJae Park }; 7254bc05954SSeongJae Park 7264bc05954SSeongJae Park static int __init __damon_dbgfs_init(void) 7274bc05954SSeongJae Park { 7284bc05954SSeongJae Park struct dentry *dbgfs_root; 72975c1c2b5SSeongJae Park const char * const file_names[] = {"mk_contexts", "rm_contexts", 73075c1c2b5SSeongJae Park "monitor_on"}; 73175c1c2b5SSeongJae Park const struct file_operations *fops[] = {&mk_contexts_fops, 73275c1c2b5SSeongJae Park &rm_contexts_fops, &monitor_on_fops}; 7334bc05954SSeongJae Park int i; 7344bc05954SSeongJae Park 7354bc05954SSeongJae Park dbgfs_root = debugfs_create_dir("damon", NULL); 7364bc05954SSeongJae Park 7374bc05954SSeongJae Park for (i = 0; i < ARRAY_SIZE(file_names); i++) 7384bc05954SSeongJae Park debugfs_create_file(file_names[i], 0600, dbgfs_root, NULL, 7394bc05954SSeongJae Park fops[i]); 7404bc05954SSeongJae Park dbgfs_fill_ctx_dir(dbgfs_root, dbgfs_ctxs[0]); 7414bc05954SSeongJae Park 7424bc05954SSeongJae Park dbgfs_dirs = kmalloc_array(1, sizeof(dbgfs_root), GFP_KERNEL); 7434bc05954SSeongJae Park if (!dbgfs_dirs) { 7444bc05954SSeongJae Park debugfs_remove(dbgfs_root); 7454bc05954SSeongJae Park return -ENOMEM; 7464bc05954SSeongJae Park } 7474bc05954SSeongJae Park dbgfs_dirs[0] = dbgfs_root; 7484bc05954SSeongJae Park 7494bc05954SSeongJae Park return 0; 7504bc05954SSeongJae Park } 7514bc05954SSeongJae Park 7524bc05954SSeongJae Park /* 7534bc05954SSeongJae Park * Functions for the initialization 7544bc05954SSeongJae Park */ 7554bc05954SSeongJae Park 7564bc05954SSeongJae Park static int __init damon_dbgfs_init(void) 7574bc05954SSeongJae Park { 7584bc05954SSeongJae Park int rc; 7594bc05954SSeongJae Park 7604bc05954SSeongJae Park dbgfs_ctxs = kmalloc(sizeof(*dbgfs_ctxs), GFP_KERNEL); 7614bc05954SSeongJae Park if (!dbgfs_ctxs) 7624bc05954SSeongJae Park return -ENOMEM; 7634bc05954SSeongJae Park dbgfs_ctxs[0] = dbgfs_new_ctx(); 7644bc05954SSeongJae Park if (!dbgfs_ctxs[0]) { 7654bc05954SSeongJae Park kfree(dbgfs_ctxs); 7664bc05954SSeongJae Park return -ENOMEM; 7674bc05954SSeongJae Park } 7684bc05954SSeongJae Park dbgfs_nr_ctxs = 1; 7694bc05954SSeongJae Park 7704bc05954SSeongJae Park rc = __damon_dbgfs_init(); 7714bc05954SSeongJae Park if (rc) { 7724bc05954SSeongJae Park kfree(dbgfs_ctxs[0]); 7734bc05954SSeongJae Park kfree(dbgfs_ctxs); 7744bc05954SSeongJae Park pr_err("%s: dbgfs init failed\n", __func__); 7754bc05954SSeongJae Park } 7764bc05954SSeongJae Park 7774bc05954SSeongJae Park return rc; 7784bc05954SSeongJae Park } 7794bc05954SSeongJae Park 7804bc05954SSeongJae Park module_init(damon_dbgfs_init); 78117ccae8bSSeongJae Park 78217ccae8bSSeongJae Park #include "dbgfs-test.h" 783