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 35db7a347bSSeongJae Park kbuf = kmalloc(count + 1, GFP_KERNEL | __GFP_NOWARN); 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; 729210622aSRongwei Wang ssize_t ret; 734bc05954SSeongJae Park 744bc05954SSeongJae Park kbuf = user_input_str(buf, count, ppos); 754bc05954SSeongJae Park if (IS_ERR(kbuf)) 764bc05954SSeongJae Park return PTR_ERR(kbuf); 774bc05954SSeongJae Park 784bc05954SSeongJae Park if (sscanf(kbuf, "%lu %lu %lu %lu %lu", 794bc05954SSeongJae Park &s, &a, &r, &minr, &maxr) != 5) { 804bc05954SSeongJae Park ret = -EINVAL; 814bc05954SSeongJae Park goto out; 824bc05954SSeongJae Park } 834bc05954SSeongJae Park 844bc05954SSeongJae Park mutex_lock(&ctx->kdamond_lock); 854bc05954SSeongJae Park if (ctx->kdamond) { 864bc05954SSeongJae Park ret = -EBUSY; 874bc05954SSeongJae Park goto unlock_out; 884bc05954SSeongJae Park } 894bc05954SSeongJae Park 909210622aSRongwei Wang ret = damon_set_attrs(ctx, s, a, r, minr, maxr); 919210622aSRongwei Wang if (!ret) 929210622aSRongwei Wang ret = count; 934bc05954SSeongJae Park unlock_out: 944bc05954SSeongJae Park mutex_unlock(&ctx->kdamond_lock); 954bc05954SSeongJae Park out: 964bc05954SSeongJae Park kfree(kbuf); 974bc05954SSeongJae Park return ret; 984bc05954SSeongJae Park } 994bc05954SSeongJae Park 100af122dd8SSeongJae Park static ssize_t sprint_schemes(struct damon_ctx *c, char *buf, ssize_t len) 101af122dd8SSeongJae Park { 102af122dd8SSeongJae Park struct damos *s; 103af122dd8SSeongJae Park int written = 0; 104af122dd8SSeongJae Park int rc; 105af122dd8SSeongJae Park 106af122dd8SSeongJae Park damon_for_each_scheme(s, c) { 107af122dd8SSeongJae Park rc = scnprintf(&buf[written], len - written, 108ae666a6dSSeongJae Park "%lu %lu %u %u %u %u %d %lu %lu %lu %u %u %u %d %lu %lu %lu %lu %lu %lu\n", 109af122dd8SSeongJae Park s->min_sz_region, s->max_sz_region, 110af122dd8SSeongJae Park s->min_nr_accesses, s->max_nr_accesses, 111af122dd8SSeongJae Park s->min_age_region, s->max_age_region, 112d7d0ec85SSeongJae Park s->action, 113d7d0ec85SSeongJae Park s->quota.ms, s->quota.sz, 114d7d0ec85SSeongJae Park s->quota.reset_interval, 115f4a68b4aSSeongJae Park s->quota.weight_sz, 116f4a68b4aSSeongJae Park s->quota.weight_nr_accesses, 117f4a68b4aSSeongJae Park s->quota.weight_age, 118ae666a6dSSeongJae Park s->wmarks.metric, s->wmarks.interval, 119ae666a6dSSeongJae Park s->wmarks.high, s->wmarks.mid, s->wmarks.low, 120d7d0ec85SSeongJae Park s->stat_count, s->stat_sz); 121af122dd8SSeongJae Park if (!rc) 122af122dd8SSeongJae Park return -ENOMEM; 123af122dd8SSeongJae Park 124af122dd8SSeongJae Park written += rc; 125af122dd8SSeongJae Park } 126af122dd8SSeongJae Park return written; 127af122dd8SSeongJae Park } 128af122dd8SSeongJae Park 129af122dd8SSeongJae Park static ssize_t dbgfs_schemes_read(struct file *file, char __user *buf, 130af122dd8SSeongJae Park size_t count, loff_t *ppos) 131af122dd8SSeongJae Park { 132af122dd8SSeongJae Park struct damon_ctx *ctx = file->private_data; 133af122dd8SSeongJae Park char *kbuf; 134af122dd8SSeongJae Park ssize_t len; 135af122dd8SSeongJae Park 136db7a347bSSeongJae Park kbuf = kmalloc(count, GFP_KERNEL | __GFP_NOWARN); 137af122dd8SSeongJae Park if (!kbuf) 138af122dd8SSeongJae Park return -ENOMEM; 139af122dd8SSeongJae Park 140af122dd8SSeongJae Park mutex_lock(&ctx->kdamond_lock); 141af122dd8SSeongJae Park len = sprint_schemes(ctx, kbuf, count); 142af122dd8SSeongJae Park mutex_unlock(&ctx->kdamond_lock); 143af122dd8SSeongJae Park if (len < 0) 144af122dd8SSeongJae Park goto out; 145af122dd8SSeongJae Park len = simple_read_from_buffer(buf, count, ppos, kbuf, len); 146af122dd8SSeongJae Park 147af122dd8SSeongJae Park out: 148af122dd8SSeongJae Park kfree(kbuf); 149af122dd8SSeongJae Park return len; 150af122dd8SSeongJae Park } 151af122dd8SSeongJae Park 152af122dd8SSeongJae Park static void free_schemes_arr(struct damos **schemes, ssize_t nr_schemes) 153af122dd8SSeongJae Park { 154af122dd8SSeongJae Park ssize_t i; 155af122dd8SSeongJae Park 156af122dd8SSeongJae Park for (i = 0; i < nr_schemes; i++) 157af122dd8SSeongJae Park kfree(schemes[i]); 158af122dd8SSeongJae Park kfree(schemes); 159af122dd8SSeongJae Park } 160af122dd8SSeongJae Park 161af122dd8SSeongJae Park static bool damos_action_valid(int action) 162af122dd8SSeongJae Park { 163af122dd8SSeongJae Park switch (action) { 164af122dd8SSeongJae Park case DAMOS_WILLNEED: 165af122dd8SSeongJae Park case DAMOS_COLD: 166af122dd8SSeongJae Park case DAMOS_PAGEOUT: 167af122dd8SSeongJae Park case DAMOS_HUGEPAGE: 168af122dd8SSeongJae Park case DAMOS_NOHUGEPAGE: 1692f0b548cSSeongJae Park case DAMOS_STAT: 170af122dd8SSeongJae Park return true; 171af122dd8SSeongJae Park default: 172af122dd8SSeongJae Park return false; 173af122dd8SSeongJae Park } 174af122dd8SSeongJae Park } 175af122dd8SSeongJae Park 176af122dd8SSeongJae Park /* 177af122dd8SSeongJae Park * Converts a string into an array of struct damos pointers 178af122dd8SSeongJae Park * 179af122dd8SSeongJae Park * Returns an array of struct damos pointers that converted if the conversion 180af122dd8SSeongJae Park * success, or NULL otherwise. 181af122dd8SSeongJae Park */ 182af122dd8SSeongJae Park static struct damos **str_to_schemes(const char *str, ssize_t len, 183af122dd8SSeongJae Park ssize_t *nr_schemes) 184af122dd8SSeongJae Park { 185af122dd8SSeongJae Park struct damos *scheme, **schemes; 186af122dd8SSeongJae Park const int max_nr_schemes = 256; 187af122dd8SSeongJae Park int pos = 0, parsed, ret; 188af122dd8SSeongJae Park unsigned long min_sz, max_sz; 189af122dd8SSeongJae Park unsigned int min_nr_a, max_nr_a, min_age, max_age; 190af122dd8SSeongJae Park unsigned int action; 191af122dd8SSeongJae Park 192af122dd8SSeongJae Park schemes = kmalloc_array(max_nr_schemes, sizeof(scheme), 193af122dd8SSeongJae Park GFP_KERNEL); 194af122dd8SSeongJae Park if (!schemes) 195af122dd8SSeongJae Park return NULL; 196af122dd8SSeongJae Park 197af122dd8SSeongJae Park *nr_schemes = 0; 198af122dd8SSeongJae Park while (pos < len && *nr_schemes < max_nr_schemes) { 1992b8a248dSSeongJae Park struct damos_quota quota = {}; 200ae666a6dSSeongJae Park struct damos_watermarks wmarks; 2012b8a248dSSeongJae Park 202f4a68b4aSSeongJae Park ret = sscanf(&str[pos], 203ae666a6dSSeongJae Park "%lu %lu %u %u %u %u %u %lu %lu %lu %u %u %u %u %lu %lu %lu %lu%n", 204af122dd8SSeongJae Park &min_sz, &max_sz, &min_nr_a, &max_nr_a, 205d7d0ec85SSeongJae Park &min_age, &max_age, &action, "a.ms, 206f4a68b4aSSeongJae Park "a.sz, "a.reset_interval, 207f4a68b4aSSeongJae Park "a.weight_sz, "a.weight_nr_accesses, 208ae666a6dSSeongJae Park "a.weight_age, &wmarks.metric, 209ae666a6dSSeongJae Park &wmarks.interval, &wmarks.high, &wmarks.mid, 210ae666a6dSSeongJae Park &wmarks.low, &parsed); 211ae666a6dSSeongJae Park if (ret != 18) 212af122dd8SSeongJae Park break; 2130bceffa2SSeongJae Park if (!damos_action_valid(action)) 214af122dd8SSeongJae Park goto fail; 215af122dd8SSeongJae Park 216*c89ae63eSXin Hao if (min_sz > max_sz || min_nr_a > max_nr_a || min_age > max_age) 217*c89ae63eSXin Hao goto fail; 218*c89ae63eSXin Hao 219*c89ae63eSXin Hao if (wmarks.high < wmarks.mid || wmarks.high < wmarks.low || 220*c89ae63eSXin Hao wmarks.mid < wmarks.low) 221*c89ae63eSXin Hao goto fail; 222*c89ae63eSXin Hao 223af122dd8SSeongJae Park pos += parsed; 224af122dd8SSeongJae Park scheme = damon_new_scheme(min_sz, max_sz, min_nr_a, max_nr_a, 225ee801b7dSSeongJae Park min_age, max_age, action, "a, &wmarks); 226af122dd8SSeongJae Park if (!scheme) 227af122dd8SSeongJae Park goto fail; 228af122dd8SSeongJae Park 229af122dd8SSeongJae Park schemes[*nr_schemes] = scheme; 230af122dd8SSeongJae Park *nr_schemes += 1; 231af122dd8SSeongJae Park } 232af122dd8SSeongJae Park return schemes; 233af122dd8SSeongJae Park fail: 234af122dd8SSeongJae Park free_schemes_arr(schemes, *nr_schemes); 235af122dd8SSeongJae Park return NULL; 236af122dd8SSeongJae Park } 237af122dd8SSeongJae Park 238af122dd8SSeongJae Park static ssize_t dbgfs_schemes_write(struct file *file, const char __user *buf, 239af122dd8SSeongJae Park size_t count, loff_t *ppos) 240af122dd8SSeongJae Park { 241af122dd8SSeongJae Park struct damon_ctx *ctx = file->private_data; 242af122dd8SSeongJae Park char *kbuf; 243af122dd8SSeongJae Park struct damos **schemes; 2449210622aSRongwei Wang ssize_t nr_schemes = 0, ret; 245af122dd8SSeongJae Park 246af122dd8SSeongJae Park kbuf = user_input_str(buf, count, ppos); 247af122dd8SSeongJae Park if (IS_ERR(kbuf)) 248af122dd8SSeongJae Park return PTR_ERR(kbuf); 249af122dd8SSeongJae Park 2509210622aSRongwei Wang schemes = str_to_schemes(kbuf, count, &nr_schemes); 251af122dd8SSeongJae Park if (!schemes) { 252af122dd8SSeongJae Park ret = -EINVAL; 253af122dd8SSeongJae Park goto out; 254af122dd8SSeongJae Park } 255af122dd8SSeongJae Park 256af122dd8SSeongJae Park mutex_lock(&ctx->kdamond_lock); 257af122dd8SSeongJae Park if (ctx->kdamond) { 258af122dd8SSeongJae Park ret = -EBUSY; 259af122dd8SSeongJae Park goto unlock_out; 260af122dd8SSeongJae Park } 261af122dd8SSeongJae Park 2629210622aSRongwei Wang ret = damon_set_schemes(ctx, schemes, nr_schemes); 2639210622aSRongwei Wang if (!ret) { 2649210622aSRongwei Wang ret = count; 265af122dd8SSeongJae Park nr_schemes = 0; 2669210622aSRongwei Wang } 2679210622aSRongwei Wang 268af122dd8SSeongJae Park unlock_out: 269af122dd8SSeongJae Park mutex_unlock(&ctx->kdamond_lock); 270af122dd8SSeongJae Park free_schemes_arr(schemes, nr_schemes); 271af122dd8SSeongJae Park out: 272af122dd8SSeongJae Park kfree(kbuf); 273af122dd8SSeongJae Park return ret; 274af122dd8SSeongJae Park } 275af122dd8SSeongJae Park 2764bc05954SSeongJae Park static inline bool targetid_is_pid(const struct damon_ctx *ctx) 2774bc05954SSeongJae Park { 2784bc05954SSeongJae Park return ctx->primitive.target_valid == damon_va_target_valid; 2794bc05954SSeongJae Park } 2804bc05954SSeongJae Park 2814bc05954SSeongJae Park static ssize_t sprint_target_ids(struct damon_ctx *ctx, char *buf, ssize_t len) 2824bc05954SSeongJae Park { 2834bc05954SSeongJae Park struct damon_target *t; 2844bc05954SSeongJae Park unsigned long id; 2854bc05954SSeongJae Park int written = 0; 2864bc05954SSeongJae Park int rc; 2874bc05954SSeongJae Park 2884bc05954SSeongJae Park damon_for_each_target(t, ctx) { 2894bc05954SSeongJae Park id = t->id; 2904bc05954SSeongJae Park if (targetid_is_pid(ctx)) 2914bc05954SSeongJae Park /* Show pid numbers to debugfs users */ 2924bc05954SSeongJae Park id = (unsigned long)pid_vnr((struct pid *)id); 2934bc05954SSeongJae Park 2944bc05954SSeongJae Park rc = scnprintf(&buf[written], len - written, "%lu ", id); 2954bc05954SSeongJae Park if (!rc) 2964bc05954SSeongJae Park return -ENOMEM; 2974bc05954SSeongJae Park written += rc; 2984bc05954SSeongJae Park } 2994bc05954SSeongJae Park if (written) 3004bc05954SSeongJae Park written -= 1; 3014bc05954SSeongJae Park written += scnprintf(&buf[written], len - written, "\n"); 3024bc05954SSeongJae Park return written; 3034bc05954SSeongJae Park } 3044bc05954SSeongJae Park 3054bc05954SSeongJae Park static ssize_t dbgfs_target_ids_read(struct file *file, 3064bc05954SSeongJae Park char __user *buf, size_t count, loff_t *ppos) 3074bc05954SSeongJae Park { 3084bc05954SSeongJae Park struct damon_ctx *ctx = file->private_data; 3094bc05954SSeongJae Park ssize_t len; 3104bc05954SSeongJae Park char ids_buf[320]; 3114bc05954SSeongJae Park 3124bc05954SSeongJae Park mutex_lock(&ctx->kdamond_lock); 3134bc05954SSeongJae Park len = sprint_target_ids(ctx, ids_buf, 320); 3144bc05954SSeongJae Park mutex_unlock(&ctx->kdamond_lock); 3154bc05954SSeongJae Park if (len < 0) 3164bc05954SSeongJae Park return len; 3174bc05954SSeongJae Park 3184bc05954SSeongJae Park return simple_read_from_buffer(buf, count, ppos, ids_buf, len); 3194bc05954SSeongJae Park } 3204bc05954SSeongJae Park 3214bc05954SSeongJae Park /* 3224bc05954SSeongJae Park * Converts a string into an array of unsigned long integers 3234bc05954SSeongJae Park * 3244bc05954SSeongJae Park * Returns an array of unsigned long integers if the conversion success, or 3254bc05954SSeongJae Park * NULL otherwise. 3264bc05954SSeongJae Park */ 3274bc05954SSeongJae Park static unsigned long *str_to_target_ids(const char *str, ssize_t len, 3284bc05954SSeongJae Park ssize_t *nr_ids) 3294bc05954SSeongJae Park { 3304bc05954SSeongJae Park unsigned long *ids; 3314bc05954SSeongJae Park const int max_nr_ids = 32; 3324bc05954SSeongJae Park unsigned long id; 3334bc05954SSeongJae Park int pos = 0, parsed, ret; 3344bc05954SSeongJae Park 3354bc05954SSeongJae Park *nr_ids = 0; 3364bc05954SSeongJae Park ids = kmalloc_array(max_nr_ids, sizeof(id), GFP_KERNEL); 3374bc05954SSeongJae Park if (!ids) 3384bc05954SSeongJae Park return NULL; 3394bc05954SSeongJae Park while (*nr_ids < max_nr_ids && pos < len) { 3404bc05954SSeongJae Park ret = sscanf(&str[pos], "%lu%n", &id, &parsed); 3414bc05954SSeongJae Park pos += parsed; 3424bc05954SSeongJae Park if (ret != 1) 3434bc05954SSeongJae Park break; 3444bc05954SSeongJae Park ids[*nr_ids] = id; 3454bc05954SSeongJae Park *nr_ids += 1; 3464bc05954SSeongJae Park } 3474bc05954SSeongJae Park 3484bc05954SSeongJae Park return ids; 3494bc05954SSeongJae Park } 3504bc05954SSeongJae Park 3514bc05954SSeongJae Park static void dbgfs_put_pids(unsigned long *ids, int nr_ids) 3524bc05954SSeongJae Park { 3534bc05954SSeongJae Park int i; 3544bc05954SSeongJae Park 3554bc05954SSeongJae Park for (i = 0; i < nr_ids; i++) 3564bc05954SSeongJae Park put_pid((struct pid *)ids[i]); 3574bc05954SSeongJae Park } 3584bc05954SSeongJae Park 3594bc05954SSeongJae Park static ssize_t dbgfs_target_ids_write(struct file *file, 3604bc05954SSeongJae Park const char __user *buf, size_t count, loff_t *ppos) 3614bc05954SSeongJae Park { 3624bc05954SSeongJae Park struct damon_ctx *ctx = file->private_data; 363ebb3f994SSeongJae Park struct damon_target *t, *next_t; 364c026291aSSeongJae Park bool id_is_pid = true; 3654bc05954SSeongJae Park char *kbuf, *nrs; 3664bc05954SSeongJae Park unsigned long *targets; 3674bc05954SSeongJae Park ssize_t nr_targets; 3689210622aSRongwei Wang ssize_t ret; 3694bc05954SSeongJae Park int i; 3704bc05954SSeongJae Park 3714bc05954SSeongJae Park kbuf = user_input_str(buf, count, ppos); 3724bc05954SSeongJae Park if (IS_ERR(kbuf)) 3734bc05954SSeongJae Park return PTR_ERR(kbuf); 3744bc05954SSeongJae Park 3754bc05954SSeongJae Park nrs = kbuf; 376c026291aSSeongJae Park if (!strncmp(kbuf, "paddr\n", count)) { 377c026291aSSeongJae Park id_is_pid = false; 378c026291aSSeongJae Park /* target id is meaningless here, but we set it just for fun */ 379c026291aSSeongJae Park scnprintf(kbuf, count, "42 "); 380c026291aSSeongJae Park } 3814bc05954SSeongJae Park 3829210622aSRongwei Wang targets = str_to_target_ids(nrs, count, &nr_targets); 3834bc05954SSeongJae Park if (!targets) { 3844bc05954SSeongJae Park ret = -ENOMEM; 3854bc05954SSeongJae Park goto out; 3864bc05954SSeongJae Park } 3874bc05954SSeongJae Park 388c026291aSSeongJae Park if (id_is_pid) { 3894bc05954SSeongJae Park for (i = 0; i < nr_targets; i++) { 3904bc05954SSeongJae Park targets[i] = (unsigned long)find_get_pid( 3914bc05954SSeongJae Park (int)targets[i]); 3924bc05954SSeongJae Park if (!targets[i]) { 3934bc05954SSeongJae Park dbgfs_put_pids(targets, i); 3944bc05954SSeongJae Park ret = -EINVAL; 3954bc05954SSeongJae Park goto free_targets_out; 3964bc05954SSeongJae Park } 3974bc05954SSeongJae Park } 3984bc05954SSeongJae Park } 3994bc05954SSeongJae Park 4004bc05954SSeongJae Park mutex_lock(&ctx->kdamond_lock); 4014bc05954SSeongJae Park if (ctx->kdamond) { 402c026291aSSeongJae Park if (id_is_pid) 4034bc05954SSeongJae Park dbgfs_put_pids(targets, nr_targets); 4044bc05954SSeongJae Park ret = -EBUSY; 4054bc05954SSeongJae Park goto unlock_out; 4064bc05954SSeongJae Park } 4074bc05954SSeongJae Park 408ebb3f994SSeongJae Park /* remove previously set targets */ 409ebb3f994SSeongJae Park damon_for_each_target_safe(t, next_t, ctx) { 410ebb3f994SSeongJae Park if (targetid_is_pid(ctx)) 411ebb3f994SSeongJae Park put_pid((struct pid *)t->id); 412ebb3f994SSeongJae Park damon_destroy_target(t); 413ebb3f994SSeongJae Park } 414c026291aSSeongJae Park 415c026291aSSeongJae Park /* Configure the context for the address space type */ 416c026291aSSeongJae Park if (id_is_pid) 417c026291aSSeongJae Park damon_va_set_primitives(ctx); 418c026291aSSeongJae Park else 419c026291aSSeongJae Park damon_pa_set_primitives(ctx); 420c026291aSSeongJae Park 4219210622aSRongwei Wang ret = damon_set_targets(ctx, targets, nr_targets); 4229210622aSRongwei Wang if (ret) { 423c026291aSSeongJae Park if (id_is_pid) 4244bc05954SSeongJae Park dbgfs_put_pids(targets, nr_targets); 4259210622aSRongwei Wang } else { 4269210622aSRongwei Wang ret = count; 4274bc05954SSeongJae Park } 4284bc05954SSeongJae Park 4294bc05954SSeongJae Park unlock_out: 4304bc05954SSeongJae Park mutex_unlock(&ctx->kdamond_lock); 4314bc05954SSeongJae Park free_targets_out: 4324bc05954SSeongJae Park kfree(targets); 4334bc05954SSeongJae Park out: 4344bc05954SSeongJae Park kfree(kbuf); 4354bc05954SSeongJae Park return ret; 4364bc05954SSeongJae Park } 4374bc05954SSeongJae Park 43890bebce9SSeongJae Park static ssize_t sprint_init_regions(struct damon_ctx *c, char *buf, ssize_t len) 43990bebce9SSeongJae Park { 44090bebce9SSeongJae Park struct damon_target *t; 44190bebce9SSeongJae Park struct damon_region *r; 44290bebce9SSeongJae Park int written = 0; 44390bebce9SSeongJae Park int rc; 44490bebce9SSeongJae Park 44590bebce9SSeongJae Park damon_for_each_target(t, c) { 44690bebce9SSeongJae Park damon_for_each_region(r, t) { 44790bebce9SSeongJae Park rc = scnprintf(&buf[written], len - written, 44890bebce9SSeongJae Park "%lu %lu %lu\n", 44990bebce9SSeongJae Park t->id, r->ar.start, r->ar.end); 45090bebce9SSeongJae Park if (!rc) 45190bebce9SSeongJae Park return -ENOMEM; 45290bebce9SSeongJae Park written += rc; 45390bebce9SSeongJae Park } 45490bebce9SSeongJae Park } 45590bebce9SSeongJae Park return written; 45690bebce9SSeongJae Park } 45790bebce9SSeongJae Park 45890bebce9SSeongJae Park static ssize_t dbgfs_init_regions_read(struct file *file, char __user *buf, 45990bebce9SSeongJae Park size_t count, loff_t *ppos) 46090bebce9SSeongJae Park { 46190bebce9SSeongJae Park struct damon_ctx *ctx = file->private_data; 46290bebce9SSeongJae Park char *kbuf; 46390bebce9SSeongJae Park ssize_t len; 46490bebce9SSeongJae Park 465db7a347bSSeongJae Park kbuf = kmalloc(count, GFP_KERNEL | __GFP_NOWARN); 46690bebce9SSeongJae Park if (!kbuf) 46790bebce9SSeongJae Park return -ENOMEM; 46890bebce9SSeongJae Park 46990bebce9SSeongJae Park mutex_lock(&ctx->kdamond_lock); 47090bebce9SSeongJae Park if (ctx->kdamond) { 47190bebce9SSeongJae Park mutex_unlock(&ctx->kdamond_lock); 47290bebce9SSeongJae Park len = -EBUSY; 47390bebce9SSeongJae Park goto out; 47490bebce9SSeongJae Park } 47590bebce9SSeongJae Park 47690bebce9SSeongJae Park len = sprint_init_regions(ctx, kbuf, count); 47790bebce9SSeongJae Park mutex_unlock(&ctx->kdamond_lock); 47890bebce9SSeongJae Park if (len < 0) 47990bebce9SSeongJae Park goto out; 48090bebce9SSeongJae Park len = simple_read_from_buffer(buf, count, ppos, kbuf, len); 48190bebce9SSeongJae Park 48290bebce9SSeongJae Park out: 48390bebce9SSeongJae Park kfree(kbuf); 48490bebce9SSeongJae Park return len; 48590bebce9SSeongJae Park } 48690bebce9SSeongJae Park 48790bebce9SSeongJae Park static int add_init_region(struct damon_ctx *c, 48890bebce9SSeongJae Park unsigned long target_id, struct damon_addr_range *ar) 48990bebce9SSeongJae Park { 49090bebce9SSeongJae Park struct damon_target *t; 49190bebce9SSeongJae Park struct damon_region *r, *prev; 49290bebce9SSeongJae Park unsigned long id; 49390bebce9SSeongJae Park int rc = -EINVAL; 49490bebce9SSeongJae Park 49590bebce9SSeongJae Park if (ar->start >= ar->end) 49690bebce9SSeongJae Park return -EINVAL; 49790bebce9SSeongJae Park 49890bebce9SSeongJae Park damon_for_each_target(t, c) { 49990bebce9SSeongJae Park id = t->id; 50090bebce9SSeongJae Park if (targetid_is_pid(c)) 50190bebce9SSeongJae Park id = (unsigned long)pid_vnr((struct pid *)id); 50290bebce9SSeongJae Park if (id == target_id) { 50390bebce9SSeongJae Park r = damon_new_region(ar->start, ar->end); 50490bebce9SSeongJae Park if (!r) 50590bebce9SSeongJae Park return -ENOMEM; 50690bebce9SSeongJae Park damon_add_region(r, t); 50790bebce9SSeongJae Park if (damon_nr_regions(t) > 1) { 50890bebce9SSeongJae Park prev = damon_prev_region(r); 50990bebce9SSeongJae Park if (prev->ar.end > r->ar.start) { 51090bebce9SSeongJae Park damon_destroy_region(r, t); 51190bebce9SSeongJae Park return -EINVAL; 51290bebce9SSeongJae Park } 51390bebce9SSeongJae Park } 51490bebce9SSeongJae Park rc = 0; 51590bebce9SSeongJae Park } 51690bebce9SSeongJae Park } 51790bebce9SSeongJae Park return rc; 51890bebce9SSeongJae Park } 51990bebce9SSeongJae Park 52090bebce9SSeongJae Park static int set_init_regions(struct damon_ctx *c, const char *str, ssize_t len) 52190bebce9SSeongJae Park { 52290bebce9SSeongJae Park struct damon_target *t; 52390bebce9SSeongJae Park struct damon_region *r, *next; 52490bebce9SSeongJae Park int pos = 0, parsed, ret; 52590bebce9SSeongJae Park unsigned long target_id; 52690bebce9SSeongJae Park struct damon_addr_range ar; 52790bebce9SSeongJae Park int err; 52890bebce9SSeongJae Park 52990bebce9SSeongJae Park damon_for_each_target(t, c) { 53090bebce9SSeongJae Park damon_for_each_region_safe(r, next, t) 53190bebce9SSeongJae Park damon_destroy_region(r, t); 53290bebce9SSeongJae Park } 53390bebce9SSeongJae Park 53490bebce9SSeongJae Park while (pos < len) { 53590bebce9SSeongJae Park ret = sscanf(&str[pos], "%lu %lu %lu%n", 53690bebce9SSeongJae Park &target_id, &ar.start, &ar.end, &parsed); 53790bebce9SSeongJae Park if (ret != 3) 53890bebce9SSeongJae Park break; 53990bebce9SSeongJae Park err = add_init_region(c, target_id, &ar); 54090bebce9SSeongJae Park if (err) 54190bebce9SSeongJae Park goto fail; 54290bebce9SSeongJae Park pos += parsed; 54390bebce9SSeongJae Park } 54490bebce9SSeongJae Park 54590bebce9SSeongJae Park return 0; 54690bebce9SSeongJae Park 54790bebce9SSeongJae Park fail: 54890bebce9SSeongJae Park damon_for_each_target(t, c) { 54990bebce9SSeongJae Park damon_for_each_region_safe(r, next, t) 55090bebce9SSeongJae Park damon_destroy_region(r, t); 55190bebce9SSeongJae Park } 55290bebce9SSeongJae Park return err; 55390bebce9SSeongJae Park } 55490bebce9SSeongJae Park 55590bebce9SSeongJae Park static ssize_t dbgfs_init_regions_write(struct file *file, 55690bebce9SSeongJae Park const char __user *buf, size_t count, 55790bebce9SSeongJae Park loff_t *ppos) 55890bebce9SSeongJae Park { 55990bebce9SSeongJae Park struct damon_ctx *ctx = file->private_data; 56090bebce9SSeongJae Park char *kbuf; 56190bebce9SSeongJae Park ssize_t ret = count; 56290bebce9SSeongJae Park int err; 56390bebce9SSeongJae Park 56490bebce9SSeongJae Park kbuf = user_input_str(buf, count, ppos); 56590bebce9SSeongJae Park if (IS_ERR(kbuf)) 56690bebce9SSeongJae Park return PTR_ERR(kbuf); 56790bebce9SSeongJae Park 56890bebce9SSeongJae Park mutex_lock(&ctx->kdamond_lock); 56990bebce9SSeongJae Park if (ctx->kdamond) { 57090bebce9SSeongJae Park ret = -EBUSY; 57190bebce9SSeongJae Park goto unlock_out; 57290bebce9SSeongJae Park } 57390bebce9SSeongJae Park 57490bebce9SSeongJae Park err = set_init_regions(ctx, kbuf, ret); 57590bebce9SSeongJae Park if (err) 57690bebce9SSeongJae Park ret = err; 57790bebce9SSeongJae Park 57890bebce9SSeongJae Park unlock_out: 57990bebce9SSeongJae Park mutex_unlock(&ctx->kdamond_lock); 58090bebce9SSeongJae Park kfree(kbuf); 58190bebce9SSeongJae Park return ret; 58290bebce9SSeongJae Park } 58390bebce9SSeongJae Park 584429538e8SSeongJae Park static ssize_t dbgfs_kdamond_pid_read(struct file *file, 585429538e8SSeongJae Park char __user *buf, size_t count, loff_t *ppos) 586429538e8SSeongJae Park { 587429538e8SSeongJae Park struct damon_ctx *ctx = file->private_data; 588429538e8SSeongJae Park char *kbuf; 589429538e8SSeongJae Park ssize_t len; 590429538e8SSeongJae Park 591db7a347bSSeongJae Park kbuf = kmalloc(count, GFP_KERNEL | __GFP_NOWARN); 592429538e8SSeongJae Park if (!kbuf) 593429538e8SSeongJae Park return -ENOMEM; 594429538e8SSeongJae Park 595429538e8SSeongJae Park mutex_lock(&ctx->kdamond_lock); 596429538e8SSeongJae Park if (ctx->kdamond) 597429538e8SSeongJae Park len = scnprintf(kbuf, count, "%d\n", ctx->kdamond->pid); 598429538e8SSeongJae Park else 599429538e8SSeongJae Park len = scnprintf(kbuf, count, "none\n"); 600429538e8SSeongJae Park mutex_unlock(&ctx->kdamond_lock); 601429538e8SSeongJae Park if (!len) 602429538e8SSeongJae Park goto out; 603429538e8SSeongJae Park len = simple_read_from_buffer(buf, count, ppos, kbuf, len); 604429538e8SSeongJae Park 605429538e8SSeongJae Park out: 606429538e8SSeongJae Park kfree(kbuf); 607429538e8SSeongJae Park return len; 608429538e8SSeongJae Park } 609429538e8SSeongJae Park 6104bc05954SSeongJae Park static int damon_dbgfs_open(struct inode *inode, struct file *file) 6114bc05954SSeongJae Park { 6124bc05954SSeongJae Park file->private_data = inode->i_private; 6134bc05954SSeongJae Park 6144bc05954SSeongJae Park return nonseekable_open(inode, file); 6154bc05954SSeongJae Park } 6164bc05954SSeongJae Park 6174bc05954SSeongJae Park static const struct file_operations attrs_fops = { 6184bc05954SSeongJae Park .open = damon_dbgfs_open, 6194bc05954SSeongJae Park .read = dbgfs_attrs_read, 6204bc05954SSeongJae Park .write = dbgfs_attrs_write, 6214bc05954SSeongJae Park }; 6224bc05954SSeongJae Park 623af122dd8SSeongJae Park static const struct file_operations schemes_fops = { 624af122dd8SSeongJae Park .open = damon_dbgfs_open, 625af122dd8SSeongJae Park .read = dbgfs_schemes_read, 626af122dd8SSeongJae Park .write = dbgfs_schemes_write, 627af122dd8SSeongJae Park }; 628af122dd8SSeongJae Park 6294bc05954SSeongJae Park static const struct file_operations target_ids_fops = { 6304bc05954SSeongJae Park .open = damon_dbgfs_open, 6314bc05954SSeongJae Park .read = dbgfs_target_ids_read, 6324bc05954SSeongJae Park .write = dbgfs_target_ids_write, 6334bc05954SSeongJae Park }; 6344bc05954SSeongJae Park 63590bebce9SSeongJae Park static const struct file_operations init_regions_fops = { 63690bebce9SSeongJae Park .open = damon_dbgfs_open, 63790bebce9SSeongJae Park .read = dbgfs_init_regions_read, 63890bebce9SSeongJae Park .write = dbgfs_init_regions_write, 63990bebce9SSeongJae Park }; 64090bebce9SSeongJae Park 641429538e8SSeongJae Park static const struct file_operations kdamond_pid_fops = { 642429538e8SSeongJae Park .open = damon_dbgfs_open, 643429538e8SSeongJae Park .read = dbgfs_kdamond_pid_read, 644429538e8SSeongJae Park }; 645429538e8SSeongJae Park 6464bc05954SSeongJae Park static void dbgfs_fill_ctx_dir(struct dentry *dir, struct damon_ctx *ctx) 6474bc05954SSeongJae Park { 648af122dd8SSeongJae Park const char * const file_names[] = {"attrs", "schemes", "target_ids", 64990bebce9SSeongJae Park "init_regions", "kdamond_pid"}; 650af122dd8SSeongJae Park const struct file_operations *fops[] = {&attrs_fops, &schemes_fops, 65190bebce9SSeongJae Park &target_ids_fops, &init_regions_fops, &kdamond_pid_fops}; 6524bc05954SSeongJae Park int i; 6534bc05954SSeongJae Park 6544bc05954SSeongJae Park for (i = 0; i < ARRAY_SIZE(file_names); i++) 6554bc05954SSeongJae Park debugfs_create_file(file_names[i], 0600, dir, ctx, fops[i]); 6564bc05954SSeongJae Park } 6574bc05954SSeongJae Park 658658f9ae7SChangbin Du static void dbgfs_before_terminate(struct damon_ctx *ctx) 6594bc05954SSeongJae Park { 6604bc05954SSeongJae Park struct damon_target *t, *next; 6614bc05954SSeongJae Park 6624bc05954SSeongJae Park if (!targetid_is_pid(ctx)) 663658f9ae7SChangbin Du return; 6644bc05954SSeongJae Park 66534796417SSeongJae Park mutex_lock(&ctx->kdamond_lock); 6664bc05954SSeongJae Park damon_for_each_target_safe(t, next, ctx) { 6674bc05954SSeongJae Park put_pid((struct pid *)t->id); 6684bc05954SSeongJae Park damon_destroy_target(t); 6694bc05954SSeongJae Park } 67034796417SSeongJae Park mutex_unlock(&ctx->kdamond_lock); 6714bc05954SSeongJae Park } 6724bc05954SSeongJae Park 6734bc05954SSeongJae Park static struct damon_ctx *dbgfs_new_ctx(void) 6744bc05954SSeongJae Park { 6754bc05954SSeongJae Park struct damon_ctx *ctx; 6764bc05954SSeongJae Park 6774bc05954SSeongJae Park ctx = damon_new_ctx(); 6784bc05954SSeongJae Park if (!ctx) 6794bc05954SSeongJae Park return NULL; 6804bc05954SSeongJae Park 6814bc05954SSeongJae Park damon_va_set_primitives(ctx); 6824bc05954SSeongJae Park ctx->callback.before_terminate = dbgfs_before_terminate; 6834bc05954SSeongJae Park return ctx; 6844bc05954SSeongJae Park } 6854bc05954SSeongJae Park 68675c1c2b5SSeongJae Park static void dbgfs_destroy_ctx(struct damon_ctx *ctx) 68775c1c2b5SSeongJae Park { 68875c1c2b5SSeongJae Park damon_destroy_ctx(ctx); 68975c1c2b5SSeongJae Park } 69075c1c2b5SSeongJae Park 69175c1c2b5SSeongJae Park /* 69275c1c2b5SSeongJae Park * Make a context of @name and create a debugfs directory for it. 69375c1c2b5SSeongJae Park * 69475c1c2b5SSeongJae Park * This function should be called while holding damon_dbgfs_lock. 69575c1c2b5SSeongJae Park * 69675c1c2b5SSeongJae Park * Returns 0 on success, negative error code otherwise. 69775c1c2b5SSeongJae Park */ 69875c1c2b5SSeongJae Park static int dbgfs_mk_context(char *name) 69975c1c2b5SSeongJae Park { 70075c1c2b5SSeongJae Park struct dentry *root, **new_dirs, *new_dir; 70175c1c2b5SSeongJae Park struct damon_ctx **new_ctxs, *new_ctx; 70275c1c2b5SSeongJae Park 70375c1c2b5SSeongJae Park if (damon_nr_running_ctxs()) 70475c1c2b5SSeongJae Park return -EBUSY; 70575c1c2b5SSeongJae Park 70675c1c2b5SSeongJae Park new_ctxs = krealloc(dbgfs_ctxs, sizeof(*dbgfs_ctxs) * 70775c1c2b5SSeongJae Park (dbgfs_nr_ctxs + 1), GFP_KERNEL); 70875c1c2b5SSeongJae Park if (!new_ctxs) 70975c1c2b5SSeongJae Park return -ENOMEM; 71075c1c2b5SSeongJae Park dbgfs_ctxs = new_ctxs; 71175c1c2b5SSeongJae Park 71275c1c2b5SSeongJae Park new_dirs = krealloc(dbgfs_dirs, sizeof(*dbgfs_dirs) * 71375c1c2b5SSeongJae Park (dbgfs_nr_ctxs + 1), GFP_KERNEL); 71475c1c2b5SSeongJae Park if (!new_dirs) 71575c1c2b5SSeongJae Park return -ENOMEM; 71675c1c2b5SSeongJae Park dbgfs_dirs = new_dirs; 71775c1c2b5SSeongJae Park 71875c1c2b5SSeongJae Park root = dbgfs_dirs[0]; 71975c1c2b5SSeongJae Park if (!root) 72075c1c2b5SSeongJae Park return -ENOENT; 72175c1c2b5SSeongJae Park 72275c1c2b5SSeongJae Park new_dir = debugfs_create_dir(name, root); 72375c1c2b5SSeongJae Park dbgfs_dirs[dbgfs_nr_ctxs] = new_dir; 72475c1c2b5SSeongJae Park 72575c1c2b5SSeongJae Park new_ctx = dbgfs_new_ctx(); 72675c1c2b5SSeongJae Park if (!new_ctx) { 72775c1c2b5SSeongJae Park debugfs_remove(new_dir); 72875c1c2b5SSeongJae Park dbgfs_dirs[dbgfs_nr_ctxs] = NULL; 72975c1c2b5SSeongJae Park return -ENOMEM; 73075c1c2b5SSeongJae Park } 73175c1c2b5SSeongJae Park 73275c1c2b5SSeongJae Park dbgfs_ctxs[dbgfs_nr_ctxs] = new_ctx; 73375c1c2b5SSeongJae Park dbgfs_fill_ctx_dir(dbgfs_dirs[dbgfs_nr_ctxs], 73475c1c2b5SSeongJae Park dbgfs_ctxs[dbgfs_nr_ctxs]); 73575c1c2b5SSeongJae Park dbgfs_nr_ctxs++; 73675c1c2b5SSeongJae Park 73775c1c2b5SSeongJae Park return 0; 73875c1c2b5SSeongJae Park } 73975c1c2b5SSeongJae Park 74075c1c2b5SSeongJae Park static ssize_t dbgfs_mk_context_write(struct file *file, 74175c1c2b5SSeongJae Park const char __user *buf, size_t count, loff_t *ppos) 74275c1c2b5SSeongJae Park { 74375c1c2b5SSeongJae Park char *kbuf; 74475c1c2b5SSeongJae Park char *ctx_name; 7459210622aSRongwei Wang ssize_t ret; 74675c1c2b5SSeongJae Park 74775c1c2b5SSeongJae Park kbuf = user_input_str(buf, count, ppos); 74875c1c2b5SSeongJae Park if (IS_ERR(kbuf)) 74975c1c2b5SSeongJae Park return PTR_ERR(kbuf); 75075c1c2b5SSeongJae Park ctx_name = kmalloc(count + 1, GFP_KERNEL); 75175c1c2b5SSeongJae Park if (!ctx_name) { 75275c1c2b5SSeongJae Park kfree(kbuf); 75375c1c2b5SSeongJae Park return -ENOMEM; 75475c1c2b5SSeongJae Park } 75575c1c2b5SSeongJae Park 75675c1c2b5SSeongJae Park /* Trim white space */ 75775c1c2b5SSeongJae Park if (sscanf(kbuf, "%s", ctx_name) != 1) { 75875c1c2b5SSeongJae Park ret = -EINVAL; 75975c1c2b5SSeongJae Park goto out; 76075c1c2b5SSeongJae Park } 76175c1c2b5SSeongJae Park 76275c1c2b5SSeongJae Park mutex_lock(&damon_dbgfs_lock); 7639210622aSRongwei Wang ret = dbgfs_mk_context(ctx_name); 7649210622aSRongwei Wang if (!ret) 7659210622aSRongwei Wang ret = count; 76675c1c2b5SSeongJae Park mutex_unlock(&damon_dbgfs_lock); 76775c1c2b5SSeongJae Park 76875c1c2b5SSeongJae Park out: 76975c1c2b5SSeongJae Park kfree(kbuf); 77075c1c2b5SSeongJae Park kfree(ctx_name); 77175c1c2b5SSeongJae Park return ret; 77275c1c2b5SSeongJae Park } 77375c1c2b5SSeongJae Park 77475c1c2b5SSeongJae Park /* 77575c1c2b5SSeongJae Park * Remove a context of @name and its debugfs directory. 77675c1c2b5SSeongJae Park * 77775c1c2b5SSeongJae Park * This function should be called while holding damon_dbgfs_lock. 77875c1c2b5SSeongJae Park * 77975c1c2b5SSeongJae Park * Return 0 on success, negative error code otherwise. 78075c1c2b5SSeongJae Park */ 78175c1c2b5SSeongJae Park static int dbgfs_rm_context(char *name) 78275c1c2b5SSeongJae Park { 78375c1c2b5SSeongJae Park struct dentry *root, *dir, **new_dirs; 78475c1c2b5SSeongJae Park struct damon_ctx **new_ctxs; 78575c1c2b5SSeongJae Park int i, j; 78675c1c2b5SSeongJae Park 78775c1c2b5SSeongJae Park if (damon_nr_running_ctxs()) 78875c1c2b5SSeongJae Park return -EBUSY; 78975c1c2b5SSeongJae Park 79075c1c2b5SSeongJae Park root = dbgfs_dirs[0]; 79175c1c2b5SSeongJae Park if (!root) 79275c1c2b5SSeongJae Park return -ENOENT; 79375c1c2b5SSeongJae Park 79475c1c2b5SSeongJae Park dir = debugfs_lookup(name, root); 79575c1c2b5SSeongJae Park if (!dir) 79675c1c2b5SSeongJae Park return -ENOENT; 79775c1c2b5SSeongJae Park 79875c1c2b5SSeongJae Park new_dirs = kmalloc_array(dbgfs_nr_ctxs - 1, sizeof(*dbgfs_dirs), 79975c1c2b5SSeongJae Park GFP_KERNEL); 80075c1c2b5SSeongJae Park if (!new_dirs) 80175c1c2b5SSeongJae Park return -ENOMEM; 80275c1c2b5SSeongJae Park 80375c1c2b5SSeongJae Park new_ctxs = kmalloc_array(dbgfs_nr_ctxs - 1, sizeof(*dbgfs_ctxs), 80475c1c2b5SSeongJae Park GFP_KERNEL); 80575c1c2b5SSeongJae Park if (!new_ctxs) { 80675c1c2b5SSeongJae Park kfree(new_dirs); 80775c1c2b5SSeongJae Park return -ENOMEM; 80875c1c2b5SSeongJae Park } 80975c1c2b5SSeongJae Park 81075c1c2b5SSeongJae Park for (i = 0, j = 0; i < dbgfs_nr_ctxs; i++) { 81175c1c2b5SSeongJae Park if (dbgfs_dirs[i] == dir) { 81275c1c2b5SSeongJae Park debugfs_remove(dbgfs_dirs[i]); 81375c1c2b5SSeongJae Park dbgfs_destroy_ctx(dbgfs_ctxs[i]); 81475c1c2b5SSeongJae Park continue; 81575c1c2b5SSeongJae Park } 81675c1c2b5SSeongJae Park new_dirs[j] = dbgfs_dirs[i]; 81775c1c2b5SSeongJae Park new_ctxs[j++] = dbgfs_ctxs[i]; 81875c1c2b5SSeongJae Park } 81975c1c2b5SSeongJae Park 82075c1c2b5SSeongJae Park kfree(dbgfs_dirs); 82175c1c2b5SSeongJae Park kfree(dbgfs_ctxs); 82275c1c2b5SSeongJae Park 82375c1c2b5SSeongJae Park dbgfs_dirs = new_dirs; 82475c1c2b5SSeongJae Park dbgfs_ctxs = new_ctxs; 82575c1c2b5SSeongJae Park dbgfs_nr_ctxs--; 82675c1c2b5SSeongJae Park 82775c1c2b5SSeongJae Park return 0; 82875c1c2b5SSeongJae Park } 82975c1c2b5SSeongJae Park 83075c1c2b5SSeongJae Park static ssize_t dbgfs_rm_context_write(struct file *file, 83175c1c2b5SSeongJae Park const char __user *buf, size_t count, loff_t *ppos) 83275c1c2b5SSeongJae Park { 83375c1c2b5SSeongJae Park char *kbuf; 8349210622aSRongwei Wang ssize_t ret; 83575c1c2b5SSeongJae Park char *ctx_name; 83675c1c2b5SSeongJae Park 83775c1c2b5SSeongJae Park kbuf = user_input_str(buf, count, ppos); 83875c1c2b5SSeongJae Park if (IS_ERR(kbuf)) 83975c1c2b5SSeongJae Park return PTR_ERR(kbuf); 84075c1c2b5SSeongJae Park ctx_name = kmalloc(count + 1, GFP_KERNEL); 84175c1c2b5SSeongJae Park if (!ctx_name) { 84275c1c2b5SSeongJae Park kfree(kbuf); 84375c1c2b5SSeongJae Park return -ENOMEM; 84475c1c2b5SSeongJae Park } 84575c1c2b5SSeongJae Park 84675c1c2b5SSeongJae Park /* Trim white space */ 84775c1c2b5SSeongJae Park if (sscanf(kbuf, "%s", ctx_name) != 1) { 84875c1c2b5SSeongJae Park ret = -EINVAL; 84975c1c2b5SSeongJae Park goto out; 85075c1c2b5SSeongJae Park } 85175c1c2b5SSeongJae Park 85275c1c2b5SSeongJae Park mutex_lock(&damon_dbgfs_lock); 8539210622aSRongwei Wang ret = dbgfs_rm_context(ctx_name); 8549210622aSRongwei Wang if (!ret) 8559210622aSRongwei Wang ret = count; 85675c1c2b5SSeongJae Park mutex_unlock(&damon_dbgfs_lock); 85775c1c2b5SSeongJae Park 85875c1c2b5SSeongJae Park out: 85975c1c2b5SSeongJae Park kfree(kbuf); 86075c1c2b5SSeongJae Park kfree(ctx_name); 86175c1c2b5SSeongJae Park return ret; 86275c1c2b5SSeongJae Park } 86375c1c2b5SSeongJae Park 8644bc05954SSeongJae Park static ssize_t dbgfs_monitor_on_read(struct file *file, 8654bc05954SSeongJae Park char __user *buf, size_t count, loff_t *ppos) 8664bc05954SSeongJae Park { 8674bc05954SSeongJae Park char monitor_on_buf[5]; 8684bc05954SSeongJae Park bool monitor_on = damon_nr_running_ctxs() != 0; 8694bc05954SSeongJae Park int len; 8704bc05954SSeongJae Park 8714bc05954SSeongJae Park len = scnprintf(monitor_on_buf, 5, monitor_on ? "on\n" : "off\n"); 8724bc05954SSeongJae Park 8734bc05954SSeongJae Park return simple_read_from_buffer(buf, count, ppos, monitor_on_buf, len); 8744bc05954SSeongJae Park } 8754bc05954SSeongJae Park 8764bc05954SSeongJae Park static ssize_t dbgfs_monitor_on_write(struct file *file, 8774bc05954SSeongJae Park const char __user *buf, size_t count, loff_t *ppos) 8784bc05954SSeongJae Park { 8799210622aSRongwei Wang ssize_t ret; 8804bc05954SSeongJae Park char *kbuf; 8814bc05954SSeongJae Park 8824bc05954SSeongJae Park kbuf = user_input_str(buf, count, ppos); 8834bc05954SSeongJae Park if (IS_ERR(kbuf)) 8844bc05954SSeongJae Park return PTR_ERR(kbuf); 8854bc05954SSeongJae Park 8864bc05954SSeongJae Park /* Remove white space */ 8874bc05954SSeongJae Park if (sscanf(kbuf, "%s", kbuf) != 1) { 8884bc05954SSeongJae Park kfree(kbuf); 8894bc05954SSeongJae Park return -EINVAL; 8904bc05954SSeongJae Park } 8914bc05954SSeongJae Park 892d78f3853SSeongJae Park mutex_lock(&damon_dbgfs_lock); 893b5ca3e83SXin Hao if (!strncmp(kbuf, "on", count)) { 894b5ca3e83SXin Hao int i; 895b5ca3e83SXin Hao 896b5ca3e83SXin Hao for (i = 0; i < dbgfs_nr_ctxs; i++) { 897b5ca3e83SXin Hao if (damon_targets_empty(dbgfs_ctxs[i])) { 898b5ca3e83SXin Hao kfree(kbuf); 899d78f3853SSeongJae Park mutex_unlock(&damon_dbgfs_lock); 900b5ca3e83SXin Hao return -EINVAL; 901b5ca3e83SXin Hao } 902b5ca3e83SXin Hao } 9039210622aSRongwei Wang ret = damon_start(dbgfs_ctxs, dbgfs_nr_ctxs); 904b5ca3e83SXin Hao } else if (!strncmp(kbuf, "off", count)) { 9059210622aSRongwei Wang ret = damon_stop(dbgfs_ctxs, dbgfs_nr_ctxs); 906b5ca3e83SXin Hao } else { 9079210622aSRongwei Wang ret = -EINVAL; 908b5ca3e83SXin Hao } 909d78f3853SSeongJae Park mutex_unlock(&damon_dbgfs_lock); 9104bc05954SSeongJae Park 9119210622aSRongwei Wang if (!ret) 9129210622aSRongwei Wang ret = count; 9134bc05954SSeongJae Park kfree(kbuf); 9144bc05954SSeongJae Park return ret; 9154bc05954SSeongJae Park } 9164bc05954SSeongJae Park 91775c1c2b5SSeongJae Park static const struct file_operations mk_contexts_fops = { 91875c1c2b5SSeongJae Park .write = dbgfs_mk_context_write, 91975c1c2b5SSeongJae Park }; 92075c1c2b5SSeongJae Park 92175c1c2b5SSeongJae Park static const struct file_operations rm_contexts_fops = { 92275c1c2b5SSeongJae Park .write = dbgfs_rm_context_write, 92375c1c2b5SSeongJae Park }; 92475c1c2b5SSeongJae Park 9254bc05954SSeongJae Park static const struct file_operations monitor_on_fops = { 9264bc05954SSeongJae Park .read = dbgfs_monitor_on_read, 9274bc05954SSeongJae Park .write = dbgfs_monitor_on_write, 9284bc05954SSeongJae Park }; 9294bc05954SSeongJae Park 9304bc05954SSeongJae Park static int __init __damon_dbgfs_init(void) 9314bc05954SSeongJae Park { 9324bc05954SSeongJae Park struct dentry *dbgfs_root; 93375c1c2b5SSeongJae Park const char * const file_names[] = {"mk_contexts", "rm_contexts", 93475c1c2b5SSeongJae Park "monitor_on"}; 93575c1c2b5SSeongJae Park const struct file_operations *fops[] = {&mk_contexts_fops, 93675c1c2b5SSeongJae Park &rm_contexts_fops, &monitor_on_fops}; 9374bc05954SSeongJae Park int i; 9384bc05954SSeongJae Park 9394bc05954SSeongJae Park dbgfs_root = debugfs_create_dir("damon", NULL); 9404bc05954SSeongJae Park 9414bc05954SSeongJae Park for (i = 0; i < ARRAY_SIZE(file_names); i++) 9424bc05954SSeongJae Park debugfs_create_file(file_names[i], 0600, dbgfs_root, NULL, 9434bc05954SSeongJae Park fops[i]); 9444bc05954SSeongJae Park dbgfs_fill_ctx_dir(dbgfs_root, dbgfs_ctxs[0]); 9454bc05954SSeongJae Park 9464bc05954SSeongJae Park dbgfs_dirs = kmalloc_array(1, sizeof(dbgfs_root), GFP_KERNEL); 9474bc05954SSeongJae Park if (!dbgfs_dirs) { 9484bc05954SSeongJae Park debugfs_remove(dbgfs_root); 9494bc05954SSeongJae Park return -ENOMEM; 9504bc05954SSeongJae Park } 9514bc05954SSeongJae Park dbgfs_dirs[0] = dbgfs_root; 9524bc05954SSeongJae Park 9534bc05954SSeongJae Park return 0; 9544bc05954SSeongJae Park } 9554bc05954SSeongJae Park 9564bc05954SSeongJae Park /* 9574bc05954SSeongJae Park * Functions for the initialization 9584bc05954SSeongJae Park */ 9594bc05954SSeongJae Park 9604bc05954SSeongJae Park static int __init damon_dbgfs_init(void) 9614bc05954SSeongJae Park { 962d78f3853SSeongJae Park int rc = -ENOMEM; 9634bc05954SSeongJae Park 964d78f3853SSeongJae Park mutex_lock(&damon_dbgfs_lock); 9654bc05954SSeongJae Park dbgfs_ctxs = kmalloc(sizeof(*dbgfs_ctxs), GFP_KERNEL); 9664bc05954SSeongJae Park if (!dbgfs_ctxs) 967d78f3853SSeongJae Park goto out; 9684bc05954SSeongJae Park dbgfs_ctxs[0] = dbgfs_new_ctx(); 9694bc05954SSeongJae Park if (!dbgfs_ctxs[0]) { 9704bc05954SSeongJae Park kfree(dbgfs_ctxs); 971d78f3853SSeongJae Park goto out; 9724bc05954SSeongJae Park } 9734bc05954SSeongJae Park dbgfs_nr_ctxs = 1; 9744bc05954SSeongJae Park 9754bc05954SSeongJae Park rc = __damon_dbgfs_init(); 9764bc05954SSeongJae Park if (rc) { 9774bc05954SSeongJae Park kfree(dbgfs_ctxs[0]); 9784bc05954SSeongJae Park kfree(dbgfs_ctxs); 9794bc05954SSeongJae Park pr_err("%s: dbgfs init failed\n", __func__); 9804bc05954SSeongJae Park } 9814bc05954SSeongJae Park 982d78f3853SSeongJae Park out: 983d78f3853SSeongJae Park mutex_unlock(&damon_dbgfs_lock); 9844bc05954SSeongJae Park return rc; 9854bc05954SSeongJae Park } 9864bc05954SSeongJae Park 9874bc05954SSeongJae Park module_init(damon_dbgfs_init); 98817ccae8bSSeongJae Park 98917ccae8bSSeongJae Park #include "dbgfs-test.h" 990