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