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