14bc05954SSeongJae Park // SPDX-License-Identifier: GPL-2.0 24bc05954SSeongJae Park /* 34bc05954SSeongJae Park * DAMON Debugfs Interface 44bc05954SSeongJae Park * 54bc05954SSeongJae Park * Author: SeongJae Park <sjpark@amazon.de> 64bc05954SSeongJae Park */ 74bc05954SSeongJae Park 84bc05954SSeongJae Park #define pr_fmt(fmt) "damon-dbgfs: " fmt 94bc05954SSeongJae Park 104bc05954SSeongJae Park #include <linux/damon.h> 114bc05954SSeongJae Park #include <linux/debugfs.h> 124bc05954SSeongJae Park #include <linux/file.h> 134bc05954SSeongJae Park #include <linux/mm.h> 144bc05954SSeongJae Park #include <linux/module.h> 154bc05954SSeongJae Park #include <linux/page_idle.h> 164bc05954SSeongJae Park #include <linux/slab.h> 174bc05954SSeongJae Park 184bc05954SSeongJae Park static struct damon_ctx **dbgfs_ctxs; 194bc05954SSeongJae Park static int dbgfs_nr_ctxs; 204bc05954SSeongJae Park static struct dentry **dbgfs_dirs; 2175c1c2b5SSeongJae Park static DEFINE_MUTEX(damon_dbgfs_lock); 224bc05954SSeongJae Park 234bc05954SSeongJae Park /* 244bc05954SSeongJae Park * Returns non-empty string on success, negative error code otherwise. 254bc05954SSeongJae Park */ 264bc05954SSeongJae Park static char *user_input_str(const char __user *buf, size_t count, loff_t *ppos) 274bc05954SSeongJae Park { 284bc05954SSeongJae Park char *kbuf; 294bc05954SSeongJae Park ssize_t ret; 304bc05954SSeongJae Park 314bc05954SSeongJae Park /* We do not accept continuous write */ 324bc05954SSeongJae Park if (*ppos) 334bc05954SSeongJae Park return ERR_PTR(-EINVAL); 344bc05954SSeongJae Park 354bc05954SSeongJae Park kbuf = kmalloc(count + 1, GFP_KERNEL); 364bc05954SSeongJae Park if (!kbuf) 374bc05954SSeongJae Park return ERR_PTR(-ENOMEM); 384bc05954SSeongJae Park 394bc05954SSeongJae Park ret = simple_write_to_buffer(kbuf, count + 1, ppos, buf, count); 404bc05954SSeongJae Park if (ret != count) { 414bc05954SSeongJae Park kfree(kbuf); 424bc05954SSeongJae Park return ERR_PTR(-EIO); 434bc05954SSeongJae Park } 444bc05954SSeongJae Park kbuf[ret] = '\0'; 454bc05954SSeongJae Park 464bc05954SSeongJae Park return kbuf; 474bc05954SSeongJae Park } 484bc05954SSeongJae Park 494bc05954SSeongJae Park static ssize_t dbgfs_attrs_read(struct file *file, 504bc05954SSeongJae Park char __user *buf, size_t count, loff_t *ppos) 514bc05954SSeongJae Park { 524bc05954SSeongJae Park struct damon_ctx *ctx = file->private_data; 534bc05954SSeongJae Park char kbuf[128]; 544bc05954SSeongJae Park int ret; 554bc05954SSeongJae Park 564bc05954SSeongJae Park mutex_lock(&ctx->kdamond_lock); 574bc05954SSeongJae Park ret = scnprintf(kbuf, ARRAY_SIZE(kbuf), "%lu %lu %lu %lu %lu\n", 584bc05954SSeongJae Park ctx->sample_interval, ctx->aggr_interval, 594bc05954SSeongJae Park ctx->primitive_update_interval, ctx->min_nr_regions, 604bc05954SSeongJae Park ctx->max_nr_regions); 614bc05954SSeongJae Park mutex_unlock(&ctx->kdamond_lock); 624bc05954SSeongJae Park 634bc05954SSeongJae Park return simple_read_from_buffer(buf, count, ppos, kbuf, ret); 644bc05954SSeongJae Park } 654bc05954SSeongJae Park 664bc05954SSeongJae Park static ssize_t dbgfs_attrs_write(struct file *file, 674bc05954SSeongJae Park const char __user *buf, size_t count, loff_t *ppos) 684bc05954SSeongJae Park { 694bc05954SSeongJae Park struct damon_ctx *ctx = file->private_data; 704bc05954SSeongJae Park unsigned long s, a, r, minr, maxr; 714bc05954SSeongJae Park char *kbuf; 724bc05954SSeongJae Park ssize_t ret = count; 734bc05954SSeongJae Park int err; 744bc05954SSeongJae Park 754bc05954SSeongJae Park kbuf = user_input_str(buf, count, ppos); 764bc05954SSeongJae Park if (IS_ERR(kbuf)) 774bc05954SSeongJae Park return PTR_ERR(kbuf); 784bc05954SSeongJae Park 794bc05954SSeongJae Park if (sscanf(kbuf, "%lu %lu %lu %lu %lu", 804bc05954SSeongJae Park &s, &a, &r, &minr, &maxr) != 5) { 814bc05954SSeongJae Park ret = -EINVAL; 824bc05954SSeongJae Park goto out; 834bc05954SSeongJae Park } 844bc05954SSeongJae Park 854bc05954SSeongJae Park mutex_lock(&ctx->kdamond_lock); 864bc05954SSeongJae Park if (ctx->kdamond) { 874bc05954SSeongJae Park ret = -EBUSY; 884bc05954SSeongJae Park goto unlock_out; 894bc05954SSeongJae Park } 904bc05954SSeongJae Park 914bc05954SSeongJae Park err = damon_set_attrs(ctx, s, a, r, minr, maxr); 924bc05954SSeongJae Park if (err) 934bc05954SSeongJae Park ret = err; 944bc05954SSeongJae Park unlock_out: 954bc05954SSeongJae Park mutex_unlock(&ctx->kdamond_lock); 964bc05954SSeongJae Park out: 974bc05954SSeongJae Park kfree(kbuf); 984bc05954SSeongJae Park return ret; 994bc05954SSeongJae Park } 1004bc05954SSeongJae Park 1014bc05954SSeongJae Park static inline bool targetid_is_pid(const struct damon_ctx *ctx) 1024bc05954SSeongJae Park { 1034bc05954SSeongJae Park return ctx->primitive.target_valid == damon_va_target_valid; 1044bc05954SSeongJae Park } 1054bc05954SSeongJae Park 1064bc05954SSeongJae Park static ssize_t sprint_target_ids(struct damon_ctx *ctx, char *buf, ssize_t len) 1074bc05954SSeongJae Park { 1084bc05954SSeongJae Park struct damon_target *t; 1094bc05954SSeongJae Park unsigned long id; 1104bc05954SSeongJae Park int written = 0; 1114bc05954SSeongJae Park int rc; 1124bc05954SSeongJae Park 1134bc05954SSeongJae Park damon_for_each_target(t, ctx) { 1144bc05954SSeongJae Park id = t->id; 1154bc05954SSeongJae Park if (targetid_is_pid(ctx)) 1164bc05954SSeongJae Park /* Show pid numbers to debugfs users */ 1174bc05954SSeongJae Park id = (unsigned long)pid_vnr((struct pid *)id); 1184bc05954SSeongJae Park 1194bc05954SSeongJae Park rc = scnprintf(&buf[written], len - written, "%lu ", id); 1204bc05954SSeongJae Park if (!rc) 1214bc05954SSeongJae Park return -ENOMEM; 1224bc05954SSeongJae Park written += rc; 1234bc05954SSeongJae Park } 1244bc05954SSeongJae Park if (written) 1254bc05954SSeongJae Park written -= 1; 1264bc05954SSeongJae Park written += scnprintf(&buf[written], len - written, "\n"); 1274bc05954SSeongJae Park return written; 1284bc05954SSeongJae Park } 1294bc05954SSeongJae Park 1304bc05954SSeongJae Park static ssize_t dbgfs_target_ids_read(struct file *file, 1314bc05954SSeongJae Park char __user *buf, size_t count, loff_t *ppos) 1324bc05954SSeongJae Park { 1334bc05954SSeongJae Park struct damon_ctx *ctx = file->private_data; 1344bc05954SSeongJae Park ssize_t len; 1354bc05954SSeongJae Park char ids_buf[320]; 1364bc05954SSeongJae Park 1374bc05954SSeongJae Park mutex_lock(&ctx->kdamond_lock); 1384bc05954SSeongJae Park len = sprint_target_ids(ctx, ids_buf, 320); 1394bc05954SSeongJae Park mutex_unlock(&ctx->kdamond_lock); 1404bc05954SSeongJae Park if (len < 0) 1414bc05954SSeongJae Park return len; 1424bc05954SSeongJae Park 1434bc05954SSeongJae Park return simple_read_from_buffer(buf, count, ppos, ids_buf, len); 1444bc05954SSeongJae Park } 1454bc05954SSeongJae Park 1464bc05954SSeongJae Park /* 1474bc05954SSeongJae Park * Converts a string into an array of unsigned long integers 1484bc05954SSeongJae Park * 1494bc05954SSeongJae Park * Returns an array of unsigned long integers if the conversion success, or 1504bc05954SSeongJae Park * NULL otherwise. 1514bc05954SSeongJae Park */ 1524bc05954SSeongJae Park static unsigned long *str_to_target_ids(const char *str, ssize_t len, 1534bc05954SSeongJae Park ssize_t *nr_ids) 1544bc05954SSeongJae Park { 1554bc05954SSeongJae Park unsigned long *ids; 1564bc05954SSeongJae Park const int max_nr_ids = 32; 1574bc05954SSeongJae Park unsigned long id; 1584bc05954SSeongJae Park int pos = 0, parsed, ret; 1594bc05954SSeongJae Park 1604bc05954SSeongJae Park *nr_ids = 0; 1614bc05954SSeongJae Park ids = kmalloc_array(max_nr_ids, sizeof(id), GFP_KERNEL); 1624bc05954SSeongJae Park if (!ids) 1634bc05954SSeongJae Park return NULL; 1644bc05954SSeongJae Park while (*nr_ids < max_nr_ids && pos < len) { 1654bc05954SSeongJae Park ret = sscanf(&str[pos], "%lu%n", &id, &parsed); 1664bc05954SSeongJae Park pos += parsed; 1674bc05954SSeongJae Park if (ret != 1) 1684bc05954SSeongJae Park break; 1694bc05954SSeongJae Park ids[*nr_ids] = id; 1704bc05954SSeongJae Park *nr_ids += 1; 1714bc05954SSeongJae Park } 1724bc05954SSeongJae Park 1734bc05954SSeongJae Park return ids; 1744bc05954SSeongJae Park } 1754bc05954SSeongJae Park 1764bc05954SSeongJae Park static void dbgfs_put_pids(unsigned long *ids, int nr_ids) 1774bc05954SSeongJae Park { 1784bc05954SSeongJae Park int i; 1794bc05954SSeongJae Park 1804bc05954SSeongJae Park for (i = 0; i < nr_ids; i++) 1814bc05954SSeongJae Park put_pid((struct pid *)ids[i]); 1824bc05954SSeongJae Park } 1834bc05954SSeongJae Park 1844bc05954SSeongJae Park static ssize_t dbgfs_target_ids_write(struct file *file, 1854bc05954SSeongJae Park const char __user *buf, size_t count, loff_t *ppos) 1864bc05954SSeongJae Park { 1874bc05954SSeongJae Park struct damon_ctx *ctx = file->private_data; 1884bc05954SSeongJae Park char *kbuf, *nrs; 1894bc05954SSeongJae Park unsigned long *targets; 1904bc05954SSeongJae Park ssize_t nr_targets; 1914bc05954SSeongJae Park ssize_t ret = count; 1924bc05954SSeongJae Park int i; 1934bc05954SSeongJae Park int err; 1944bc05954SSeongJae Park 1954bc05954SSeongJae Park kbuf = user_input_str(buf, count, ppos); 1964bc05954SSeongJae Park if (IS_ERR(kbuf)) 1974bc05954SSeongJae Park return PTR_ERR(kbuf); 1984bc05954SSeongJae Park 1994bc05954SSeongJae Park nrs = kbuf; 2004bc05954SSeongJae Park 2014bc05954SSeongJae Park targets = str_to_target_ids(nrs, ret, &nr_targets); 2024bc05954SSeongJae Park if (!targets) { 2034bc05954SSeongJae Park ret = -ENOMEM; 2044bc05954SSeongJae Park goto out; 2054bc05954SSeongJae Park } 2064bc05954SSeongJae Park 2074bc05954SSeongJae Park if (targetid_is_pid(ctx)) { 2084bc05954SSeongJae Park for (i = 0; i < nr_targets; i++) { 2094bc05954SSeongJae Park targets[i] = (unsigned long)find_get_pid( 2104bc05954SSeongJae Park (int)targets[i]); 2114bc05954SSeongJae Park if (!targets[i]) { 2124bc05954SSeongJae Park dbgfs_put_pids(targets, i); 2134bc05954SSeongJae Park ret = -EINVAL; 2144bc05954SSeongJae Park goto free_targets_out; 2154bc05954SSeongJae Park } 2164bc05954SSeongJae Park } 2174bc05954SSeongJae Park } 2184bc05954SSeongJae Park 2194bc05954SSeongJae Park mutex_lock(&ctx->kdamond_lock); 2204bc05954SSeongJae Park if (ctx->kdamond) { 2214bc05954SSeongJae Park if (targetid_is_pid(ctx)) 2224bc05954SSeongJae Park dbgfs_put_pids(targets, nr_targets); 2234bc05954SSeongJae Park ret = -EBUSY; 2244bc05954SSeongJae Park goto unlock_out; 2254bc05954SSeongJae Park } 2264bc05954SSeongJae Park 2274bc05954SSeongJae Park err = damon_set_targets(ctx, targets, nr_targets); 2284bc05954SSeongJae Park if (err) { 2294bc05954SSeongJae Park if (targetid_is_pid(ctx)) 2304bc05954SSeongJae Park dbgfs_put_pids(targets, nr_targets); 2314bc05954SSeongJae Park ret = err; 2324bc05954SSeongJae Park } 2334bc05954SSeongJae Park 2344bc05954SSeongJae Park unlock_out: 2354bc05954SSeongJae Park mutex_unlock(&ctx->kdamond_lock); 2364bc05954SSeongJae Park free_targets_out: 2374bc05954SSeongJae Park kfree(targets); 2384bc05954SSeongJae Park out: 2394bc05954SSeongJae Park kfree(kbuf); 2404bc05954SSeongJae Park return ret; 2414bc05954SSeongJae Park } 2424bc05954SSeongJae Park 243429538e8SSeongJae Park static ssize_t dbgfs_kdamond_pid_read(struct file *file, 244429538e8SSeongJae Park char __user *buf, size_t count, loff_t *ppos) 245429538e8SSeongJae Park { 246429538e8SSeongJae Park struct damon_ctx *ctx = file->private_data; 247429538e8SSeongJae Park char *kbuf; 248429538e8SSeongJae Park ssize_t len; 249429538e8SSeongJae Park 250429538e8SSeongJae Park kbuf = kmalloc(count, GFP_KERNEL); 251429538e8SSeongJae Park if (!kbuf) 252429538e8SSeongJae Park return -ENOMEM; 253429538e8SSeongJae Park 254429538e8SSeongJae Park mutex_lock(&ctx->kdamond_lock); 255429538e8SSeongJae Park if (ctx->kdamond) 256429538e8SSeongJae Park len = scnprintf(kbuf, count, "%d\n", ctx->kdamond->pid); 257429538e8SSeongJae Park else 258429538e8SSeongJae Park len = scnprintf(kbuf, count, "none\n"); 259429538e8SSeongJae Park mutex_unlock(&ctx->kdamond_lock); 260429538e8SSeongJae Park if (!len) 261429538e8SSeongJae Park goto out; 262429538e8SSeongJae Park len = simple_read_from_buffer(buf, count, ppos, kbuf, len); 263429538e8SSeongJae Park 264429538e8SSeongJae Park out: 265429538e8SSeongJae Park kfree(kbuf); 266429538e8SSeongJae Park return len; 267429538e8SSeongJae Park } 268429538e8SSeongJae Park 2694bc05954SSeongJae Park static int damon_dbgfs_open(struct inode *inode, struct file *file) 2704bc05954SSeongJae Park { 2714bc05954SSeongJae Park file->private_data = inode->i_private; 2724bc05954SSeongJae Park 2734bc05954SSeongJae Park return nonseekable_open(inode, file); 2744bc05954SSeongJae Park } 2754bc05954SSeongJae Park 2764bc05954SSeongJae Park static const struct file_operations attrs_fops = { 2774bc05954SSeongJae Park .open = damon_dbgfs_open, 2784bc05954SSeongJae Park .read = dbgfs_attrs_read, 2794bc05954SSeongJae Park .write = dbgfs_attrs_write, 2804bc05954SSeongJae Park }; 2814bc05954SSeongJae Park 2824bc05954SSeongJae Park static const struct file_operations target_ids_fops = { 2834bc05954SSeongJae Park .open = damon_dbgfs_open, 2844bc05954SSeongJae Park .read = dbgfs_target_ids_read, 2854bc05954SSeongJae Park .write = dbgfs_target_ids_write, 2864bc05954SSeongJae Park }; 2874bc05954SSeongJae Park 288429538e8SSeongJae Park static const struct file_operations kdamond_pid_fops = { 289429538e8SSeongJae Park .open = damon_dbgfs_open, 290429538e8SSeongJae Park .read = dbgfs_kdamond_pid_read, 291429538e8SSeongJae Park }; 292429538e8SSeongJae Park 2934bc05954SSeongJae Park static void dbgfs_fill_ctx_dir(struct dentry *dir, struct damon_ctx *ctx) 2944bc05954SSeongJae Park { 295429538e8SSeongJae Park const char * const file_names[] = {"attrs", "target_ids", 296429538e8SSeongJae Park "kdamond_pid"}; 297429538e8SSeongJae Park const struct file_operations *fops[] = {&attrs_fops, &target_ids_fops, 298429538e8SSeongJae Park &kdamond_pid_fops}; 2994bc05954SSeongJae Park int i; 3004bc05954SSeongJae Park 3014bc05954SSeongJae Park for (i = 0; i < ARRAY_SIZE(file_names); i++) 3024bc05954SSeongJae Park debugfs_create_file(file_names[i], 0600, dir, ctx, fops[i]); 3034bc05954SSeongJae Park } 3044bc05954SSeongJae Park 3054bc05954SSeongJae Park static int dbgfs_before_terminate(struct damon_ctx *ctx) 3064bc05954SSeongJae Park { 3074bc05954SSeongJae Park struct damon_target *t, *next; 3084bc05954SSeongJae Park 3094bc05954SSeongJae Park if (!targetid_is_pid(ctx)) 3104bc05954SSeongJae Park return 0; 3114bc05954SSeongJae Park 3124bc05954SSeongJae Park damon_for_each_target_safe(t, next, ctx) { 3134bc05954SSeongJae Park put_pid((struct pid *)t->id); 3144bc05954SSeongJae Park damon_destroy_target(t); 3154bc05954SSeongJae Park } 3164bc05954SSeongJae Park return 0; 3174bc05954SSeongJae Park } 3184bc05954SSeongJae Park 3194bc05954SSeongJae Park static struct damon_ctx *dbgfs_new_ctx(void) 3204bc05954SSeongJae Park { 3214bc05954SSeongJae Park struct damon_ctx *ctx; 3224bc05954SSeongJae Park 3234bc05954SSeongJae Park ctx = damon_new_ctx(); 3244bc05954SSeongJae Park if (!ctx) 3254bc05954SSeongJae Park return NULL; 3264bc05954SSeongJae Park 3274bc05954SSeongJae Park damon_va_set_primitives(ctx); 3284bc05954SSeongJae Park ctx->callback.before_terminate = dbgfs_before_terminate; 3294bc05954SSeongJae Park return ctx; 3304bc05954SSeongJae Park } 3314bc05954SSeongJae Park 33275c1c2b5SSeongJae Park static void dbgfs_destroy_ctx(struct damon_ctx *ctx) 33375c1c2b5SSeongJae Park { 33475c1c2b5SSeongJae Park damon_destroy_ctx(ctx); 33575c1c2b5SSeongJae Park } 33675c1c2b5SSeongJae Park 33775c1c2b5SSeongJae Park /* 33875c1c2b5SSeongJae Park * Make a context of @name and create a debugfs directory for it. 33975c1c2b5SSeongJae Park * 34075c1c2b5SSeongJae Park * This function should be called while holding damon_dbgfs_lock. 34175c1c2b5SSeongJae Park * 34275c1c2b5SSeongJae Park * Returns 0 on success, negative error code otherwise. 34375c1c2b5SSeongJae Park */ 34475c1c2b5SSeongJae Park static int dbgfs_mk_context(char *name) 34575c1c2b5SSeongJae Park { 34675c1c2b5SSeongJae Park struct dentry *root, **new_dirs, *new_dir; 34775c1c2b5SSeongJae Park struct damon_ctx **new_ctxs, *new_ctx; 34875c1c2b5SSeongJae Park 34975c1c2b5SSeongJae Park if (damon_nr_running_ctxs()) 35075c1c2b5SSeongJae Park return -EBUSY; 35175c1c2b5SSeongJae Park 35275c1c2b5SSeongJae Park new_ctxs = krealloc(dbgfs_ctxs, sizeof(*dbgfs_ctxs) * 35375c1c2b5SSeongJae Park (dbgfs_nr_ctxs + 1), GFP_KERNEL); 35475c1c2b5SSeongJae Park if (!new_ctxs) 35575c1c2b5SSeongJae Park return -ENOMEM; 35675c1c2b5SSeongJae Park dbgfs_ctxs = new_ctxs; 35775c1c2b5SSeongJae Park 35875c1c2b5SSeongJae Park new_dirs = krealloc(dbgfs_dirs, sizeof(*dbgfs_dirs) * 35975c1c2b5SSeongJae Park (dbgfs_nr_ctxs + 1), GFP_KERNEL); 36075c1c2b5SSeongJae Park if (!new_dirs) 36175c1c2b5SSeongJae Park return -ENOMEM; 36275c1c2b5SSeongJae Park dbgfs_dirs = new_dirs; 36375c1c2b5SSeongJae Park 36475c1c2b5SSeongJae Park root = dbgfs_dirs[0]; 36575c1c2b5SSeongJae Park if (!root) 36675c1c2b5SSeongJae Park return -ENOENT; 36775c1c2b5SSeongJae Park 36875c1c2b5SSeongJae Park new_dir = debugfs_create_dir(name, root); 36975c1c2b5SSeongJae Park dbgfs_dirs[dbgfs_nr_ctxs] = new_dir; 37075c1c2b5SSeongJae Park 37175c1c2b5SSeongJae Park new_ctx = dbgfs_new_ctx(); 37275c1c2b5SSeongJae Park if (!new_ctx) { 37375c1c2b5SSeongJae Park debugfs_remove(new_dir); 37475c1c2b5SSeongJae Park dbgfs_dirs[dbgfs_nr_ctxs] = NULL; 37575c1c2b5SSeongJae Park return -ENOMEM; 37675c1c2b5SSeongJae Park } 37775c1c2b5SSeongJae Park 37875c1c2b5SSeongJae Park dbgfs_ctxs[dbgfs_nr_ctxs] = new_ctx; 37975c1c2b5SSeongJae Park dbgfs_fill_ctx_dir(dbgfs_dirs[dbgfs_nr_ctxs], 38075c1c2b5SSeongJae Park dbgfs_ctxs[dbgfs_nr_ctxs]); 38175c1c2b5SSeongJae Park dbgfs_nr_ctxs++; 38275c1c2b5SSeongJae Park 38375c1c2b5SSeongJae Park return 0; 38475c1c2b5SSeongJae Park } 38575c1c2b5SSeongJae Park 38675c1c2b5SSeongJae Park static ssize_t dbgfs_mk_context_write(struct file *file, 38775c1c2b5SSeongJae Park const char __user *buf, size_t count, loff_t *ppos) 38875c1c2b5SSeongJae Park { 38975c1c2b5SSeongJae Park char *kbuf; 39075c1c2b5SSeongJae Park char *ctx_name; 39175c1c2b5SSeongJae Park ssize_t ret = count; 39275c1c2b5SSeongJae Park int err; 39375c1c2b5SSeongJae Park 39475c1c2b5SSeongJae Park kbuf = user_input_str(buf, count, ppos); 39575c1c2b5SSeongJae Park if (IS_ERR(kbuf)) 39675c1c2b5SSeongJae Park return PTR_ERR(kbuf); 39775c1c2b5SSeongJae Park ctx_name = kmalloc(count + 1, GFP_KERNEL); 39875c1c2b5SSeongJae Park if (!ctx_name) { 39975c1c2b5SSeongJae Park kfree(kbuf); 40075c1c2b5SSeongJae Park return -ENOMEM; 40175c1c2b5SSeongJae Park } 40275c1c2b5SSeongJae Park 40375c1c2b5SSeongJae Park /* Trim white space */ 40475c1c2b5SSeongJae Park if (sscanf(kbuf, "%s", ctx_name) != 1) { 40575c1c2b5SSeongJae Park ret = -EINVAL; 40675c1c2b5SSeongJae Park goto out; 40775c1c2b5SSeongJae Park } 40875c1c2b5SSeongJae Park 40975c1c2b5SSeongJae Park mutex_lock(&damon_dbgfs_lock); 41075c1c2b5SSeongJae Park err = dbgfs_mk_context(ctx_name); 41175c1c2b5SSeongJae Park if (err) 41275c1c2b5SSeongJae Park ret = err; 41375c1c2b5SSeongJae Park mutex_unlock(&damon_dbgfs_lock); 41475c1c2b5SSeongJae Park 41575c1c2b5SSeongJae Park out: 41675c1c2b5SSeongJae Park kfree(kbuf); 41775c1c2b5SSeongJae Park kfree(ctx_name); 41875c1c2b5SSeongJae Park return ret; 41975c1c2b5SSeongJae Park } 42075c1c2b5SSeongJae Park 42175c1c2b5SSeongJae Park /* 42275c1c2b5SSeongJae Park * Remove a context of @name and its debugfs directory. 42375c1c2b5SSeongJae Park * 42475c1c2b5SSeongJae Park * This function should be called while holding damon_dbgfs_lock. 42575c1c2b5SSeongJae Park * 42675c1c2b5SSeongJae Park * Return 0 on success, negative error code otherwise. 42775c1c2b5SSeongJae Park */ 42875c1c2b5SSeongJae Park static int dbgfs_rm_context(char *name) 42975c1c2b5SSeongJae Park { 43075c1c2b5SSeongJae Park struct dentry *root, *dir, **new_dirs; 43175c1c2b5SSeongJae Park struct damon_ctx **new_ctxs; 43275c1c2b5SSeongJae Park int i, j; 43375c1c2b5SSeongJae Park 43475c1c2b5SSeongJae Park if (damon_nr_running_ctxs()) 43575c1c2b5SSeongJae Park return -EBUSY; 43675c1c2b5SSeongJae Park 43775c1c2b5SSeongJae Park root = dbgfs_dirs[0]; 43875c1c2b5SSeongJae Park if (!root) 43975c1c2b5SSeongJae Park return -ENOENT; 44075c1c2b5SSeongJae Park 44175c1c2b5SSeongJae Park dir = debugfs_lookup(name, root); 44275c1c2b5SSeongJae Park if (!dir) 44375c1c2b5SSeongJae Park return -ENOENT; 44475c1c2b5SSeongJae Park 44575c1c2b5SSeongJae Park new_dirs = kmalloc_array(dbgfs_nr_ctxs - 1, sizeof(*dbgfs_dirs), 44675c1c2b5SSeongJae Park GFP_KERNEL); 44775c1c2b5SSeongJae Park if (!new_dirs) 44875c1c2b5SSeongJae Park return -ENOMEM; 44975c1c2b5SSeongJae Park 45075c1c2b5SSeongJae Park new_ctxs = kmalloc_array(dbgfs_nr_ctxs - 1, sizeof(*dbgfs_ctxs), 45175c1c2b5SSeongJae Park GFP_KERNEL); 45275c1c2b5SSeongJae Park if (!new_ctxs) { 45375c1c2b5SSeongJae Park kfree(new_dirs); 45475c1c2b5SSeongJae Park return -ENOMEM; 45575c1c2b5SSeongJae Park } 45675c1c2b5SSeongJae Park 45775c1c2b5SSeongJae Park for (i = 0, j = 0; i < dbgfs_nr_ctxs; i++) { 45875c1c2b5SSeongJae Park if (dbgfs_dirs[i] == dir) { 45975c1c2b5SSeongJae Park debugfs_remove(dbgfs_dirs[i]); 46075c1c2b5SSeongJae Park dbgfs_destroy_ctx(dbgfs_ctxs[i]); 46175c1c2b5SSeongJae Park continue; 46275c1c2b5SSeongJae Park } 46375c1c2b5SSeongJae Park new_dirs[j] = dbgfs_dirs[i]; 46475c1c2b5SSeongJae Park new_ctxs[j++] = dbgfs_ctxs[i]; 46575c1c2b5SSeongJae Park } 46675c1c2b5SSeongJae Park 46775c1c2b5SSeongJae Park kfree(dbgfs_dirs); 46875c1c2b5SSeongJae Park kfree(dbgfs_ctxs); 46975c1c2b5SSeongJae Park 47075c1c2b5SSeongJae Park dbgfs_dirs = new_dirs; 47175c1c2b5SSeongJae Park dbgfs_ctxs = new_ctxs; 47275c1c2b5SSeongJae Park dbgfs_nr_ctxs--; 47375c1c2b5SSeongJae Park 47475c1c2b5SSeongJae Park return 0; 47575c1c2b5SSeongJae Park } 47675c1c2b5SSeongJae Park 47775c1c2b5SSeongJae Park static ssize_t dbgfs_rm_context_write(struct file *file, 47875c1c2b5SSeongJae Park const char __user *buf, size_t count, loff_t *ppos) 47975c1c2b5SSeongJae Park { 48075c1c2b5SSeongJae Park char *kbuf; 48175c1c2b5SSeongJae Park ssize_t ret = count; 48275c1c2b5SSeongJae Park int err; 48375c1c2b5SSeongJae Park char *ctx_name; 48475c1c2b5SSeongJae Park 48575c1c2b5SSeongJae Park kbuf = user_input_str(buf, count, ppos); 48675c1c2b5SSeongJae Park if (IS_ERR(kbuf)) 48775c1c2b5SSeongJae Park return PTR_ERR(kbuf); 48875c1c2b5SSeongJae Park ctx_name = kmalloc(count + 1, GFP_KERNEL); 48975c1c2b5SSeongJae Park if (!ctx_name) { 49075c1c2b5SSeongJae Park kfree(kbuf); 49175c1c2b5SSeongJae Park return -ENOMEM; 49275c1c2b5SSeongJae Park } 49375c1c2b5SSeongJae Park 49475c1c2b5SSeongJae Park /* Trim white space */ 49575c1c2b5SSeongJae Park if (sscanf(kbuf, "%s", ctx_name) != 1) { 49675c1c2b5SSeongJae Park ret = -EINVAL; 49775c1c2b5SSeongJae Park goto out; 49875c1c2b5SSeongJae Park } 49975c1c2b5SSeongJae Park 50075c1c2b5SSeongJae Park mutex_lock(&damon_dbgfs_lock); 50175c1c2b5SSeongJae Park err = dbgfs_rm_context(ctx_name); 50275c1c2b5SSeongJae Park if (err) 50375c1c2b5SSeongJae Park ret = err; 50475c1c2b5SSeongJae Park mutex_unlock(&damon_dbgfs_lock); 50575c1c2b5SSeongJae Park 50675c1c2b5SSeongJae Park out: 50775c1c2b5SSeongJae Park kfree(kbuf); 50875c1c2b5SSeongJae Park kfree(ctx_name); 50975c1c2b5SSeongJae Park return ret; 51075c1c2b5SSeongJae Park } 51175c1c2b5SSeongJae Park 5124bc05954SSeongJae Park static ssize_t dbgfs_monitor_on_read(struct file *file, 5134bc05954SSeongJae Park char __user *buf, size_t count, loff_t *ppos) 5144bc05954SSeongJae Park { 5154bc05954SSeongJae Park char monitor_on_buf[5]; 5164bc05954SSeongJae Park bool monitor_on = damon_nr_running_ctxs() != 0; 5174bc05954SSeongJae Park int len; 5184bc05954SSeongJae Park 5194bc05954SSeongJae Park len = scnprintf(monitor_on_buf, 5, monitor_on ? "on\n" : "off\n"); 5204bc05954SSeongJae Park 5214bc05954SSeongJae Park return simple_read_from_buffer(buf, count, ppos, monitor_on_buf, len); 5224bc05954SSeongJae Park } 5234bc05954SSeongJae Park 5244bc05954SSeongJae Park static ssize_t dbgfs_monitor_on_write(struct file *file, 5254bc05954SSeongJae Park const char __user *buf, size_t count, loff_t *ppos) 5264bc05954SSeongJae Park { 5274bc05954SSeongJae Park ssize_t ret = count; 5284bc05954SSeongJae Park char *kbuf; 5294bc05954SSeongJae Park int err; 5304bc05954SSeongJae Park 5314bc05954SSeongJae Park kbuf = user_input_str(buf, count, ppos); 5324bc05954SSeongJae Park if (IS_ERR(kbuf)) 5334bc05954SSeongJae Park return PTR_ERR(kbuf); 5344bc05954SSeongJae Park 5354bc05954SSeongJae Park /* Remove white space */ 5364bc05954SSeongJae Park if (sscanf(kbuf, "%s", kbuf) != 1) { 5374bc05954SSeongJae Park kfree(kbuf); 5384bc05954SSeongJae Park return -EINVAL; 5394bc05954SSeongJae Park } 5404bc05954SSeongJae Park 5414bc05954SSeongJae Park if (!strncmp(kbuf, "on", count)) 5424bc05954SSeongJae Park err = damon_start(dbgfs_ctxs, dbgfs_nr_ctxs); 5434bc05954SSeongJae Park else if (!strncmp(kbuf, "off", count)) 5444bc05954SSeongJae Park err = damon_stop(dbgfs_ctxs, dbgfs_nr_ctxs); 5454bc05954SSeongJae Park else 5464bc05954SSeongJae Park err = -EINVAL; 5474bc05954SSeongJae Park 5484bc05954SSeongJae Park if (err) 5494bc05954SSeongJae Park ret = err; 5504bc05954SSeongJae Park kfree(kbuf); 5514bc05954SSeongJae Park return ret; 5524bc05954SSeongJae Park } 5534bc05954SSeongJae Park 55475c1c2b5SSeongJae Park static const struct file_operations mk_contexts_fops = { 55575c1c2b5SSeongJae Park .write = dbgfs_mk_context_write, 55675c1c2b5SSeongJae Park }; 55775c1c2b5SSeongJae Park 55875c1c2b5SSeongJae Park static const struct file_operations rm_contexts_fops = { 55975c1c2b5SSeongJae Park .write = dbgfs_rm_context_write, 56075c1c2b5SSeongJae Park }; 56175c1c2b5SSeongJae Park 5624bc05954SSeongJae Park static const struct file_operations monitor_on_fops = { 5634bc05954SSeongJae Park .read = dbgfs_monitor_on_read, 5644bc05954SSeongJae Park .write = dbgfs_monitor_on_write, 5654bc05954SSeongJae Park }; 5664bc05954SSeongJae Park 5674bc05954SSeongJae Park static int __init __damon_dbgfs_init(void) 5684bc05954SSeongJae Park { 5694bc05954SSeongJae Park struct dentry *dbgfs_root; 57075c1c2b5SSeongJae Park const char * const file_names[] = {"mk_contexts", "rm_contexts", 57175c1c2b5SSeongJae Park "monitor_on"}; 57275c1c2b5SSeongJae Park const struct file_operations *fops[] = {&mk_contexts_fops, 57375c1c2b5SSeongJae Park &rm_contexts_fops, &monitor_on_fops}; 5744bc05954SSeongJae Park int i; 5754bc05954SSeongJae Park 5764bc05954SSeongJae Park dbgfs_root = debugfs_create_dir("damon", NULL); 5774bc05954SSeongJae Park 5784bc05954SSeongJae Park for (i = 0; i < ARRAY_SIZE(file_names); i++) 5794bc05954SSeongJae Park debugfs_create_file(file_names[i], 0600, dbgfs_root, NULL, 5804bc05954SSeongJae Park fops[i]); 5814bc05954SSeongJae Park dbgfs_fill_ctx_dir(dbgfs_root, dbgfs_ctxs[0]); 5824bc05954SSeongJae Park 5834bc05954SSeongJae Park dbgfs_dirs = kmalloc_array(1, sizeof(dbgfs_root), GFP_KERNEL); 5844bc05954SSeongJae Park if (!dbgfs_dirs) { 5854bc05954SSeongJae Park debugfs_remove(dbgfs_root); 5864bc05954SSeongJae Park return -ENOMEM; 5874bc05954SSeongJae Park } 5884bc05954SSeongJae Park dbgfs_dirs[0] = dbgfs_root; 5894bc05954SSeongJae Park 5904bc05954SSeongJae Park return 0; 5914bc05954SSeongJae Park } 5924bc05954SSeongJae Park 5934bc05954SSeongJae Park /* 5944bc05954SSeongJae Park * Functions for the initialization 5954bc05954SSeongJae Park */ 5964bc05954SSeongJae Park 5974bc05954SSeongJae Park static int __init damon_dbgfs_init(void) 5984bc05954SSeongJae Park { 5994bc05954SSeongJae Park int rc; 6004bc05954SSeongJae Park 6014bc05954SSeongJae Park dbgfs_ctxs = kmalloc(sizeof(*dbgfs_ctxs), GFP_KERNEL); 6024bc05954SSeongJae Park if (!dbgfs_ctxs) 6034bc05954SSeongJae Park return -ENOMEM; 6044bc05954SSeongJae Park dbgfs_ctxs[0] = dbgfs_new_ctx(); 6054bc05954SSeongJae Park if (!dbgfs_ctxs[0]) { 6064bc05954SSeongJae Park kfree(dbgfs_ctxs); 6074bc05954SSeongJae Park return -ENOMEM; 6084bc05954SSeongJae Park } 6094bc05954SSeongJae Park dbgfs_nr_ctxs = 1; 6104bc05954SSeongJae Park 6114bc05954SSeongJae Park rc = __damon_dbgfs_init(); 6124bc05954SSeongJae Park if (rc) { 6134bc05954SSeongJae Park kfree(dbgfs_ctxs[0]); 6144bc05954SSeongJae Park kfree(dbgfs_ctxs); 6154bc05954SSeongJae Park pr_err("%s: dbgfs init failed\n", __func__); 6164bc05954SSeongJae Park } 6174bc05954SSeongJae Park 6184bc05954SSeongJae Park return rc; 6194bc05954SSeongJae Park } 6204bc05954SSeongJae Park 6214bc05954SSeongJae Park module_init(damon_dbgfs_init); 622*17ccae8bSSeongJae Park 623*17ccae8bSSeongJae Park #include "dbgfs-test.h" 624