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, 1083a619fdbSSeongJae Park "%lu %lu %u %u %u %u %d %lu %lu %lu %u %u %u %d %lu %lu %lu %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, 1203a619fdbSSeongJae Park s->stat.nr_tried, s->stat.sz_tried, 1213a619fdbSSeongJae Park s->stat.nr_applied, s->stat.sz_applied, 1223a619fdbSSeongJae Park s->stat.qt_exceeds); 123af122dd8SSeongJae Park if (!rc) 124af122dd8SSeongJae Park return -ENOMEM; 125af122dd8SSeongJae Park 126af122dd8SSeongJae Park written += rc; 127af122dd8SSeongJae Park } 128af122dd8SSeongJae Park return written; 129af122dd8SSeongJae Park } 130af122dd8SSeongJae Park 131af122dd8SSeongJae Park static ssize_t dbgfs_schemes_read(struct file *file, char __user *buf, 132af122dd8SSeongJae Park size_t count, loff_t *ppos) 133af122dd8SSeongJae Park { 134af122dd8SSeongJae Park struct damon_ctx *ctx = file->private_data; 135af122dd8SSeongJae Park char *kbuf; 136af122dd8SSeongJae Park ssize_t len; 137af122dd8SSeongJae Park 138db7a347bSSeongJae Park kbuf = kmalloc(count, GFP_KERNEL | __GFP_NOWARN); 139af122dd8SSeongJae Park if (!kbuf) 140af122dd8SSeongJae Park return -ENOMEM; 141af122dd8SSeongJae Park 142af122dd8SSeongJae Park mutex_lock(&ctx->kdamond_lock); 143af122dd8SSeongJae Park len = sprint_schemes(ctx, kbuf, count); 144af122dd8SSeongJae Park mutex_unlock(&ctx->kdamond_lock); 145af122dd8SSeongJae Park if (len < 0) 146af122dd8SSeongJae Park goto out; 147af122dd8SSeongJae Park len = simple_read_from_buffer(buf, count, ppos, kbuf, len); 148af122dd8SSeongJae Park 149af122dd8SSeongJae Park out: 150af122dd8SSeongJae Park kfree(kbuf); 151af122dd8SSeongJae Park return len; 152af122dd8SSeongJae Park } 153af122dd8SSeongJae Park 154af122dd8SSeongJae Park static void free_schemes_arr(struct damos **schemes, ssize_t nr_schemes) 155af122dd8SSeongJae Park { 156af122dd8SSeongJae Park ssize_t i; 157af122dd8SSeongJae Park 158af122dd8SSeongJae Park for (i = 0; i < nr_schemes; i++) 159af122dd8SSeongJae Park kfree(schemes[i]); 160af122dd8SSeongJae Park kfree(schemes); 161af122dd8SSeongJae Park } 162af122dd8SSeongJae Park 163af122dd8SSeongJae Park static bool damos_action_valid(int action) 164af122dd8SSeongJae Park { 165af122dd8SSeongJae Park switch (action) { 166af122dd8SSeongJae Park case DAMOS_WILLNEED: 167af122dd8SSeongJae Park case DAMOS_COLD: 168af122dd8SSeongJae Park case DAMOS_PAGEOUT: 169af122dd8SSeongJae Park case DAMOS_HUGEPAGE: 170af122dd8SSeongJae Park case DAMOS_NOHUGEPAGE: 1712f0b548cSSeongJae Park case DAMOS_STAT: 172af122dd8SSeongJae Park return true; 173af122dd8SSeongJae Park default: 174af122dd8SSeongJae Park return false; 175af122dd8SSeongJae Park } 176af122dd8SSeongJae Park } 177af122dd8SSeongJae Park 178af122dd8SSeongJae Park /* 179af122dd8SSeongJae Park * Converts a string into an array of struct damos pointers 180af122dd8SSeongJae Park * 181af122dd8SSeongJae Park * Returns an array of struct damos pointers that converted if the conversion 182af122dd8SSeongJae Park * success, or NULL otherwise. 183af122dd8SSeongJae Park */ 184af122dd8SSeongJae Park static struct damos **str_to_schemes(const char *str, ssize_t len, 185af122dd8SSeongJae Park ssize_t *nr_schemes) 186af122dd8SSeongJae Park { 187af122dd8SSeongJae Park struct damos *scheme, **schemes; 188af122dd8SSeongJae Park const int max_nr_schemes = 256; 189af122dd8SSeongJae Park int pos = 0, parsed, ret; 190af122dd8SSeongJae Park unsigned long min_sz, max_sz; 191af122dd8SSeongJae Park unsigned int min_nr_a, max_nr_a, min_age, max_age; 192af122dd8SSeongJae Park unsigned int action; 193af122dd8SSeongJae Park 194af122dd8SSeongJae Park schemes = kmalloc_array(max_nr_schemes, sizeof(scheme), 195af122dd8SSeongJae Park GFP_KERNEL); 196af122dd8SSeongJae Park if (!schemes) 197af122dd8SSeongJae Park return NULL; 198af122dd8SSeongJae Park 199af122dd8SSeongJae Park *nr_schemes = 0; 200af122dd8SSeongJae Park while (pos < len && *nr_schemes < max_nr_schemes) { 2012b8a248dSSeongJae Park struct damos_quota quota = {}; 202ae666a6dSSeongJae Park struct damos_watermarks wmarks; 2032b8a248dSSeongJae Park 204f4a68b4aSSeongJae Park ret = sscanf(&str[pos], 205ae666a6dSSeongJae Park "%lu %lu %u %u %u %u %u %lu %lu %lu %u %u %u %u %lu %lu %lu %lu%n", 206af122dd8SSeongJae Park &min_sz, &max_sz, &min_nr_a, &max_nr_a, 207d7d0ec85SSeongJae Park &min_age, &max_age, &action, "a.ms, 208f4a68b4aSSeongJae Park "a.sz, "a.reset_interval, 209f4a68b4aSSeongJae Park "a.weight_sz, "a.weight_nr_accesses, 210ae666a6dSSeongJae Park "a.weight_age, &wmarks.metric, 211ae666a6dSSeongJae Park &wmarks.interval, &wmarks.high, &wmarks.mid, 212ae666a6dSSeongJae Park &wmarks.low, &parsed); 213ae666a6dSSeongJae Park if (ret != 18) 214af122dd8SSeongJae Park break; 2150bceffa2SSeongJae Park if (!damos_action_valid(action)) 216af122dd8SSeongJae Park goto fail; 217af122dd8SSeongJae Park 218c89ae63eSXin Hao if (min_sz > max_sz || min_nr_a > max_nr_a || min_age > max_age) 219c89ae63eSXin Hao goto fail; 220c89ae63eSXin Hao 221c89ae63eSXin Hao if (wmarks.high < wmarks.mid || wmarks.high < wmarks.low || 222c89ae63eSXin Hao wmarks.mid < wmarks.low) 223c89ae63eSXin Hao goto fail; 224c89ae63eSXin Hao 225af122dd8SSeongJae Park pos += parsed; 226af122dd8SSeongJae Park scheme = damon_new_scheme(min_sz, max_sz, min_nr_a, max_nr_a, 227ee801b7dSSeongJae Park min_age, max_age, action, "a, &wmarks); 228af122dd8SSeongJae Park if (!scheme) 229af122dd8SSeongJae Park goto fail; 230af122dd8SSeongJae Park 231af122dd8SSeongJae Park schemes[*nr_schemes] = scheme; 232af122dd8SSeongJae Park *nr_schemes += 1; 233af122dd8SSeongJae Park } 234af122dd8SSeongJae Park return schemes; 235af122dd8SSeongJae Park fail: 236af122dd8SSeongJae Park free_schemes_arr(schemes, *nr_schemes); 237af122dd8SSeongJae Park return NULL; 238af122dd8SSeongJae Park } 239af122dd8SSeongJae Park 240af122dd8SSeongJae Park static ssize_t dbgfs_schemes_write(struct file *file, const char __user *buf, 241af122dd8SSeongJae Park size_t count, loff_t *ppos) 242af122dd8SSeongJae Park { 243af122dd8SSeongJae Park struct damon_ctx *ctx = file->private_data; 244af122dd8SSeongJae Park char *kbuf; 245af122dd8SSeongJae Park struct damos **schemes; 2469210622aSRongwei Wang ssize_t nr_schemes = 0, ret; 247af122dd8SSeongJae Park 248af122dd8SSeongJae Park kbuf = user_input_str(buf, count, ppos); 249af122dd8SSeongJae Park if (IS_ERR(kbuf)) 250af122dd8SSeongJae Park return PTR_ERR(kbuf); 251af122dd8SSeongJae Park 2529210622aSRongwei Wang schemes = str_to_schemes(kbuf, count, &nr_schemes); 253af122dd8SSeongJae Park if (!schemes) { 254af122dd8SSeongJae Park ret = -EINVAL; 255af122dd8SSeongJae Park goto out; 256af122dd8SSeongJae Park } 257af122dd8SSeongJae Park 258af122dd8SSeongJae Park mutex_lock(&ctx->kdamond_lock); 259af122dd8SSeongJae Park if (ctx->kdamond) { 260af122dd8SSeongJae Park ret = -EBUSY; 261af122dd8SSeongJae Park goto unlock_out; 262af122dd8SSeongJae Park } 263af122dd8SSeongJae Park 2649210622aSRongwei Wang ret = damon_set_schemes(ctx, schemes, nr_schemes); 2659210622aSRongwei Wang if (!ret) { 2669210622aSRongwei Wang ret = count; 267af122dd8SSeongJae Park nr_schemes = 0; 2689210622aSRongwei Wang } 2699210622aSRongwei Wang 270af122dd8SSeongJae Park unlock_out: 271af122dd8SSeongJae Park mutex_unlock(&ctx->kdamond_lock); 272af122dd8SSeongJae Park free_schemes_arr(schemes, nr_schemes); 273af122dd8SSeongJae Park out: 274af122dd8SSeongJae Park kfree(kbuf); 275af122dd8SSeongJae Park return ret; 276af122dd8SSeongJae Park } 277af122dd8SSeongJae Park 2784bc05954SSeongJae Park static inline bool targetid_is_pid(const struct damon_ctx *ctx) 2794bc05954SSeongJae Park { 2804bc05954SSeongJae Park return ctx->primitive.target_valid == damon_va_target_valid; 2814bc05954SSeongJae Park } 2824bc05954SSeongJae Park 2834bc05954SSeongJae Park static ssize_t sprint_target_ids(struct damon_ctx *ctx, char *buf, ssize_t len) 2844bc05954SSeongJae Park { 2854bc05954SSeongJae Park struct damon_target *t; 2864bc05954SSeongJae Park unsigned long id; 2874bc05954SSeongJae Park int written = 0; 2884bc05954SSeongJae Park int rc; 2894bc05954SSeongJae Park 2904bc05954SSeongJae Park damon_for_each_target(t, ctx) { 2914bc05954SSeongJae Park id = t->id; 2924bc05954SSeongJae Park if (targetid_is_pid(ctx)) 2934bc05954SSeongJae Park /* Show pid numbers to debugfs users */ 2944bc05954SSeongJae Park id = (unsigned long)pid_vnr((struct pid *)id); 2954bc05954SSeongJae Park 2964bc05954SSeongJae Park rc = scnprintf(&buf[written], len - written, "%lu ", id); 2974bc05954SSeongJae Park if (!rc) 2984bc05954SSeongJae Park return -ENOMEM; 2994bc05954SSeongJae Park written += rc; 3004bc05954SSeongJae Park } 3014bc05954SSeongJae Park if (written) 3024bc05954SSeongJae Park written -= 1; 3034bc05954SSeongJae Park written += scnprintf(&buf[written], len - written, "\n"); 3044bc05954SSeongJae Park return written; 3054bc05954SSeongJae Park } 3064bc05954SSeongJae Park 3074bc05954SSeongJae Park static ssize_t dbgfs_target_ids_read(struct file *file, 3084bc05954SSeongJae Park char __user *buf, size_t count, loff_t *ppos) 3094bc05954SSeongJae Park { 3104bc05954SSeongJae Park struct damon_ctx *ctx = file->private_data; 3114bc05954SSeongJae Park ssize_t len; 3124bc05954SSeongJae Park char ids_buf[320]; 3134bc05954SSeongJae Park 3144bc05954SSeongJae Park mutex_lock(&ctx->kdamond_lock); 3154bc05954SSeongJae Park len = sprint_target_ids(ctx, ids_buf, 320); 3164bc05954SSeongJae Park mutex_unlock(&ctx->kdamond_lock); 3174bc05954SSeongJae Park if (len < 0) 3184bc05954SSeongJae Park return len; 3194bc05954SSeongJae Park 3204bc05954SSeongJae Park return simple_read_from_buffer(buf, count, ppos, ids_buf, len); 3214bc05954SSeongJae Park } 3224bc05954SSeongJae Park 3234bc05954SSeongJae Park /* 3244bc05954SSeongJae Park * Converts a string into an array of unsigned long integers 3254bc05954SSeongJae Park * 3264bc05954SSeongJae Park * Returns an array of unsigned long integers if the conversion success, or 3274bc05954SSeongJae Park * NULL otherwise. 3284bc05954SSeongJae Park */ 3294bc05954SSeongJae Park static unsigned long *str_to_target_ids(const char *str, ssize_t len, 3304bc05954SSeongJae Park ssize_t *nr_ids) 3314bc05954SSeongJae Park { 3324bc05954SSeongJae Park unsigned long *ids; 3334bc05954SSeongJae Park const int max_nr_ids = 32; 3344bc05954SSeongJae Park unsigned long id; 3354bc05954SSeongJae Park int pos = 0, parsed, ret; 3364bc05954SSeongJae Park 3374bc05954SSeongJae Park *nr_ids = 0; 3384bc05954SSeongJae Park ids = kmalloc_array(max_nr_ids, sizeof(id), GFP_KERNEL); 3394bc05954SSeongJae Park if (!ids) 3404bc05954SSeongJae Park return NULL; 3414bc05954SSeongJae Park while (*nr_ids < max_nr_ids && pos < len) { 3424bc05954SSeongJae Park ret = sscanf(&str[pos], "%lu%n", &id, &parsed); 3434bc05954SSeongJae Park pos += parsed; 3444bc05954SSeongJae Park if (ret != 1) 3454bc05954SSeongJae Park break; 3464bc05954SSeongJae Park ids[*nr_ids] = id; 3474bc05954SSeongJae Park *nr_ids += 1; 3484bc05954SSeongJae Park } 3494bc05954SSeongJae Park 3504bc05954SSeongJae Park return ids; 3514bc05954SSeongJae Park } 3524bc05954SSeongJae Park 3534bc05954SSeongJae Park static void dbgfs_put_pids(unsigned long *ids, int nr_ids) 3544bc05954SSeongJae Park { 3554bc05954SSeongJae Park int i; 3564bc05954SSeongJae Park 3574bc05954SSeongJae Park for (i = 0; i < nr_ids; i++) 3584bc05954SSeongJae Park put_pid((struct pid *)ids[i]); 3594bc05954SSeongJae Park } 3604bc05954SSeongJae Park 361*43642825SSeongJae Park /* 362*43642825SSeongJae Park * dbgfs_set_targets() - Set monitoring targets. 363*43642825SSeongJae Park * @ctx: monitoring context 364*43642825SSeongJae Park * @ids: array of target ids 365*43642825SSeongJae Park * @nr_ids: number of entries in @ids 366*43642825SSeongJae Park * 367*43642825SSeongJae Park * This function should not be called while the kdamond is running. 368*43642825SSeongJae Park * 369*43642825SSeongJae Park * Return: 0 on success, negative error code otherwise. 370*43642825SSeongJae Park */ 371*43642825SSeongJae Park static int dbgfs_set_targets(struct damon_ctx *ctx, 372*43642825SSeongJae Park unsigned long *ids, ssize_t nr_ids) 373*43642825SSeongJae Park { 374*43642825SSeongJae Park ssize_t i; 375*43642825SSeongJae Park struct damon_target *t, *next; 376*43642825SSeongJae Park 377*43642825SSeongJae Park damon_for_each_target_safe(t, next, ctx) { 378*43642825SSeongJae Park if (targetid_is_pid(ctx)) 379*43642825SSeongJae Park put_pid((struct pid *)t->id); 380*43642825SSeongJae Park damon_destroy_target(t); 381*43642825SSeongJae Park } 382*43642825SSeongJae Park 383*43642825SSeongJae Park for (i = 0; i < nr_ids; i++) { 384*43642825SSeongJae Park t = damon_new_target(ids[i]); 385*43642825SSeongJae Park if (!t) { 386*43642825SSeongJae Park /* The caller should do cleanup of the ids itself */ 387*43642825SSeongJae Park damon_for_each_target_safe(t, next, ctx) 388*43642825SSeongJae Park damon_destroy_target(t); 389*43642825SSeongJae Park if (targetid_is_pid(ctx)) 390*43642825SSeongJae Park dbgfs_put_pids(ids, nr_ids); 391*43642825SSeongJae Park return -ENOMEM; 392*43642825SSeongJae Park } 393*43642825SSeongJae Park damon_add_target(ctx, t); 394*43642825SSeongJae Park } 395*43642825SSeongJae Park 396*43642825SSeongJae Park return 0; 397*43642825SSeongJae Park } 398*43642825SSeongJae Park 3994bc05954SSeongJae Park static ssize_t dbgfs_target_ids_write(struct file *file, 4004bc05954SSeongJae Park const char __user *buf, size_t count, loff_t *ppos) 4014bc05954SSeongJae Park { 4024bc05954SSeongJae Park struct damon_ctx *ctx = file->private_data; 403c026291aSSeongJae Park bool id_is_pid = true; 40470b84808SSeongJae Park char *kbuf; 4054bc05954SSeongJae Park unsigned long *targets; 4064bc05954SSeongJae Park ssize_t nr_targets; 4079210622aSRongwei Wang ssize_t ret; 4084bc05954SSeongJae Park int i; 4094bc05954SSeongJae Park 4104bc05954SSeongJae Park kbuf = user_input_str(buf, count, ppos); 4114bc05954SSeongJae Park if (IS_ERR(kbuf)) 4124bc05954SSeongJae Park return PTR_ERR(kbuf); 4134bc05954SSeongJae Park 414c026291aSSeongJae Park if (!strncmp(kbuf, "paddr\n", count)) { 415c026291aSSeongJae Park id_is_pid = false; 416c026291aSSeongJae Park /* target id is meaningless here, but we set it just for fun */ 417c026291aSSeongJae Park scnprintf(kbuf, count, "42 "); 418c026291aSSeongJae Park } 4194bc05954SSeongJae Park 42070b84808SSeongJae Park targets = str_to_target_ids(kbuf, count, &nr_targets); 4214bc05954SSeongJae Park if (!targets) { 4224bc05954SSeongJae Park ret = -ENOMEM; 4234bc05954SSeongJae Park goto out; 4244bc05954SSeongJae Park } 4254bc05954SSeongJae Park 426c026291aSSeongJae Park if (id_is_pid) { 4274bc05954SSeongJae Park for (i = 0; i < nr_targets; i++) { 4284bc05954SSeongJae Park targets[i] = (unsigned long)find_get_pid( 4294bc05954SSeongJae Park (int)targets[i]); 4304bc05954SSeongJae Park if (!targets[i]) { 4314bc05954SSeongJae Park dbgfs_put_pids(targets, i); 4324bc05954SSeongJae Park ret = -EINVAL; 4334bc05954SSeongJae Park goto free_targets_out; 4344bc05954SSeongJae Park } 4354bc05954SSeongJae Park } 4364bc05954SSeongJae Park } 4374bc05954SSeongJae Park 4384bc05954SSeongJae Park mutex_lock(&ctx->kdamond_lock); 4394bc05954SSeongJae Park if (ctx->kdamond) { 440c026291aSSeongJae Park if (id_is_pid) 4414bc05954SSeongJae Park dbgfs_put_pids(targets, nr_targets); 4424bc05954SSeongJae Park ret = -EBUSY; 4434bc05954SSeongJae Park goto unlock_out; 4444bc05954SSeongJae Park } 4454bc05954SSeongJae Park 446ebb3f994SSeongJae Park /* remove previously set targets */ 447*43642825SSeongJae Park dbgfs_set_targets(ctx, NULL, 0); 448c026291aSSeongJae Park 449c026291aSSeongJae Park /* Configure the context for the address space type */ 450c026291aSSeongJae Park if (id_is_pid) 451c026291aSSeongJae Park damon_va_set_primitives(ctx); 452c026291aSSeongJae Park else 453c026291aSSeongJae Park damon_pa_set_primitives(ctx); 454c026291aSSeongJae Park 455*43642825SSeongJae Park ret = dbgfs_set_targets(ctx, targets, nr_targets); 456*43642825SSeongJae Park if (!ret) 4579210622aSRongwei Wang ret = count; 4584bc05954SSeongJae Park 4594bc05954SSeongJae Park unlock_out: 4604bc05954SSeongJae Park mutex_unlock(&ctx->kdamond_lock); 4614bc05954SSeongJae Park free_targets_out: 4624bc05954SSeongJae Park kfree(targets); 4634bc05954SSeongJae Park out: 4644bc05954SSeongJae Park kfree(kbuf); 4654bc05954SSeongJae Park return ret; 4664bc05954SSeongJae Park } 4674bc05954SSeongJae Park 46890bebce9SSeongJae Park static ssize_t sprint_init_regions(struct damon_ctx *c, char *buf, ssize_t len) 46990bebce9SSeongJae Park { 47090bebce9SSeongJae Park struct damon_target *t; 47190bebce9SSeongJae Park struct damon_region *r; 472144760f8SSeongJae Park int target_idx = 0; 47390bebce9SSeongJae Park int written = 0; 47490bebce9SSeongJae Park int rc; 47590bebce9SSeongJae Park 47690bebce9SSeongJae Park damon_for_each_target(t, c) { 47790bebce9SSeongJae Park damon_for_each_region(r, t) { 47890bebce9SSeongJae Park rc = scnprintf(&buf[written], len - written, 479144760f8SSeongJae Park "%d %lu %lu\n", 480144760f8SSeongJae Park target_idx, r->ar.start, r->ar.end); 48190bebce9SSeongJae Park if (!rc) 48290bebce9SSeongJae Park return -ENOMEM; 48390bebce9SSeongJae Park written += rc; 48490bebce9SSeongJae Park } 485144760f8SSeongJae Park target_idx++; 48690bebce9SSeongJae Park } 48790bebce9SSeongJae Park return written; 48890bebce9SSeongJae Park } 48990bebce9SSeongJae Park 49090bebce9SSeongJae Park static ssize_t dbgfs_init_regions_read(struct file *file, char __user *buf, 49190bebce9SSeongJae Park size_t count, loff_t *ppos) 49290bebce9SSeongJae Park { 49390bebce9SSeongJae Park struct damon_ctx *ctx = file->private_data; 49490bebce9SSeongJae Park char *kbuf; 49590bebce9SSeongJae Park ssize_t len; 49690bebce9SSeongJae Park 497db7a347bSSeongJae Park kbuf = kmalloc(count, GFP_KERNEL | __GFP_NOWARN); 49890bebce9SSeongJae Park if (!kbuf) 49990bebce9SSeongJae Park return -ENOMEM; 50090bebce9SSeongJae Park 50190bebce9SSeongJae Park mutex_lock(&ctx->kdamond_lock); 50290bebce9SSeongJae Park if (ctx->kdamond) { 50390bebce9SSeongJae Park mutex_unlock(&ctx->kdamond_lock); 50490bebce9SSeongJae Park len = -EBUSY; 50590bebce9SSeongJae Park goto out; 50690bebce9SSeongJae Park } 50790bebce9SSeongJae Park 50890bebce9SSeongJae Park len = sprint_init_regions(ctx, kbuf, count); 50990bebce9SSeongJae Park mutex_unlock(&ctx->kdamond_lock); 51090bebce9SSeongJae Park if (len < 0) 51190bebce9SSeongJae Park goto out; 51290bebce9SSeongJae Park len = simple_read_from_buffer(buf, count, ppos, kbuf, len); 51390bebce9SSeongJae Park 51490bebce9SSeongJae Park out: 51590bebce9SSeongJae Park kfree(kbuf); 51690bebce9SSeongJae Park return len; 51790bebce9SSeongJae Park } 51890bebce9SSeongJae Park 519144760f8SSeongJae Park static int add_init_region(struct damon_ctx *c, int target_idx, 520144760f8SSeongJae Park struct damon_addr_range *ar) 52190bebce9SSeongJae Park { 52290bebce9SSeongJae Park struct damon_target *t; 52390bebce9SSeongJae Park struct damon_region *r, *prev; 524144760f8SSeongJae Park unsigned long idx = 0; 52590bebce9SSeongJae Park int rc = -EINVAL; 52690bebce9SSeongJae Park 52790bebce9SSeongJae Park if (ar->start >= ar->end) 52890bebce9SSeongJae Park return -EINVAL; 52990bebce9SSeongJae Park 53090bebce9SSeongJae Park damon_for_each_target(t, c) { 531144760f8SSeongJae Park if (idx++ == target_idx) { 53290bebce9SSeongJae Park r = damon_new_region(ar->start, ar->end); 53390bebce9SSeongJae Park if (!r) 53490bebce9SSeongJae Park return -ENOMEM; 53590bebce9SSeongJae Park damon_add_region(r, t); 53690bebce9SSeongJae Park if (damon_nr_regions(t) > 1) { 53790bebce9SSeongJae Park prev = damon_prev_region(r); 53890bebce9SSeongJae Park if (prev->ar.end > r->ar.start) { 53990bebce9SSeongJae Park damon_destroy_region(r, t); 54090bebce9SSeongJae Park return -EINVAL; 54190bebce9SSeongJae Park } 54290bebce9SSeongJae Park } 54390bebce9SSeongJae Park rc = 0; 54490bebce9SSeongJae Park } 54590bebce9SSeongJae Park } 54690bebce9SSeongJae Park return rc; 54790bebce9SSeongJae Park } 54890bebce9SSeongJae Park 54990bebce9SSeongJae Park static int set_init_regions(struct damon_ctx *c, const char *str, ssize_t len) 55090bebce9SSeongJae Park { 55190bebce9SSeongJae Park struct damon_target *t; 55290bebce9SSeongJae Park struct damon_region *r, *next; 55390bebce9SSeongJae Park int pos = 0, parsed, ret; 554144760f8SSeongJae Park int target_idx; 55590bebce9SSeongJae Park struct damon_addr_range ar; 55690bebce9SSeongJae Park int err; 55790bebce9SSeongJae Park 55890bebce9SSeongJae Park damon_for_each_target(t, c) { 55990bebce9SSeongJae Park damon_for_each_region_safe(r, next, t) 56090bebce9SSeongJae Park damon_destroy_region(r, t); 56190bebce9SSeongJae Park } 56290bebce9SSeongJae Park 56390bebce9SSeongJae Park while (pos < len) { 564144760f8SSeongJae Park ret = sscanf(&str[pos], "%d %lu %lu%n", 565144760f8SSeongJae Park &target_idx, &ar.start, &ar.end, &parsed); 56690bebce9SSeongJae Park if (ret != 3) 56790bebce9SSeongJae Park break; 568144760f8SSeongJae Park err = add_init_region(c, target_idx, &ar); 56990bebce9SSeongJae Park if (err) 57090bebce9SSeongJae Park goto fail; 57190bebce9SSeongJae Park pos += parsed; 57290bebce9SSeongJae Park } 57390bebce9SSeongJae Park 57490bebce9SSeongJae Park return 0; 57590bebce9SSeongJae Park 57690bebce9SSeongJae Park fail: 57790bebce9SSeongJae Park damon_for_each_target(t, c) { 57890bebce9SSeongJae Park damon_for_each_region_safe(r, next, t) 57990bebce9SSeongJae Park damon_destroy_region(r, t); 58090bebce9SSeongJae Park } 58190bebce9SSeongJae Park return err; 58290bebce9SSeongJae Park } 58390bebce9SSeongJae Park 58490bebce9SSeongJae Park static ssize_t dbgfs_init_regions_write(struct file *file, 58590bebce9SSeongJae Park const char __user *buf, size_t count, 58690bebce9SSeongJae Park loff_t *ppos) 58790bebce9SSeongJae Park { 58890bebce9SSeongJae Park struct damon_ctx *ctx = file->private_data; 58990bebce9SSeongJae Park char *kbuf; 59090bebce9SSeongJae Park ssize_t ret = count; 59190bebce9SSeongJae Park int err; 59290bebce9SSeongJae Park 59390bebce9SSeongJae Park kbuf = user_input_str(buf, count, ppos); 59490bebce9SSeongJae Park if (IS_ERR(kbuf)) 59590bebce9SSeongJae Park return PTR_ERR(kbuf); 59690bebce9SSeongJae Park 59790bebce9SSeongJae Park mutex_lock(&ctx->kdamond_lock); 59890bebce9SSeongJae Park if (ctx->kdamond) { 59990bebce9SSeongJae Park ret = -EBUSY; 60090bebce9SSeongJae Park goto unlock_out; 60190bebce9SSeongJae Park } 60290bebce9SSeongJae Park 60390bebce9SSeongJae Park err = set_init_regions(ctx, kbuf, ret); 60490bebce9SSeongJae Park if (err) 60590bebce9SSeongJae Park ret = err; 60690bebce9SSeongJae Park 60790bebce9SSeongJae Park unlock_out: 60890bebce9SSeongJae Park mutex_unlock(&ctx->kdamond_lock); 60990bebce9SSeongJae Park kfree(kbuf); 61090bebce9SSeongJae Park return ret; 61190bebce9SSeongJae Park } 61290bebce9SSeongJae Park 613429538e8SSeongJae Park static ssize_t dbgfs_kdamond_pid_read(struct file *file, 614429538e8SSeongJae Park char __user *buf, size_t count, loff_t *ppos) 615429538e8SSeongJae Park { 616429538e8SSeongJae Park struct damon_ctx *ctx = file->private_data; 617429538e8SSeongJae Park char *kbuf; 618429538e8SSeongJae Park ssize_t len; 619429538e8SSeongJae Park 620db7a347bSSeongJae Park kbuf = kmalloc(count, GFP_KERNEL | __GFP_NOWARN); 621429538e8SSeongJae Park if (!kbuf) 622429538e8SSeongJae Park return -ENOMEM; 623429538e8SSeongJae Park 624429538e8SSeongJae Park mutex_lock(&ctx->kdamond_lock); 625429538e8SSeongJae Park if (ctx->kdamond) 626429538e8SSeongJae Park len = scnprintf(kbuf, count, "%d\n", ctx->kdamond->pid); 627429538e8SSeongJae Park else 628429538e8SSeongJae Park len = scnprintf(kbuf, count, "none\n"); 629429538e8SSeongJae Park mutex_unlock(&ctx->kdamond_lock); 630429538e8SSeongJae Park if (!len) 631429538e8SSeongJae Park goto out; 632429538e8SSeongJae Park len = simple_read_from_buffer(buf, count, ppos, kbuf, len); 633429538e8SSeongJae Park 634429538e8SSeongJae Park out: 635429538e8SSeongJae Park kfree(kbuf); 636429538e8SSeongJae Park return len; 637429538e8SSeongJae Park } 638429538e8SSeongJae Park 6394bc05954SSeongJae Park static int damon_dbgfs_open(struct inode *inode, struct file *file) 6404bc05954SSeongJae Park { 6414bc05954SSeongJae Park file->private_data = inode->i_private; 6424bc05954SSeongJae Park 6434bc05954SSeongJae Park return nonseekable_open(inode, file); 6444bc05954SSeongJae Park } 6454bc05954SSeongJae Park 6464bc05954SSeongJae Park static const struct file_operations attrs_fops = { 6474bc05954SSeongJae Park .open = damon_dbgfs_open, 6484bc05954SSeongJae Park .read = dbgfs_attrs_read, 6494bc05954SSeongJae Park .write = dbgfs_attrs_write, 6504bc05954SSeongJae Park }; 6514bc05954SSeongJae Park 652af122dd8SSeongJae Park static const struct file_operations schemes_fops = { 653af122dd8SSeongJae Park .open = damon_dbgfs_open, 654af122dd8SSeongJae Park .read = dbgfs_schemes_read, 655af122dd8SSeongJae Park .write = dbgfs_schemes_write, 656af122dd8SSeongJae Park }; 657af122dd8SSeongJae Park 6584bc05954SSeongJae Park static const struct file_operations target_ids_fops = { 6594bc05954SSeongJae Park .open = damon_dbgfs_open, 6604bc05954SSeongJae Park .read = dbgfs_target_ids_read, 6614bc05954SSeongJae Park .write = dbgfs_target_ids_write, 6624bc05954SSeongJae Park }; 6634bc05954SSeongJae Park 66490bebce9SSeongJae Park static const struct file_operations init_regions_fops = { 66590bebce9SSeongJae Park .open = damon_dbgfs_open, 66690bebce9SSeongJae Park .read = dbgfs_init_regions_read, 66790bebce9SSeongJae Park .write = dbgfs_init_regions_write, 66890bebce9SSeongJae Park }; 66990bebce9SSeongJae Park 670429538e8SSeongJae Park static const struct file_operations kdamond_pid_fops = { 671429538e8SSeongJae Park .open = damon_dbgfs_open, 672429538e8SSeongJae Park .read = dbgfs_kdamond_pid_read, 673429538e8SSeongJae Park }; 674429538e8SSeongJae Park 6754bc05954SSeongJae Park static void dbgfs_fill_ctx_dir(struct dentry *dir, struct damon_ctx *ctx) 6764bc05954SSeongJae Park { 677af122dd8SSeongJae Park const char * const file_names[] = {"attrs", "schemes", "target_ids", 67890bebce9SSeongJae Park "init_regions", "kdamond_pid"}; 679af122dd8SSeongJae Park const struct file_operations *fops[] = {&attrs_fops, &schemes_fops, 68090bebce9SSeongJae Park &target_ids_fops, &init_regions_fops, &kdamond_pid_fops}; 6814bc05954SSeongJae Park int i; 6824bc05954SSeongJae Park 6834bc05954SSeongJae Park for (i = 0; i < ARRAY_SIZE(file_names); i++) 6844bc05954SSeongJae Park debugfs_create_file(file_names[i], 0600, dir, ctx, fops[i]); 6854bc05954SSeongJae Park } 6864bc05954SSeongJae Park 687658f9ae7SChangbin Du static void dbgfs_before_terminate(struct damon_ctx *ctx) 6884bc05954SSeongJae Park { 6894bc05954SSeongJae Park struct damon_target *t, *next; 6904bc05954SSeongJae Park 6914bc05954SSeongJae Park if (!targetid_is_pid(ctx)) 692658f9ae7SChangbin Du return; 6934bc05954SSeongJae Park 69434796417SSeongJae Park mutex_lock(&ctx->kdamond_lock); 6954bc05954SSeongJae Park damon_for_each_target_safe(t, next, ctx) { 6964bc05954SSeongJae Park put_pid((struct pid *)t->id); 6974bc05954SSeongJae Park damon_destroy_target(t); 6984bc05954SSeongJae Park } 69934796417SSeongJae Park mutex_unlock(&ctx->kdamond_lock); 7004bc05954SSeongJae Park } 7014bc05954SSeongJae Park 7024bc05954SSeongJae Park static struct damon_ctx *dbgfs_new_ctx(void) 7034bc05954SSeongJae Park { 7044bc05954SSeongJae Park struct damon_ctx *ctx; 7054bc05954SSeongJae Park 7064bc05954SSeongJae Park ctx = damon_new_ctx(); 7074bc05954SSeongJae Park if (!ctx) 7084bc05954SSeongJae Park return NULL; 7094bc05954SSeongJae Park 7104bc05954SSeongJae Park damon_va_set_primitives(ctx); 7114bc05954SSeongJae Park ctx->callback.before_terminate = dbgfs_before_terminate; 7124bc05954SSeongJae Park return ctx; 7134bc05954SSeongJae Park } 7144bc05954SSeongJae Park 71575c1c2b5SSeongJae Park static void dbgfs_destroy_ctx(struct damon_ctx *ctx) 71675c1c2b5SSeongJae Park { 71775c1c2b5SSeongJae Park damon_destroy_ctx(ctx); 71875c1c2b5SSeongJae Park } 71975c1c2b5SSeongJae Park 72075c1c2b5SSeongJae Park /* 72175c1c2b5SSeongJae Park * Make a context of @name and create a debugfs directory for it. 72275c1c2b5SSeongJae Park * 72375c1c2b5SSeongJae Park * This function should be called while holding damon_dbgfs_lock. 72475c1c2b5SSeongJae Park * 72575c1c2b5SSeongJae Park * Returns 0 on success, negative error code otherwise. 72675c1c2b5SSeongJae Park */ 72775c1c2b5SSeongJae Park static int dbgfs_mk_context(char *name) 72875c1c2b5SSeongJae Park { 72975c1c2b5SSeongJae Park struct dentry *root, **new_dirs, *new_dir; 73075c1c2b5SSeongJae Park struct damon_ctx **new_ctxs, *new_ctx; 73175c1c2b5SSeongJae Park 73275c1c2b5SSeongJae Park if (damon_nr_running_ctxs()) 73375c1c2b5SSeongJae Park return -EBUSY; 73475c1c2b5SSeongJae Park 73575c1c2b5SSeongJae Park new_ctxs = krealloc(dbgfs_ctxs, sizeof(*dbgfs_ctxs) * 73675c1c2b5SSeongJae Park (dbgfs_nr_ctxs + 1), GFP_KERNEL); 73775c1c2b5SSeongJae Park if (!new_ctxs) 73875c1c2b5SSeongJae Park return -ENOMEM; 73975c1c2b5SSeongJae Park dbgfs_ctxs = new_ctxs; 74075c1c2b5SSeongJae Park 74175c1c2b5SSeongJae Park new_dirs = krealloc(dbgfs_dirs, sizeof(*dbgfs_dirs) * 74275c1c2b5SSeongJae Park (dbgfs_nr_ctxs + 1), GFP_KERNEL); 74375c1c2b5SSeongJae Park if (!new_dirs) 74475c1c2b5SSeongJae Park return -ENOMEM; 74575c1c2b5SSeongJae Park dbgfs_dirs = new_dirs; 74675c1c2b5SSeongJae Park 74775c1c2b5SSeongJae Park root = dbgfs_dirs[0]; 74875c1c2b5SSeongJae Park if (!root) 74975c1c2b5SSeongJae Park return -ENOENT; 75075c1c2b5SSeongJae Park 75175c1c2b5SSeongJae Park new_dir = debugfs_create_dir(name, root); 75275c1c2b5SSeongJae Park dbgfs_dirs[dbgfs_nr_ctxs] = new_dir; 75375c1c2b5SSeongJae Park 75475c1c2b5SSeongJae Park new_ctx = dbgfs_new_ctx(); 75575c1c2b5SSeongJae Park if (!new_ctx) { 75675c1c2b5SSeongJae Park debugfs_remove(new_dir); 75775c1c2b5SSeongJae Park dbgfs_dirs[dbgfs_nr_ctxs] = NULL; 75875c1c2b5SSeongJae Park return -ENOMEM; 75975c1c2b5SSeongJae Park } 76075c1c2b5SSeongJae Park 76175c1c2b5SSeongJae Park dbgfs_ctxs[dbgfs_nr_ctxs] = new_ctx; 76275c1c2b5SSeongJae Park dbgfs_fill_ctx_dir(dbgfs_dirs[dbgfs_nr_ctxs], 76375c1c2b5SSeongJae Park dbgfs_ctxs[dbgfs_nr_ctxs]); 76475c1c2b5SSeongJae Park dbgfs_nr_ctxs++; 76575c1c2b5SSeongJae Park 76675c1c2b5SSeongJae Park return 0; 76775c1c2b5SSeongJae Park } 76875c1c2b5SSeongJae Park 76975c1c2b5SSeongJae Park static ssize_t dbgfs_mk_context_write(struct file *file, 77075c1c2b5SSeongJae Park const char __user *buf, size_t count, loff_t *ppos) 77175c1c2b5SSeongJae Park { 77275c1c2b5SSeongJae Park char *kbuf; 77375c1c2b5SSeongJae Park char *ctx_name; 7749210622aSRongwei Wang ssize_t ret; 77575c1c2b5SSeongJae Park 77675c1c2b5SSeongJae Park kbuf = user_input_str(buf, count, ppos); 77775c1c2b5SSeongJae Park if (IS_ERR(kbuf)) 77875c1c2b5SSeongJae Park return PTR_ERR(kbuf); 77975c1c2b5SSeongJae Park ctx_name = kmalloc(count + 1, GFP_KERNEL); 78075c1c2b5SSeongJae Park if (!ctx_name) { 78175c1c2b5SSeongJae Park kfree(kbuf); 78275c1c2b5SSeongJae Park return -ENOMEM; 78375c1c2b5SSeongJae Park } 78475c1c2b5SSeongJae Park 78575c1c2b5SSeongJae Park /* Trim white space */ 78675c1c2b5SSeongJae Park if (sscanf(kbuf, "%s", ctx_name) != 1) { 78775c1c2b5SSeongJae Park ret = -EINVAL; 78875c1c2b5SSeongJae Park goto out; 78975c1c2b5SSeongJae Park } 79075c1c2b5SSeongJae Park 79175c1c2b5SSeongJae Park mutex_lock(&damon_dbgfs_lock); 7929210622aSRongwei Wang ret = dbgfs_mk_context(ctx_name); 7939210622aSRongwei Wang if (!ret) 7949210622aSRongwei Wang ret = count; 79575c1c2b5SSeongJae Park mutex_unlock(&damon_dbgfs_lock); 79675c1c2b5SSeongJae Park 79775c1c2b5SSeongJae Park out: 79875c1c2b5SSeongJae Park kfree(kbuf); 79975c1c2b5SSeongJae Park kfree(ctx_name); 80075c1c2b5SSeongJae Park return ret; 80175c1c2b5SSeongJae Park } 80275c1c2b5SSeongJae Park 80375c1c2b5SSeongJae Park /* 80475c1c2b5SSeongJae Park * Remove a context of @name and its debugfs directory. 80575c1c2b5SSeongJae Park * 80675c1c2b5SSeongJae Park * This function should be called while holding damon_dbgfs_lock. 80775c1c2b5SSeongJae Park * 80875c1c2b5SSeongJae Park * Return 0 on success, negative error code otherwise. 80975c1c2b5SSeongJae Park */ 81075c1c2b5SSeongJae Park static int dbgfs_rm_context(char *name) 81175c1c2b5SSeongJae Park { 81275c1c2b5SSeongJae Park struct dentry *root, *dir, **new_dirs; 81375c1c2b5SSeongJae Park struct damon_ctx **new_ctxs; 81475c1c2b5SSeongJae Park int i, j; 81575c1c2b5SSeongJae Park 81675c1c2b5SSeongJae Park if (damon_nr_running_ctxs()) 81775c1c2b5SSeongJae Park return -EBUSY; 81875c1c2b5SSeongJae Park 81975c1c2b5SSeongJae Park root = dbgfs_dirs[0]; 82075c1c2b5SSeongJae Park if (!root) 82175c1c2b5SSeongJae Park return -ENOENT; 82275c1c2b5SSeongJae Park 82375c1c2b5SSeongJae Park dir = debugfs_lookup(name, root); 82475c1c2b5SSeongJae Park if (!dir) 82575c1c2b5SSeongJae Park return -ENOENT; 82675c1c2b5SSeongJae Park 82775c1c2b5SSeongJae Park new_dirs = kmalloc_array(dbgfs_nr_ctxs - 1, sizeof(*dbgfs_dirs), 82875c1c2b5SSeongJae Park GFP_KERNEL); 82975c1c2b5SSeongJae Park if (!new_dirs) 83075c1c2b5SSeongJae Park return -ENOMEM; 83175c1c2b5SSeongJae Park 83275c1c2b5SSeongJae Park new_ctxs = kmalloc_array(dbgfs_nr_ctxs - 1, sizeof(*dbgfs_ctxs), 83375c1c2b5SSeongJae Park GFP_KERNEL); 83475c1c2b5SSeongJae Park if (!new_ctxs) { 83575c1c2b5SSeongJae Park kfree(new_dirs); 83675c1c2b5SSeongJae Park return -ENOMEM; 83775c1c2b5SSeongJae Park } 83875c1c2b5SSeongJae Park 83975c1c2b5SSeongJae Park for (i = 0, j = 0; i < dbgfs_nr_ctxs; i++) { 84075c1c2b5SSeongJae Park if (dbgfs_dirs[i] == dir) { 84175c1c2b5SSeongJae Park debugfs_remove(dbgfs_dirs[i]); 84275c1c2b5SSeongJae Park dbgfs_destroy_ctx(dbgfs_ctxs[i]); 84375c1c2b5SSeongJae Park continue; 84475c1c2b5SSeongJae Park } 84575c1c2b5SSeongJae Park new_dirs[j] = dbgfs_dirs[i]; 84675c1c2b5SSeongJae Park new_ctxs[j++] = dbgfs_ctxs[i]; 84775c1c2b5SSeongJae Park } 84875c1c2b5SSeongJae Park 84975c1c2b5SSeongJae Park kfree(dbgfs_dirs); 85075c1c2b5SSeongJae Park kfree(dbgfs_ctxs); 85175c1c2b5SSeongJae Park 85275c1c2b5SSeongJae Park dbgfs_dirs = new_dirs; 85375c1c2b5SSeongJae Park dbgfs_ctxs = new_ctxs; 85475c1c2b5SSeongJae Park dbgfs_nr_ctxs--; 85575c1c2b5SSeongJae Park 85675c1c2b5SSeongJae Park return 0; 85775c1c2b5SSeongJae Park } 85875c1c2b5SSeongJae Park 85975c1c2b5SSeongJae Park static ssize_t dbgfs_rm_context_write(struct file *file, 86075c1c2b5SSeongJae Park const char __user *buf, size_t count, loff_t *ppos) 86175c1c2b5SSeongJae Park { 86275c1c2b5SSeongJae Park char *kbuf; 8639210622aSRongwei Wang ssize_t ret; 86475c1c2b5SSeongJae Park char *ctx_name; 86575c1c2b5SSeongJae Park 86675c1c2b5SSeongJae Park kbuf = user_input_str(buf, count, ppos); 86775c1c2b5SSeongJae Park if (IS_ERR(kbuf)) 86875c1c2b5SSeongJae Park return PTR_ERR(kbuf); 86975c1c2b5SSeongJae Park ctx_name = kmalloc(count + 1, GFP_KERNEL); 87075c1c2b5SSeongJae Park if (!ctx_name) { 87175c1c2b5SSeongJae Park kfree(kbuf); 87275c1c2b5SSeongJae Park return -ENOMEM; 87375c1c2b5SSeongJae Park } 87475c1c2b5SSeongJae Park 87575c1c2b5SSeongJae Park /* Trim white space */ 87675c1c2b5SSeongJae Park if (sscanf(kbuf, "%s", ctx_name) != 1) { 87775c1c2b5SSeongJae Park ret = -EINVAL; 87875c1c2b5SSeongJae Park goto out; 87975c1c2b5SSeongJae Park } 88075c1c2b5SSeongJae Park 88175c1c2b5SSeongJae Park mutex_lock(&damon_dbgfs_lock); 8829210622aSRongwei Wang ret = dbgfs_rm_context(ctx_name); 8839210622aSRongwei Wang if (!ret) 8849210622aSRongwei Wang ret = count; 88575c1c2b5SSeongJae Park mutex_unlock(&damon_dbgfs_lock); 88675c1c2b5SSeongJae Park 88775c1c2b5SSeongJae Park out: 88875c1c2b5SSeongJae Park kfree(kbuf); 88975c1c2b5SSeongJae Park kfree(ctx_name); 89075c1c2b5SSeongJae Park return ret; 89175c1c2b5SSeongJae Park } 89275c1c2b5SSeongJae Park 8934bc05954SSeongJae Park static ssize_t dbgfs_monitor_on_read(struct file *file, 8944bc05954SSeongJae Park char __user *buf, size_t count, loff_t *ppos) 8954bc05954SSeongJae Park { 8964bc05954SSeongJae Park char monitor_on_buf[5]; 8974bc05954SSeongJae Park bool monitor_on = damon_nr_running_ctxs() != 0; 8984bc05954SSeongJae Park int len; 8994bc05954SSeongJae Park 9004bc05954SSeongJae Park len = scnprintf(monitor_on_buf, 5, monitor_on ? "on\n" : "off\n"); 9014bc05954SSeongJae Park 9024bc05954SSeongJae Park return simple_read_from_buffer(buf, count, ppos, monitor_on_buf, len); 9034bc05954SSeongJae Park } 9044bc05954SSeongJae Park 9054bc05954SSeongJae Park static ssize_t dbgfs_monitor_on_write(struct file *file, 9064bc05954SSeongJae Park const char __user *buf, size_t count, loff_t *ppos) 9074bc05954SSeongJae Park { 9089210622aSRongwei Wang ssize_t ret; 9094bc05954SSeongJae Park char *kbuf; 9104bc05954SSeongJae Park 9114bc05954SSeongJae Park kbuf = user_input_str(buf, count, ppos); 9124bc05954SSeongJae Park if (IS_ERR(kbuf)) 9134bc05954SSeongJae Park return PTR_ERR(kbuf); 9144bc05954SSeongJae Park 9154bc05954SSeongJae Park /* Remove white space */ 9164bc05954SSeongJae Park if (sscanf(kbuf, "%s", kbuf) != 1) { 9174bc05954SSeongJae Park kfree(kbuf); 9184bc05954SSeongJae Park return -EINVAL; 9194bc05954SSeongJae Park } 9204bc05954SSeongJae Park 921d78f3853SSeongJae Park mutex_lock(&damon_dbgfs_lock); 922b5ca3e83SXin Hao if (!strncmp(kbuf, "on", count)) { 923b5ca3e83SXin Hao int i; 924b5ca3e83SXin Hao 925b5ca3e83SXin Hao for (i = 0; i < dbgfs_nr_ctxs; i++) { 926b5ca3e83SXin Hao if (damon_targets_empty(dbgfs_ctxs[i])) { 927b5ca3e83SXin Hao kfree(kbuf); 928d78f3853SSeongJae Park mutex_unlock(&damon_dbgfs_lock); 929b5ca3e83SXin Hao return -EINVAL; 930b5ca3e83SXin Hao } 931b5ca3e83SXin Hao } 9329210622aSRongwei Wang ret = damon_start(dbgfs_ctxs, dbgfs_nr_ctxs); 933b5ca3e83SXin Hao } else if (!strncmp(kbuf, "off", count)) { 9349210622aSRongwei Wang ret = damon_stop(dbgfs_ctxs, dbgfs_nr_ctxs); 935b5ca3e83SXin Hao } else { 9369210622aSRongwei Wang ret = -EINVAL; 937b5ca3e83SXin Hao } 938d78f3853SSeongJae Park mutex_unlock(&damon_dbgfs_lock); 9394bc05954SSeongJae Park 9409210622aSRongwei Wang if (!ret) 9419210622aSRongwei Wang ret = count; 9424bc05954SSeongJae Park kfree(kbuf); 9434bc05954SSeongJae Park return ret; 9444bc05954SSeongJae Park } 9454bc05954SSeongJae Park 94675c1c2b5SSeongJae Park static const struct file_operations mk_contexts_fops = { 94775c1c2b5SSeongJae Park .write = dbgfs_mk_context_write, 94875c1c2b5SSeongJae Park }; 94975c1c2b5SSeongJae Park 95075c1c2b5SSeongJae Park static const struct file_operations rm_contexts_fops = { 95175c1c2b5SSeongJae Park .write = dbgfs_rm_context_write, 95275c1c2b5SSeongJae Park }; 95375c1c2b5SSeongJae Park 9544bc05954SSeongJae Park static const struct file_operations monitor_on_fops = { 9554bc05954SSeongJae Park .read = dbgfs_monitor_on_read, 9564bc05954SSeongJae Park .write = dbgfs_monitor_on_write, 9574bc05954SSeongJae Park }; 9584bc05954SSeongJae Park 9594bc05954SSeongJae Park static int __init __damon_dbgfs_init(void) 9604bc05954SSeongJae Park { 9614bc05954SSeongJae Park struct dentry *dbgfs_root; 96275c1c2b5SSeongJae Park const char * const file_names[] = {"mk_contexts", "rm_contexts", 96375c1c2b5SSeongJae Park "monitor_on"}; 96475c1c2b5SSeongJae Park const struct file_operations *fops[] = {&mk_contexts_fops, 96575c1c2b5SSeongJae Park &rm_contexts_fops, &monitor_on_fops}; 9664bc05954SSeongJae Park int i; 9674bc05954SSeongJae Park 9684bc05954SSeongJae Park dbgfs_root = debugfs_create_dir("damon", NULL); 9694bc05954SSeongJae Park 9704bc05954SSeongJae Park for (i = 0; i < ARRAY_SIZE(file_names); i++) 9714bc05954SSeongJae Park debugfs_create_file(file_names[i], 0600, dbgfs_root, NULL, 9724bc05954SSeongJae Park fops[i]); 9734bc05954SSeongJae Park dbgfs_fill_ctx_dir(dbgfs_root, dbgfs_ctxs[0]); 9744bc05954SSeongJae Park 9754bc05954SSeongJae Park dbgfs_dirs = kmalloc_array(1, sizeof(dbgfs_root), GFP_KERNEL); 9764bc05954SSeongJae Park if (!dbgfs_dirs) { 9774bc05954SSeongJae Park debugfs_remove(dbgfs_root); 9784bc05954SSeongJae Park return -ENOMEM; 9794bc05954SSeongJae Park } 9804bc05954SSeongJae Park dbgfs_dirs[0] = dbgfs_root; 9814bc05954SSeongJae Park 9824bc05954SSeongJae Park return 0; 9834bc05954SSeongJae Park } 9844bc05954SSeongJae Park 9854bc05954SSeongJae Park /* 9864bc05954SSeongJae Park * Functions for the initialization 9874bc05954SSeongJae Park */ 9884bc05954SSeongJae Park 9894bc05954SSeongJae Park static int __init damon_dbgfs_init(void) 9904bc05954SSeongJae Park { 991d78f3853SSeongJae Park int rc = -ENOMEM; 9924bc05954SSeongJae Park 993d78f3853SSeongJae Park mutex_lock(&damon_dbgfs_lock); 9944bc05954SSeongJae Park dbgfs_ctxs = kmalloc(sizeof(*dbgfs_ctxs), GFP_KERNEL); 9954bc05954SSeongJae Park if (!dbgfs_ctxs) 996d78f3853SSeongJae Park goto out; 9974bc05954SSeongJae Park dbgfs_ctxs[0] = dbgfs_new_ctx(); 9984bc05954SSeongJae Park if (!dbgfs_ctxs[0]) { 9994bc05954SSeongJae Park kfree(dbgfs_ctxs); 1000d78f3853SSeongJae Park goto out; 10014bc05954SSeongJae Park } 10024bc05954SSeongJae Park dbgfs_nr_ctxs = 1; 10034bc05954SSeongJae Park 10044bc05954SSeongJae Park rc = __damon_dbgfs_init(); 10054bc05954SSeongJae Park if (rc) { 10064bc05954SSeongJae Park kfree(dbgfs_ctxs[0]); 10074bc05954SSeongJae Park kfree(dbgfs_ctxs); 10084bc05954SSeongJae Park pr_err("%s: dbgfs init failed\n", __func__); 10094bc05954SSeongJae Park } 10104bc05954SSeongJae Park 1011d78f3853SSeongJae Park out: 1012d78f3853SSeongJae Park mutex_unlock(&damon_dbgfs_lock); 10134bc05954SSeongJae Park return rc; 10144bc05954SSeongJae Park } 10154bc05954SSeongJae Park 10164bc05954SSeongJae Park module_init(damon_dbgfs_init); 101717ccae8bSSeongJae Park 101817ccae8bSSeongJae Park #include "dbgfs-test.h" 1019