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", 58cbeaa77bSSeongJae Park ctx->attrs.sample_interval, ctx->attrs.aggr_interval, 59cbeaa77bSSeongJae Park ctx->attrs.ops_update_interval, 60cbeaa77bSSeongJae Park ctx->attrs.min_nr_regions, ctx->attrs.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; 70*bead3b00SSeongJae Park struct damon_attrs attrs; 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", 79*bead3b00SSeongJae Park &attrs.sample_interval, &attrs.aggr_interval, 80*bead3b00SSeongJae Park &attrs.ops_update_interval, 81*bead3b00SSeongJae Park &attrs.min_nr_regions, 82*bead3b00SSeongJae Park &attrs.max_nr_regions) != 5) { 834bc05954SSeongJae Park ret = -EINVAL; 844bc05954SSeongJae Park goto out; 854bc05954SSeongJae Park } 864bc05954SSeongJae Park 874bc05954SSeongJae Park mutex_lock(&ctx->kdamond_lock); 884bc05954SSeongJae Park if (ctx->kdamond) { 894bc05954SSeongJae Park ret = -EBUSY; 904bc05954SSeongJae Park goto unlock_out; 914bc05954SSeongJae Park } 924bc05954SSeongJae Park 93*bead3b00SSeongJae Park ret = damon_set_attrs(ctx, &attrs); 949210622aSRongwei Wang if (!ret) 959210622aSRongwei Wang ret = count; 964bc05954SSeongJae Park unlock_out: 974bc05954SSeongJae Park mutex_unlock(&ctx->kdamond_lock); 984bc05954SSeongJae Park out: 994bc05954SSeongJae Park kfree(kbuf); 1004bc05954SSeongJae Park return ret; 1014bc05954SSeongJae Park } 1024bc05954SSeongJae Park 103c364f9afSSeongJae Park /* 104c364f9afSSeongJae Park * Return corresponding dbgfs' scheme action value (int) for the given 105c364f9afSSeongJae Park * damos_action if the given damos_action value is valid and supported by 106c364f9afSSeongJae Park * dbgfs, negative error code otherwise. 107c364f9afSSeongJae Park */ 108c364f9afSSeongJae Park static int damos_action_to_dbgfs_scheme_action(enum damos_action action) 109c364f9afSSeongJae Park { 110c364f9afSSeongJae Park switch (action) { 111c364f9afSSeongJae Park case DAMOS_WILLNEED: 112c364f9afSSeongJae Park return 0; 113c364f9afSSeongJae Park case DAMOS_COLD: 114c364f9afSSeongJae Park return 1; 115c364f9afSSeongJae Park case DAMOS_PAGEOUT: 116c364f9afSSeongJae Park return 2; 117c364f9afSSeongJae Park case DAMOS_HUGEPAGE: 118c364f9afSSeongJae Park return 3; 119c364f9afSSeongJae Park case DAMOS_NOHUGEPAGE: 120c364f9afSSeongJae Park return 4; 121c364f9afSSeongJae Park case DAMOS_STAT: 122c364f9afSSeongJae Park return 5; 123c364f9afSSeongJae Park default: 124c364f9afSSeongJae Park return -EINVAL; 125c364f9afSSeongJae Park } 126c364f9afSSeongJae Park } 127c364f9afSSeongJae Park 128af122dd8SSeongJae Park static ssize_t sprint_schemes(struct damon_ctx *c, char *buf, ssize_t len) 129af122dd8SSeongJae Park { 130af122dd8SSeongJae Park struct damos *s; 131af122dd8SSeongJae Park int written = 0; 132af122dd8SSeongJae Park int rc; 133af122dd8SSeongJae Park 134af122dd8SSeongJae Park damon_for_each_scheme(s, c) { 135af122dd8SSeongJae Park rc = scnprintf(&buf[written], len - written, 1363a619fdbSSeongJae 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", 137f5a79d7cSYajun Deng s->pattern.min_sz_region, 138f5a79d7cSYajun Deng s->pattern.max_sz_region, 139f5a79d7cSYajun Deng s->pattern.min_nr_accesses, 140f5a79d7cSYajun Deng s->pattern.max_nr_accesses, 141f5a79d7cSYajun Deng s->pattern.min_age_region, 142f5a79d7cSYajun Deng s->pattern.max_age_region, 143c364f9afSSeongJae Park damos_action_to_dbgfs_scheme_action(s->action), 144d7d0ec85SSeongJae Park s->quota.ms, s->quota.sz, 145d7d0ec85SSeongJae Park s->quota.reset_interval, 146f4a68b4aSSeongJae Park s->quota.weight_sz, 147f4a68b4aSSeongJae Park s->quota.weight_nr_accesses, 148f4a68b4aSSeongJae Park s->quota.weight_age, 149ae666a6dSSeongJae Park s->wmarks.metric, s->wmarks.interval, 150ae666a6dSSeongJae Park s->wmarks.high, s->wmarks.mid, s->wmarks.low, 1513a619fdbSSeongJae Park s->stat.nr_tried, s->stat.sz_tried, 1523a619fdbSSeongJae Park s->stat.nr_applied, s->stat.sz_applied, 1533a619fdbSSeongJae Park s->stat.qt_exceeds); 154af122dd8SSeongJae Park if (!rc) 155af122dd8SSeongJae Park return -ENOMEM; 156af122dd8SSeongJae Park 157af122dd8SSeongJae Park written += rc; 158af122dd8SSeongJae Park } 159af122dd8SSeongJae Park return written; 160af122dd8SSeongJae Park } 161af122dd8SSeongJae Park 162af122dd8SSeongJae Park static ssize_t dbgfs_schemes_read(struct file *file, char __user *buf, 163af122dd8SSeongJae Park size_t count, loff_t *ppos) 164af122dd8SSeongJae Park { 165af122dd8SSeongJae Park struct damon_ctx *ctx = file->private_data; 166af122dd8SSeongJae Park char *kbuf; 167af122dd8SSeongJae Park ssize_t len; 168af122dd8SSeongJae Park 169db7a347bSSeongJae Park kbuf = kmalloc(count, GFP_KERNEL | __GFP_NOWARN); 170af122dd8SSeongJae Park if (!kbuf) 171af122dd8SSeongJae Park return -ENOMEM; 172af122dd8SSeongJae Park 173af122dd8SSeongJae Park mutex_lock(&ctx->kdamond_lock); 174af122dd8SSeongJae Park len = sprint_schemes(ctx, kbuf, count); 175af122dd8SSeongJae Park mutex_unlock(&ctx->kdamond_lock); 176af122dd8SSeongJae Park if (len < 0) 177af122dd8SSeongJae Park goto out; 178af122dd8SSeongJae Park len = simple_read_from_buffer(buf, count, ppos, kbuf, len); 179af122dd8SSeongJae Park 180af122dd8SSeongJae Park out: 181af122dd8SSeongJae Park kfree(kbuf); 182af122dd8SSeongJae Park return len; 183af122dd8SSeongJae Park } 184af122dd8SSeongJae Park 185af122dd8SSeongJae Park static void free_schemes_arr(struct damos **schemes, ssize_t nr_schemes) 186af122dd8SSeongJae Park { 187af122dd8SSeongJae Park ssize_t i; 188af122dd8SSeongJae Park 189af122dd8SSeongJae Park for (i = 0; i < nr_schemes; i++) 190af122dd8SSeongJae Park kfree(schemes[i]); 191af122dd8SSeongJae Park kfree(schemes); 192af122dd8SSeongJae Park } 193af122dd8SSeongJae Park 194c364f9afSSeongJae Park /* 195c364f9afSSeongJae Park * Return corresponding damos_action for the given dbgfs input for a scheme 196c364f9afSSeongJae Park * action if the input is valid, negative error code otherwise. 197c364f9afSSeongJae Park */ 198c364f9afSSeongJae Park static enum damos_action dbgfs_scheme_action_to_damos_action(int dbgfs_action) 199af122dd8SSeongJae Park { 200c364f9afSSeongJae Park switch (dbgfs_action) { 201c364f9afSSeongJae Park case 0: 202c364f9afSSeongJae Park return DAMOS_WILLNEED; 203c364f9afSSeongJae Park case 1: 204c364f9afSSeongJae Park return DAMOS_COLD; 205c364f9afSSeongJae Park case 2: 206c364f9afSSeongJae Park return DAMOS_PAGEOUT; 207c364f9afSSeongJae Park case 3: 208c364f9afSSeongJae Park return DAMOS_HUGEPAGE; 209c364f9afSSeongJae Park case 4: 210c364f9afSSeongJae Park return DAMOS_NOHUGEPAGE; 211c364f9afSSeongJae Park case 5: 212c364f9afSSeongJae Park return DAMOS_STAT; 213af122dd8SSeongJae Park default: 214c364f9afSSeongJae Park return -EINVAL; 215af122dd8SSeongJae Park } 216af122dd8SSeongJae Park } 217af122dd8SSeongJae Park 218af122dd8SSeongJae Park /* 219af122dd8SSeongJae Park * Converts a string into an array of struct damos pointers 220af122dd8SSeongJae Park * 221af122dd8SSeongJae Park * Returns an array of struct damos pointers that converted if the conversion 222af122dd8SSeongJae Park * success, or NULL otherwise. 223af122dd8SSeongJae Park */ 224af122dd8SSeongJae Park static struct damos **str_to_schemes(const char *str, ssize_t len, 225af122dd8SSeongJae Park ssize_t *nr_schemes) 226af122dd8SSeongJae Park { 227af122dd8SSeongJae Park struct damos *scheme, **schemes; 228af122dd8SSeongJae Park const int max_nr_schemes = 256; 229af122dd8SSeongJae Park int pos = 0, parsed, ret; 230c364f9afSSeongJae Park unsigned int action_input; 231c364f9afSSeongJae Park enum damos_action action; 232af122dd8SSeongJae Park 233af122dd8SSeongJae Park schemes = kmalloc_array(max_nr_schemes, sizeof(scheme), 234af122dd8SSeongJae Park GFP_KERNEL); 235af122dd8SSeongJae Park if (!schemes) 236af122dd8SSeongJae Park return NULL; 237af122dd8SSeongJae Park 238af122dd8SSeongJae Park *nr_schemes = 0; 239af122dd8SSeongJae Park while (pos < len && *nr_schemes < max_nr_schemes) { 240f5a79d7cSYajun Deng struct damos_access_pattern pattern = {}; 2412b8a248dSSeongJae Park struct damos_quota quota = {}; 242ae666a6dSSeongJae Park struct damos_watermarks wmarks; 2432b8a248dSSeongJae Park 244f4a68b4aSSeongJae Park ret = sscanf(&str[pos], 245ae666a6dSSeongJae Park "%lu %lu %u %u %u %u %u %lu %lu %lu %u %u %u %u %lu %lu %lu %lu%n", 246f5a79d7cSYajun Deng &pattern.min_sz_region, &pattern.max_sz_region, 247f5a79d7cSYajun Deng &pattern.min_nr_accesses, 248f5a79d7cSYajun Deng &pattern.max_nr_accesses, 249f5a79d7cSYajun Deng &pattern.min_age_region, 250f5a79d7cSYajun Deng &pattern.max_age_region, 251f5a79d7cSYajun Deng &action_input, "a.ms, 252f4a68b4aSSeongJae Park "a.sz, "a.reset_interval, 253f4a68b4aSSeongJae Park "a.weight_sz, "a.weight_nr_accesses, 254ae666a6dSSeongJae Park "a.weight_age, &wmarks.metric, 255ae666a6dSSeongJae Park &wmarks.interval, &wmarks.high, &wmarks.mid, 256ae666a6dSSeongJae Park &wmarks.low, &parsed); 257ae666a6dSSeongJae Park if (ret != 18) 258af122dd8SSeongJae Park break; 259c364f9afSSeongJae Park action = dbgfs_scheme_action_to_damos_action(action_input); 260c364f9afSSeongJae Park if ((int)action < 0) 261af122dd8SSeongJae Park goto fail; 262af122dd8SSeongJae Park 263f5a79d7cSYajun Deng if (pattern.min_sz_region > pattern.max_sz_region || 264f5a79d7cSYajun Deng pattern.min_nr_accesses > pattern.max_nr_accesses || 265f5a79d7cSYajun Deng pattern.min_age_region > pattern.max_age_region) 266c89ae63eSXin Hao goto fail; 267c89ae63eSXin Hao 268c89ae63eSXin Hao if (wmarks.high < wmarks.mid || wmarks.high < wmarks.low || 269c89ae63eSXin Hao wmarks.mid < wmarks.low) 270c89ae63eSXin Hao goto fail; 271c89ae63eSXin Hao 272af122dd8SSeongJae Park pos += parsed; 273f5a79d7cSYajun Deng scheme = damon_new_scheme(&pattern, action, "a, &wmarks); 274af122dd8SSeongJae Park if (!scheme) 275af122dd8SSeongJae Park goto fail; 276af122dd8SSeongJae Park 277af122dd8SSeongJae Park schemes[*nr_schemes] = scheme; 278af122dd8SSeongJae Park *nr_schemes += 1; 279af122dd8SSeongJae Park } 280af122dd8SSeongJae Park return schemes; 281af122dd8SSeongJae Park fail: 282af122dd8SSeongJae Park free_schemes_arr(schemes, *nr_schemes); 283af122dd8SSeongJae Park return NULL; 284af122dd8SSeongJae Park } 285af122dd8SSeongJae Park 286af122dd8SSeongJae Park static ssize_t dbgfs_schemes_write(struct file *file, const char __user *buf, 287af122dd8SSeongJae Park size_t count, loff_t *ppos) 288af122dd8SSeongJae Park { 289af122dd8SSeongJae Park struct damon_ctx *ctx = file->private_data; 290af122dd8SSeongJae Park char *kbuf; 291af122dd8SSeongJae Park struct damos **schemes; 2929210622aSRongwei Wang ssize_t nr_schemes = 0, ret; 293af122dd8SSeongJae Park 294af122dd8SSeongJae Park kbuf = user_input_str(buf, count, ppos); 295af122dd8SSeongJae Park if (IS_ERR(kbuf)) 296af122dd8SSeongJae Park return PTR_ERR(kbuf); 297af122dd8SSeongJae Park 2989210622aSRongwei Wang schemes = str_to_schemes(kbuf, count, &nr_schemes); 299af122dd8SSeongJae Park if (!schemes) { 300af122dd8SSeongJae Park ret = -EINVAL; 301af122dd8SSeongJae Park goto out; 302af122dd8SSeongJae Park } 303af122dd8SSeongJae Park 304af122dd8SSeongJae Park mutex_lock(&ctx->kdamond_lock); 305af122dd8SSeongJae Park if (ctx->kdamond) { 306af122dd8SSeongJae Park ret = -EBUSY; 307af122dd8SSeongJae Park goto unlock_out; 308af122dd8SSeongJae Park } 309af122dd8SSeongJae Park 3109210622aSRongwei Wang ret = damon_set_schemes(ctx, schemes, nr_schemes); 3119210622aSRongwei Wang if (!ret) { 3129210622aSRongwei Wang ret = count; 313af122dd8SSeongJae Park nr_schemes = 0; 3149210622aSRongwei Wang } 3159210622aSRongwei Wang 316af122dd8SSeongJae Park unlock_out: 317af122dd8SSeongJae Park mutex_unlock(&ctx->kdamond_lock); 318af122dd8SSeongJae Park free_schemes_arr(schemes, nr_schemes); 319af122dd8SSeongJae Park out: 320af122dd8SSeongJae Park kfree(kbuf); 321af122dd8SSeongJae Park return ret; 322af122dd8SSeongJae Park } 323af122dd8SSeongJae Park 3244bc05954SSeongJae Park static ssize_t sprint_target_ids(struct damon_ctx *ctx, char *buf, ssize_t len) 3254bc05954SSeongJae Park { 3264bc05954SSeongJae Park struct damon_target *t; 3271971bd63SSeongJae Park int id; 3284bc05954SSeongJae Park int written = 0; 3294bc05954SSeongJae Park int rc; 3304bc05954SSeongJae Park 3314bc05954SSeongJae Park damon_for_each_target(t, ctx) { 332c9e124e0SSeongJae Park if (damon_target_has_pid(ctx)) 3334bc05954SSeongJae Park /* Show pid numbers to debugfs users */ 3341971bd63SSeongJae Park id = pid_vnr(t->pid); 3351971bd63SSeongJae Park else 3361971bd63SSeongJae Park /* Show 42 for physical address space, just for fun */ 3371971bd63SSeongJae Park id = 42; 3384bc05954SSeongJae Park 3391971bd63SSeongJae Park rc = scnprintf(&buf[written], len - written, "%d ", id); 3404bc05954SSeongJae Park if (!rc) 3414bc05954SSeongJae Park return -ENOMEM; 3424bc05954SSeongJae Park written += rc; 3434bc05954SSeongJae Park } 3444bc05954SSeongJae Park if (written) 3454bc05954SSeongJae Park written -= 1; 3464bc05954SSeongJae Park written += scnprintf(&buf[written], len - written, "\n"); 3474bc05954SSeongJae Park return written; 3484bc05954SSeongJae Park } 3494bc05954SSeongJae Park 3504bc05954SSeongJae Park static ssize_t dbgfs_target_ids_read(struct file *file, 3514bc05954SSeongJae Park char __user *buf, size_t count, loff_t *ppos) 3524bc05954SSeongJae Park { 3534bc05954SSeongJae Park struct damon_ctx *ctx = file->private_data; 3544bc05954SSeongJae Park ssize_t len; 3554bc05954SSeongJae Park char ids_buf[320]; 3564bc05954SSeongJae Park 3574bc05954SSeongJae Park mutex_lock(&ctx->kdamond_lock); 3584bc05954SSeongJae Park len = sprint_target_ids(ctx, ids_buf, 320); 3594bc05954SSeongJae Park mutex_unlock(&ctx->kdamond_lock); 3604bc05954SSeongJae Park if (len < 0) 3614bc05954SSeongJae Park return len; 3624bc05954SSeongJae Park 3634bc05954SSeongJae Park return simple_read_from_buffer(buf, count, ppos, ids_buf, len); 3644bc05954SSeongJae Park } 3654bc05954SSeongJae Park 3664bc05954SSeongJae Park /* 3671971bd63SSeongJae Park * Converts a string into an integers array 3684bc05954SSeongJae Park * 3691971bd63SSeongJae Park * Returns an array of integers array if the conversion success, or NULL 3701971bd63SSeongJae Park * otherwise. 3714bc05954SSeongJae Park */ 3721971bd63SSeongJae Park static int *str_to_ints(const char *str, ssize_t len, ssize_t *nr_ints) 3734bc05954SSeongJae Park { 3741971bd63SSeongJae Park int *array; 3751971bd63SSeongJae Park const int max_nr_ints = 32; 3761971bd63SSeongJae Park int nr; 3774bc05954SSeongJae Park int pos = 0, parsed, ret; 3784bc05954SSeongJae Park 3791971bd63SSeongJae Park *nr_ints = 0; 3801971bd63SSeongJae Park array = kmalloc_array(max_nr_ints, sizeof(*array), GFP_KERNEL); 3811971bd63SSeongJae Park if (!array) 3824bc05954SSeongJae Park return NULL; 3831971bd63SSeongJae Park while (*nr_ints < max_nr_ints && pos < len) { 3841971bd63SSeongJae Park ret = sscanf(&str[pos], "%d%n", &nr, &parsed); 3854bc05954SSeongJae Park pos += parsed; 3864bc05954SSeongJae Park if (ret != 1) 3874bc05954SSeongJae Park break; 3881971bd63SSeongJae Park array[*nr_ints] = nr; 3891971bd63SSeongJae Park *nr_ints += 1; 3904bc05954SSeongJae Park } 3914bc05954SSeongJae Park 3921971bd63SSeongJae Park return array; 3934bc05954SSeongJae Park } 3944bc05954SSeongJae Park 3951971bd63SSeongJae Park static void dbgfs_put_pids(struct pid **pids, int nr_pids) 3964bc05954SSeongJae Park { 3974bc05954SSeongJae Park int i; 3984bc05954SSeongJae Park 3991971bd63SSeongJae Park for (i = 0; i < nr_pids; i++) 4001971bd63SSeongJae Park put_pid(pids[i]); 4011971bd63SSeongJae Park } 4021971bd63SSeongJae Park 4031971bd63SSeongJae Park /* 4041971bd63SSeongJae Park * Converts a string into an struct pid pointers array 4051971bd63SSeongJae Park * 4061971bd63SSeongJae Park * Returns an array of struct pid pointers if the conversion success, or NULL 4071971bd63SSeongJae Park * otherwise. 4081971bd63SSeongJae Park */ 4091971bd63SSeongJae Park static struct pid **str_to_pids(const char *str, ssize_t len, ssize_t *nr_pids) 4101971bd63SSeongJae Park { 4111971bd63SSeongJae Park int *ints; 4121971bd63SSeongJae Park ssize_t nr_ints; 4131971bd63SSeongJae Park struct pid **pids; 4141971bd63SSeongJae Park 4151971bd63SSeongJae Park *nr_pids = 0; 4161971bd63SSeongJae Park 4171971bd63SSeongJae Park ints = str_to_ints(str, len, &nr_ints); 4181971bd63SSeongJae Park if (!ints) 4191971bd63SSeongJae Park return NULL; 4201971bd63SSeongJae Park 4211971bd63SSeongJae Park pids = kmalloc_array(nr_ints, sizeof(*pids), GFP_KERNEL); 4221971bd63SSeongJae Park if (!pids) 4231971bd63SSeongJae Park goto out; 4241971bd63SSeongJae Park 4251971bd63SSeongJae Park for (; *nr_pids < nr_ints; (*nr_pids)++) { 4261971bd63SSeongJae Park pids[*nr_pids] = find_get_pid(ints[*nr_pids]); 4271971bd63SSeongJae Park if (!pids[*nr_pids]) { 4281971bd63SSeongJae Park dbgfs_put_pids(pids, *nr_pids); 4291971bd63SSeongJae Park kfree(ints); 4301971bd63SSeongJae Park kfree(pids); 4311971bd63SSeongJae Park return NULL; 4321971bd63SSeongJae Park } 4331971bd63SSeongJae Park } 4341971bd63SSeongJae Park 4351971bd63SSeongJae Park out: 4361971bd63SSeongJae Park kfree(ints); 4371971bd63SSeongJae Park return pids; 4384bc05954SSeongJae Park } 4394bc05954SSeongJae Park 44043642825SSeongJae Park /* 44143642825SSeongJae Park * dbgfs_set_targets() - Set monitoring targets. 44243642825SSeongJae Park * @ctx: monitoring context 4431971bd63SSeongJae Park * @nr_targets: number of targets 4441971bd63SSeongJae Park * @pids: array of target pids (size is same to @nr_targets) 44543642825SSeongJae Park * 4461971bd63SSeongJae Park * This function should not be called while the kdamond is running. @pids is 4471971bd63SSeongJae Park * ignored if the context is not configured to have pid in each target. On 4481971bd63SSeongJae Park * failure, reference counts of all pids in @pids are decremented. 44943642825SSeongJae Park * 45043642825SSeongJae Park * Return: 0 on success, negative error code otherwise. 45143642825SSeongJae Park */ 4521971bd63SSeongJae Park static int dbgfs_set_targets(struct damon_ctx *ctx, ssize_t nr_targets, 4531971bd63SSeongJae Park struct pid **pids) 45443642825SSeongJae Park { 45543642825SSeongJae Park ssize_t i; 45643642825SSeongJae Park struct damon_target *t, *next; 45743642825SSeongJae Park 45843642825SSeongJae Park damon_for_each_target_safe(t, next, ctx) { 459c9e124e0SSeongJae Park if (damon_target_has_pid(ctx)) 4601971bd63SSeongJae Park put_pid(t->pid); 46143642825SSeongJae Park damon_destroy_target(t); 46243642825SSeongJae Park } 46343642825SSeongJae Park 4641971bd63SSeongJae Park for (i = 0; i < nr_targets; i++) { 4651971bd63SSeongJae Park t = damon_new_target(); 46643642825SSeongJae Park if (!t) { 46743642825SSeongJae Park damon_for_each_target_safe(t, next, ctx) 46843642825SSeongJae Park damon_destroy_target(t); 469c9e124e0SSeongJae Park if (damon_target_has_pid(ctx)) 4701971bd63SSeongJae Park dbgfs_put_pids(pids, nr_targets); 47143642825SSeongJae Park return -ENOMEM; 47243642825SSeongJae Park } 473c9e124e0SSeongJae Park if (damon_target_has_pid(ctx)) 4741971bd63SSeongJae Park t->pid = pids[i]; 47543642825SSeongJae Park damon_add_target(ctx, t); 47643642825SSeongJae Park } 47743642825SSeongJae Park 47843642825SSeongJae Park return 0; 47943642825SSeongJae Park } 48043642825SSeongJae Park 4814bc05954SSeongJae Park static ssize_t dbgfs_target_ids_write(struct file *file, 4824bc05954SSeongJae Park const char __user *buf, size_t count, loff_t *ppos) 4834bc05954SSeongJae Park { 4844bc05954SSeongJae Park struct damon_ctx *ctx = file->private_data; 485c026291aSSeongJae Park bool id_is_pid = true; 48670b84808SSeongJae Park char *kbuf; 4871971bd63SSeongJae Park struct pid **target_pids = NULL; 4884bc05954SSeongJae Park ssize_t nr_targets; 4899210622aSRongwei Wang ssize_t ret; 4904bc05954SSeongJae Park 4914bc05954SSeongJae Park kbuf = user_input_str(buf, count, ppos); 4924bc05954SSeongJae Park if (IS_ERR(kbuf)) 4934bc05954SSeongJae Park return PTR_ERR(kbuf); 4944bc05954SSeongJae Park 495c026291aSSeongJae Park if (!strncmp(kbuf, "paddr\n", count)) { 496c026291aSSeongJae Park id_is_pid = false; 4971971bd63SSeongJae Park nr_targets = 1; 4984bc05954SSeongJae Park } 4994bc05954SSeongJae Park 500c026291aSSeongJae Park if (id_is_pid) { 5011971bd63SSeongJae Park target_pids = str_to_pids(kbuf, count, &nr_targets); 5021971bd63SSeongJae Park if (!target_pids) { 5031971bd63SSeongJae Park ret = -ENOMEM; 5041971bd63SSeongJae Park goto out; 5054bc05954SSeongJae Park } 5064bc05954SSeongJae Park } 5074bc05954SSeongJae Park 5084bc05954SSeongJae Park mutex_lock(&ctx->kdamond_lock); 5094bc05954SSeongJae Park if (ctx->kdamond) { 510c026291aSSeongJae Park if (id_is_pid) 5111971bd63SSeongJae Park dbgfs_put_pids(target_pids, nr_targets); 5124bc05954SSeongJae Park ret = -EBUSY; 5134bc05954SSeongJae Park goto unlock_out; 5144bc05954SSeongJae Park } 5154bc05954SSeongJae Park 516ebb3f994SSeongJae Park /* remove previously set targets */ 5171971bd63SSeongJae Park dbgfs_set_targets(ctx, 0, NULL); 518da7aaca0SSeongJae Park if (!nr_targets) { 519da7aaca0SSeongJae Park ret = count; 520da7aaca0SSeongJae Park goto unlock_out; 521da7aaca0SSeongJae Park } 522c026291aSSeongJae Park 523c026291aSSeongJae Park /* Configure the context for the address space type */ 524c026291aSSeongJae Park if (id_is_pid) 525da7aaca0SSeongJae Park ret = damon_select_ops(ctx, DAMON_OPS_VADDR); 526c026291aSSeongJae Park else 527da7aaca0SSeongJae Park ret = damon_select_ops(ctx, DAMON_OPS_PADDR); 528da7aaca0SSeongJae Park if (ret) 529da7aaca0SSeongJae Park goto unlock_out; 530c026291aSSeongJae Park 5311971bd63SSeongJae Park ret = dbgfs_set_targets(ctx, nr_targets, target_pids); 53243642825SSeongJae Park if (!ret) 5339210622aSRongwei Wang ret = count; 5344bc05954SSeongJae Park 5354bc05954SSeongJae Park unlock_out: 5364bc05954SSeongJae Park mutex_unlock(&ctx->kdamond_lock); 5371971bd63SSeongJae Park kfree(target_pids); 5384bc05954SSeongJae Park out: 5394bc05954SSeongJae Park kfree(kbuf); 5404bc05954SSeongJae Park return ret; 5414bc05954SSeongJae Park } 5424bc05954SSeongJae Park 54390bebce9SSeongJae Park static ssize_t sprint_init_regions(struct damon_ctx *c, char *buf, ssize_t len) 54490bebce9SSeongJae Park { 54590bebce9SSeongJae Park struct damon_target *t; 54690bebce9SSeongJae Park struct damon_region *r; 547144760f8SSeongJae Park int target_idx = 0; 54890bebce9SSeongJae Park int written = 0; 54990bebce9SSeongJae Park int rc; 55090bebce9SSeongJae Park 55190bebce9SSeongJae Park damon_for_each_target(t, c) { 55290bebce9SSeongJae Park damon_for_each_region(r, t) { 55390bebce9SSeongJae Park rc = scnprintf(&buf[written], len - written, 554144760f8SSeongJae Park "%d %lu %lu\n", 555144760f8SSeongJae Park target_idx, r->ar.start, r->ar.end); 55690bebce9SSeongJae Park if (!rc) 55790bebce9SSeongJae Park return -ENOMEM; 55890bebce9SSeongJae Park written += rc; 55990bebce9SSeongJae Park } 560144760f8SSeongJae Park target_idx++; 56190bebce9SSeongJae Park } 56290bebce9SSeongJae Park return written; 56390bebce9SSeongJae Park } 56490bebce9SSeongJae Park 56590bebce9SSeongJae Park static ssize_t dbgfs_init_regions_read(struct file *file, char __user *buf, 56690bebce9SSeongJae Park size_t count, loff_t *ppos) 56790bebce9SSeongJae Park { 56890bebce9SSeongJae Park struct damon_ctx *ctx = file->private_data; 56990bebce9SSeongJae Park char *kbuf; 57090bebce9SSeongJae Park ssize_t len; 57190bebce9SSeongJae Park 572db7a347bSSeongJae Park kbuf = kmalloc(count, GFP_KERNEL | __GFP_NOWARN); 57390bebce9SSeongJae Park if (!kbuf) 57490bebce9SSeongJae Park return -ENOMEM; 57590bebce9SSeongJae Park 57690bebce9SSeongJae Park mutex_lock(&ctx->kdamond_lock); 57790bebce9SSeongJae Park if (ctx->kdamond) { 57890bebce9SSeongJae Park mutex_unlock(&ctx->kdamond_lock); 57990bebce9SSeongJae Park len = -EBUSY; 58090bebce9SSeongJae Park goto out; 58190bebce9SSeongJae Park } 58290bebce9SSeongJae Park 58390bebce9SSeongJae Park len = sprint_init_regions(ctx, kbuf, count); 58490bebce9SSeongJae Park mutex_unlock(&ctx->kdamond_lock); 58590bebce9SSeongJae Park if (len < 0) 58690bebce9SSeongJae Park goto out; 58790bebce9SSeongJae Park len = simple_read_from_buffer(buf, count, ppos, kbuf, len); 58890bebce9SSeongJae Park 58990bebce9SSeongJae Park out: 59090bebce9SSeongJae Park kfree(kbuf); 59190bebce9SSeongJae Park return len; 59290bebce9SSeongJae Park } 59390bebce9SSeongJae Park 594144760f8SSeongJae Park static int add_init_region(struct damon_ctx *c, int target_idx, 595144760f8SSeongJae Park struct damon_addr_range *ar) 59690bebce9SSeongJae Park { 59790bebce9SSeongJae Park struct damon_target *t; 59890bebce9SSeongJae Park struct damon_region *r, *prev; 599144760f8SSeongJae Park unsigned long idx = 0; 60090bebce9SSeongJae Park int rc = -EINVAL; 60190bebce9SSeongJae Park 60290bebce9SSeongJae Park if (ar->start >= ar->end) 60390bebce9SSeongJae Park return -EINVAL; 60490bebce9SSeongJae Park 60590bebce9SSeongJae Park damon_for_each_target(t, c) { 606144760f8SSeongJae Park if (idx++ == target_idx) { 60790bebce9SSeongJae Park r = damon_new_region(ar->start, ar->end); 60890bebce9SSeongJae Park if (!r) 60990bebce9SSeongJae Park return -ENOMEM; 61090bebce9SSeongJae Park damon_add_region(r, t); 61190bebce9SSeongJae Park if (damon_nr_regions(t) > 1) { 61290bebce9SSeongJae Park prev = damon_prev_region(r); 61390bebce9SSeongJae Park if (prev->ar.end > r->ar.start) { 61490bebce9SSeongJae Park damon_destroy_region(r, t); 61590bebce9SSeongJae Park return -EINVAL; 61690bebce9SSeongJae Park } 61790bebce9SSeongJae Park } 61890bebce9SSeongJae Park rc = 0; 61990bebce9SSeongJae Park } 62090bebce9SSeongJae Park } 62190bebce9SSeongJae Park return rc; 62290bebce9SSeongJae Park } 62390bebce9SSeongJae Park 62490bebce9SSeongJae Park static int set_init_regions(struct damon_ctx *c, const char *str, ssize_t len) 62590bebce9SSeongJae Park { 62690bebce9SSeongJae Park struct damon_target *t; 62790bebce9SSeongJae Park struct damon_region *r, *next; 62890bebce9SSeongJae Park int pos = 0, parsed, ret; 629144760f8SSeongJae Park int target_idx; 63090bebce9SSeongJae Park struct damon_addr_range ar; 63190bebce9SSeongJae Park int err; 63290bebce9SSeongJae Park 63390bebce9SSeongJae Park damon_for_each_target(t, c) { 63490bebce9SSeongJae Park damon_for_each_region_safe(r, next, t) 63590bebce9SSeongJae Park damon_destroy_region(r, t); 63690bebce9SSeongJae Park } 63790bebce9SSeongJae Park 63890bebce9SSeongJae Park while (pos < len) { 639144760f8SSeongJae Park ret = sscanf(&str[pos], "%d %lu %lu%n", 640144760f8SSeongJae Park &target_idx, &ar.start, &ar.end, &parsed); 64190bebce9SSeongJae Park if (ret != 3) 64290bebce9SSeongJae Park break; 643144760f8SSeongJae Park err = add_init_region(c, target_idx, &ar); 64490bebce9SSeongJae Park if (err) 64590bebce9SSeongJae Park goto fail; 64690bebce9SSeongJae Park pos += parsed; 64790bebce9SSeongJae Park } 64890bebce9SSeongJae Park 64990bebce9SSeongJae Park return 0; 65090bebce9SSeongJae Park 65190bebce9SSeongJae Park fail: 65290bebce9SSeongJae Park damon_for_each_target(t, c) { 65390bebce9SSeongJae Park damon_for_each_region_safe(r, next, t) 65490bebce9SSeongJae Park damon_destroy_region(r, t); 65590bebce9SSeongJae Park } 65690bebce9SSeongJae Park return err; 65790bebce9SSeongJae Park } 65890bebce9SSeongJae Park 65990bebce9SSeongJae Park static ssize_t dbgfs_init_regions_write(struct file *file, 66090bebce9SSeongJae Park const char __user *buf, size_t count, 66190bebce9SSeongJae Park loff_t *ppos) 66290bebce9SSeongJae Park { 66390bebce9SSeongJae Park struct damon_ctx *ctx = file->private_data; 66490bebce9SSeongJae Park char *kbuf; 66590bebce9SSeongJae Park ssize_t ret = count; 66690bebce9SSeongJae Park int err; 66790bebce9SSeongJae Park 66890bebce9SSeongJae Park kbuf = user_input_str(buf, count, ppos); 66990bebce9SSeongJae Park if (IS_ERR(kbuf)) 67090bebce9SSeongJae Park return PTR_ERR(kbuf); 67190bebce9SSeongJae Park 67290bebce9SSeongJae Park mutex_lock(&ctx->kdamond_lock); 67390bebce9SSeongJae Park if (ctx->kdamond) { 67490bebce9SSeongJae Park ret = -EBUSY; 67590bebce9SSeongJae Park goto unlock_out; 67690bebce9SSeongJae Park } 67790bebce9SSeongJae Park 67890bebce9SSeongJae Park err = set_init_regions(ctx, kbuf, ret); 67990bebce9SSeongJae Park if (err) 68090bebce9SSeongJae Park ret = err; 68190bebce9SSeongJae Park 68290bebce9SSeongJae Park unlock_out: 68390bebce9SSeongJae Park mutex_unlock(&ctx->kdamond_lock); 68490bebce9SSeongJae Park kfree(kbuf); 68590bebce9SSeongJae Park return ret; 68690bebce9SSeongJae Park } 68790bebce9SSeongJae Park 688429538e8SSeongJae Park static ssize_t dbgfs_kdamond_pid_read(struct file *file, 689429538e8SSeongJae Park char __user *buf, size_t count, loff_t *ppos) 690429538e8SSeongJae Park { 691429538e8SSeongJae Park struct damon_ctx *ctx = file->private_data; 692429538e8SSeongJae Park char *kbuf; 693429538e8SSeongJae Park ssize_t len; 694429538e8SSeongJae Park 695db7a347bSSeongJae Park kbuf = kmalloc(count, GFP_KERNEL | __GFP_NOWARN); 696429538e8SSeongJae Park if (!kbuf) 697429538e8SSeongJae Park return -ENOMEM; 698429538e8SSeongJae Park 699429538e8SSeongJae Park mutex_lock(&ctx->kdamond_lock); 700429538e8SSeongJae Park if (ctx->kdamond) 701429538e8SSeongJae Park len = scnprintf(kbuf, count, "%d\n", ctx->kdamond->pid); 702429538e8SSeongJae Park else 703429538e8SSeongJae Park len = scnprintf(kbuf, count, "none\n"); 704429538e8SSeongJae Park mutex_unlock(&ctx->kdamond_lock); 705429538e8SSeongJae Park if (!len) 706429538e8SSeongJae Park goto out; 707429538e8SSeongJae Park len = simple_read_from_buffer(buf, count, ppos, kbuf, len); 708429538e8SSeongJae Park 709429538e8SSeongJae Park out: 710429538e8SSeongJae Park kfree(kbuf); 711429538e8SSeongJae Park return len; 712429538e8SSeongJae Park } 713429538e8SSeongJae Park 7144bc05954SSeongJae Park static int damon_dbgfs_open(struct inode *inode, struct file *file) 7154bc05954SSeongJae Park { 7164bc05954SSeongJae Park file->private_data = inode->i_private; 7174bc05954SSeongJae Park 7184bc05954SSeongJae Park return nonseekable_open(inode, file); 7194bc05954SSeongJae Park } 7204bc05954SSeongJae Park 7214bc05954SSeongJae Park static const struct file_operations attrs_fops = { 7224bc05954SSeongJae Park .open = damon_dbgfs_open, 7234bc05954SSeongJae Park .read = dbgfs_attrs_read, 7244bc05954SSeongJae Park .write = dbgfs_attrs_write, 7254bc05954SSeongJae Park }; 7264bc05954SSeongJae Park 727af122dd8SSeongJae Park static const struct file_operations schemes_fops = { 728af122dd8SSeongJae Park .open = damon_dbgfs_open, 729af122dd8SSeongJae Park .read = dbgfs_schemes_read, 730af122dd8SSeongJae Park .write = dbgfs_schemes_write, 731af122dd8SSeongJae Park }; 732af122dd8SSeongJae Park 7334bc05954SSeongJae Park static const struct file_operations target_ids_fops = { 7344bc05954SSeongJae Park .open = damon_dbgfs_open, 7354bc05954SSeongJae Park .read = dbgfs_target_ids_read, 7364bc05954SSeongJae Park .write = dbgfs_target_ids_write, 7374bc05954SSeongJae Park }; 7384bc05954SSeongJae Park 73990bebce9SSeongJae Park static const struct file_operations init_regions_fops = { 74090bebce9SSeongJae Park .open = damon_dbgfs_open, 74190bebce9SSeongJae Park .read = dbgfs_init_regions_read, 74290bebce9SSeongJae Park .write = dbgfs_init_regions_write, 74390bebce9SSeongJae Park }; 74490bebce9SSeongJae Park 745429538e8SSeongJae Park static const struct file_operations kdamond_pid_fops = { 746429538e8SSeongJae Park .open = damon_dbgfs_open, 747429538e8SSeongJae Park .read = dbgfs_kdamond_pid_read, 748429538e8SSeongJae Park }; 749429538e8SSeongJae Park 7504bc05954SSeongJae Park static void dbgfs_fill_ctx_dir(struct dentry *dir, struct damon_ctx *ctx) 7514bc05954SSeongJae Park { 752af122dd8SSeongJae Park const char * const file_names[] = {"attrs", "schemes", "target_ids", 75390bebce9SSeongJae Park "init_regions", "kdamond_pid"}; 754af122dd8SSeongJae Park const struct file_operations *fops[] = {&attrs_fops, &schemes_fops, 75590bebce9SSeongJae Park &target_ids_fops, &init_regions_fops, &kdamond_pid_fops}; 7564bc05954SSeongJae Park int i; 7574bc05954SSeongJae Park 7584bc05954SSeongJae Park for (i = 0; i < ARRAY_SIZE(file_names); i++) 7594bc05954SSeongJae Park debugfs_create_file(file_names[i], 0600, dir, ctx, fops[i]); 7604bc05954SSeongJae Park } 7614bc05954SSeongJae Park 762658f9ae7SChangbin Du static void dbgfs_before_terminate(struct damon_ctx *ctx) 7634bc05954SSeongJae Park { 7644bc05954SSeongJae Park struct damon_target *t, *next; 7654bc05954SSeongJae Park 766c9e124e0SSeongJae Park if (!damon_target_has_pid(ctx)) 767658f9ae7SChangbin Du return; 7684bc05954SSeongJae Park 76934796417SSeongJae Park mutex_lock(&ctx->kdamond_lock); 7704bc05954SSeongJae Park damon_for_each_target_safe(t, next, ctx) { 7711971bd63SSeongJae Park put_pid(t->pid); 7724bc05954SSeongJae Park damon_destroy_target(t); 7734bc05954SSeongJae Park } 77434796417SSeongJae Park mutex_unlock(&ctx->kdamond_lock); 7754bc05954SSeongJae Park } 7764bc05954SSeongJae Park 7774bc05954SSeongJae Park static struct damon_ctx *dbgfs_new_ctx(void) 7784bc05954SSeongJae Park { 7794bc05954SSeongJae Park struct damon_ctx *ctx; 7804bc05954SSeongJae Park 7814bc05954SSeongJae Park ctx = damon_new_ctx(); 7824bc05954SSeongJae Park if (!ctx) 7834bc05954SSeongJae Park return NULL; 7844bc05954SSeongJae Park 7854a20865bSSeongJae Park if (damon_select_ops(ctx, DAMON_OPS_VADDR) && 7864a20865bSSeongJae Park damon_select_ops(ctx, DAMON_OPS_PADDR)) { 787da7aaca0SSeongJae Park damon_destroy_ctx(ctx); 788da7aaca0SSeongJae Park return NULL; 789da7aaca0SSeongJae Park } 7904bc05954SSeongJae Park ctx->callback.before_terminate = dbgfs_before_terminate; 7914bc05954SSeongJae Park return ctx; 7924bc05954SSeongJae Park } 7934bc05954SSeongJae Park 79475c1c2b5SSeongJae Park static void dbgfs_destroy_ctx(struct damon_ctx *ctx) 79575c1c2b5SSeongJae Park { 79675c1c2b5SSeongJae Park damon_destroy_ctx(ctx); 79775c1c2b5SSeongJae Park } 79875c1c2b5SSeongJae Park 79975c1c2b5SSeongJae Park /* 80075c1c2b5SSeongJae Park * Make a context of @name and create a debugfs directory for it. 80175c1c2b5SSeongJae Park * 80275c1c2b5SSeongJae Park * This function should be called while holding damon_dbgfs_lock. 80375c1c2b5SSeongJae Park * 80475c1c2b5SSeongJae Park * Returns 0 on success, negative error code otherwise. 80575c1c2b5SSeongJae Park */ 80675c1c2b5SSeongJae Park static int dbgfs_mk_context(char *name) 80775c1c2b5SSeongJae Park { 80875c1c2b5SSeongJae Park struct dentry *root, **new_dirs, *new_dir; 80975c1c2b5SSeongJae Park struct damon_ctx **new_ctxs, *new_ctx; 81075c1c2b5SSeongJae Park 81175c1c2b5SSeongJae Park if (damon_nr_running_ctxs()) 81275c1c2b5SSeongJae Park return -EBUSY; 81375c1c2b5SSeongJae Park 81475c1c2b5SSeongJae Park new_ctxs = krealloc(dbgfs_ctxs, sizeof(*dbgfs_ctxs) * 81575c1c2b5SSeongJae Park (dbgfs_nr_ctxs + 1), GFP_KERNEL); 81675c1c2b5SSeongJae Park if (!new_ctxs) 81775c1c2b5SSeongJae Park return -ENOMEM; 81875c1c2b5SSeongJae Park dbgfs_ctxs = new_ctxs; 81975c1c2b5SSeongJae Park 82075c1c2b5SSeongJae Park new_dirs = krealloc(dbgfs_dirs, sizeof(*dbgfs_dirs) * 82175c1c2b5SSeongJae Park (dbgfs_nr_ctxs + 1), GFP_KERNEL); 82275c1c2b5SSeongJae Park if (!new_dirs) 82375c1c2b5SSeongJae Park return -ENOMEM; 82475c1c2b5SSeongJae Park dbgfs_dirs = new_dirs; 82575c1c2b5SSeongJae Park 82675c1c2b5SSeongJae Park root = dbgfs_dirs[0]; 82775c1c2b5SSeongJae Park if (!root) 82875c1c2b5SSeongJae Park return -ENOENT; 82975c1c2b5SSeongJae Park 83075c1c2b5SSeongJae Park new_dir = debugfs_create_dir(name, root); 831d26f6070SBadari Pulavarty /* Below check is required for a potential duplicated name case */ 832d26f6070SBadari Pulavarty if (IS_ERR(new_dir)) 833d26f6070SBadari Pulavarty return PTR_ERR(new_dir); 83475c1c2b5SSeongJae Park dbgfs_dirs[dbgfs_nr_ctxs] = new_dir; 83575c1c2b5SSeongJae Park 83675c1c2b5SSeongJae Park new_ctx = dbgfs_new_ctx(); 83775c1c2b5SSeongJae Park if (!new_ctx) { 83875c1c2b5SSeongJae Park debugfs_remove(new_dir); 83975c1c2b5SSeongJae Park dbgfs_dirs[dbgfs_nr_ctxs] = NULL; 84075c1c2b5SSeongJae Park return -ENOMEM; 84175c1c2b5SSeongJae Park } 84275c1c2b5SSeongJae Park 84375c1c2b5SSeongJae Park dbgfs_ctxs[dbgfs_nr_ctxs] = new_ctx; 84475c1c2b5SSeongJae Park dbgfs_fill_ctx_dir(dbgfs_dirs[dbgfs_nr_ctxs], 84575c1c2b5SSeongJae Park dbgfs_ctxs[dbgfs_nr_ctxs]); 84675c1c2b5SSeongJae Park dbgfs_nr_ctxs++; 84775c1c2b5SSeongJae Park 84875c1c2b5SSeongJae Park return 0; 84975c1c2b5SSeongJae Park } 85075c1c2b5SSeongJae Park 85175c1c2b5SSeongJae Park static ssize_t dbgfs_mk_context_write(struct file *file, 85275c1c2b5SSeongJae Park const char __user *buf, size_t count, loff_t *ppos) 85375c1c2b5SSeongJae Park { 85475c1c2b5SSeongJae Park char *kbuf; 85575c1c2b5SSeongJae Park char *ctx_name; 8569210622aSRongwei Wang ssize_t ret; 85775c1c2b5SSeongJae Park 85875c1c2b5SSeongJae Park kbuf = user_input_str(buf, count, ppos); 85975c1c2b5SSeongJae Park if (IS_ERR(kbuf)) 86075c1c2b5SSeongJae Park return PTR_ERR(kbuf); 86175c1c2b5SSeongJae Park ctx_name = kmalloc(count + 1, GFP_KERNEL); 86275c1c2b5SSeongJae Park if (!ctx_name) { 86375c1c2b5SSeongJae Park kfree(kbuf); 86475c1c2b5SSeongJae Park return -ENOMEM; 86575c1c2b5SSeongJae Park } 86675c1c2b5SSeongJae Park 86775c1c2b5SSeongJae Park /* Trim white space */ 86875c1c2b5SSeongJae Park if (sscanf(kbuf, "%s", ctx_name) != 1) { 86975c1c2b5SSeongJae Park ret = -EINVAL; 87075c1c2b5SSeongJae Park goto out; 87175c1c2b5SSeongJae Park } 87275c1c2b5SSeongJae Park 87375c1c2b5SSeongJae Park mutex_lock(&damon_dbgfs_lock); 8749210622aSRongwei Wang ret = dbgfs_mk_context(ctx_name); 8759210622aSRongwei Wang if (!ret) 8769210622aSRongwei Wang ret = count; 87775c1c2b5SSeongJae Park mutex_unlock(&damon_dbgfs_lock); 87875c1c2b5SSeongJae Park 87975c1c2b5SSeongJae Park out: 88075c1c2b5SSeongJae Park kfree(kbuf); 88175c1c2b5SSeongJae Park kfree(ctx_name); 88275c1c2b5SSeongJae Park return ret; 88375c1c2b5SSeongJae Park } 88475c1c2b5SSeongJae Park 88575c1c2b5SSeongJae Park /* 88675c1c2b5SSeongJae Park * Remove a context of @name and its debugfs directory. 88775c1c2b5SSeongJae Park * 88875c1c2b5SSeongJae Park * This function should be called while holding damon_dbgfs_lock. 88975c1c2b5SSeongJae Park * 89075c1c2b5SSeongJae Park * Return 0 on success, negative error code otherwise. 89175c1c2b5SSeongJae Park */ 89275c1c2b5SSeongJae Park static int dbgfs_rm_context(char *name) 89375c1c2b5SSeongJae Park { 89475c1c2b5SSeongJae Park struct dentry *root, *dir, **new_dirs; 89575c1c2b5SSeongJae Park struct damon_ctx **new_ctxs; 89675c1c2b5SSeongJae Park int i, j; 8971552fd3eSGreg Kroah-Hartman int ret = 0; 89875c1c2b5SSeongJae Park 89975c1c2b5SSeongJae Park if (damon_nr_running_ctxs()) 90075c1c2b5SSeongJae Park return -EBUSY; 90175c1c2b5SSeongJae Park 90275c1c2b5SSeongJae Park root = dbgfs_dirs[0]; 90375c1c2b5SSeongJae Park if (!root) 90475c1c2b5SSeongJae Park return -ENOENT; 90575c1c2b5SSeongJae Park 90675c1c2b5SSeongJae Park dir = debugfs_lookup(name, root); 90775c1c2b5SSeongJae Park if (!dir) 90875c1c2b5SSeongJae Park return -ENOENT; 90975c1c2b5SSeongJae Park 91075c1c2b5SSeongJae Park new_dirs = kmalloc_array(dbgfs_nr_ctxs - 1, sizeof(*dbgfs_dirs), 91175c1c2b5SSeongJae Park GFP_KERNEL); 9121552fd3eSGreg Kroah-Hartman if (!new_dirs) { 9131552fd3eSGreg Kroah-Hartman ret = -ENOMEM; 9141552fd3eSGreg Kroah-Hartman goto out_dput; 9151552fd3eSGreg Kroah-Hartman } 91675c1c2b5SSeongJae Park 91775c1c2b5SSeongJae Park new_ctxs = kmalloc_array(dbgfs_nr_ctxs - 1, sizeof(*dbgfs_ctxs), 91875c1c2b5SSeongJae Park GFP_KERNEL); 91975c1c2b5SSeongJae Park if (!new_ctxs) { 9201552fd3eSGreg Kroah-Hartman ret = -ENOMEM; 9211552fd3eSGreg Kroah-Hartman goto out_new_dirs; 92275c1c2b5SSeongJae Park } 92375c1c2b5SSeongJae Park 92475c1c2b5SSeongJae Park for (i = 0, j = 0; i < dbgfs_nr_ctxs; i++) { 92575c1c2b5SSeongJae Park if (dbgfs_dirs[i] == dir) { 92675c1c2b5SSeongJae Park debugfs_remove(dbgfs_dirs[i]); 92775c1c2b5SSeongJae Park dbgfs_destroy_ctx(dbgfs_ctxs[i]); 92875c1c2b5SSeongJae Park continue; 92975c1c2b5SSeongJae Park } 93075c1c2b5SSeongJae Park new_dirs[j] = dbgfs_dirs[i]; 93175c1c2b5SSeongJae Park new_ctxs[j++] = dbgfs_ctxs[i]; 93275c1c2b5SSeongJae Park } 93375c1c2b5SSeongJae Park 93475c1c2b5SSeongJae Park kfree(dbgfs_dirs); 93575c1c2b5SSeongJae Park kfree(dbgfs_ctxs); 93675c1c2b5SSeongJae Park 93775c1c2b5SSeongJae Park dbgfs_dirs = new_dirs; 93875c1c2b5SSeongJae Park dbgfs_ctxs = new_ctxs; 93975c1c2b5SSeongJae Park dbgfs_nr_ctxs--; 94075c1c2b5SSeongJae Park 9411552fd3eSGreg Kroah-Hartman goto out_dput; 9421552fd3eSGreg Kroah-Hartman 9431552fd3eSGreg Kroah-Hartman out_new_dirs: 9441552fd3eSGreg Kroah-Hartman kfree(new_dirs); 9451552fd3eSGreg Kroah-Hartman out_dput: 9461552fd3eSGreg Kroah-Hartman dput(dir); 9471552fd3eSGreg Kroah-Hartman return ret; 94875c1c2b5SSeongJae Park } 94975c1c2b5SSeongJae Park 95075c1c2b5SSeongJae Park static ssize_t dbgfs_rm_context_write(struct file *file, 95175c1c2b5SSeongJae Park const char __user *buf, size_t count, loff_t *ppos) 95275c1c2b5SSeongJae Park { 95375c1c2b5SSeongJae Park char *kbuf; 9549210622aSRongwei Wang ssize_t ret; 95575c1c2b5SSeongJae Park char *ctx_name; 95675c1c2b5SSeongJae Park 95775c1c2b5SSeongJae Park kbuf = user_input_str(buf, count, ppos); 95875c1c2b5SSeongJae Park if (IS_ERR(kbuf)) 95975c1c2b5SSeongJae Park return PTR_ERR(kbuf); 96075c1c2b5SSeongJae Park ctx_name = kmalloc(count + 1, GFP_KERNEL); 96175c1c2b5SSeongJae Park if (!ctx_name) { 96275c1c2b5SSeongJae Park kfree(kbuf); 96375c1c2b5SSeongJae Park return -ENOMEM; 96475c1c2b5SSeongJae Park } 96575c1c2b5SSeongJae Park 96675c1c2b5SSeongJae Park /* Trim white space */ 96775c1c2b5SSeongJae Park if (sscanf(kbuf, "%s", ctx_name) != 1) { 96875c1c2b5SSeongJae Park ret = -EINVAL; 96975c1c2b5SSeongJae Park goto out; 97075c1c2b5SSeongJae Park } 97175c1c2b5SSeongJae Park 97275c1c2b5SSeongJae Park mutex_lock(&damon_dbgfs_lock); 9739210622aSRongwei Wang ret = dbgfs_rm_context(ctx_name); 9749210622aSRongwei Wang if (!ret) 9759210622aSRongwei Wang ret = count; 97675c1c2b5SSeongJae Park mutex_unlock(&damon_dbgfs_lock); 97775c1c2b5SSeongJae Park 97875c1c2b5SSeongJae Park out: 97975c1c2b5SSeongJae Park kfree(kbuf); 98075c1c2b5SSeongJae Park kfree(ctx_name); 98175c1c2b5SSeongJae Park return ret; 98275c1c2b5SSeongJae Park } 98375c1c2b5SSeongJae Park 9844bc05954SSeongJae Park static ssize_t dbgfs_monitor_on_read(struct file *file, 9854bc05954SSeongJae Park char __user *buf, size_t count, loff_t *ppos) 9864bc05954SSeongJae Park { 9874bc05954SSeongJae Park char monitor_on_buf[5]; 9884bc05954SSeongJae Park bool monitor_on = damon_nr_running_ctxs() != 0; 9894bc05954SSeongJae Park int len; 9904bc05954SSeongJae Park 9914bc05954SSeongJae Park len = scnprintf(monitor_on_buf, 5, monitor_on ? "on\n" : "off\n"); 9924bc05954SSeongJae Park 9934bc05954SSeongJae Park return simple_read_from_buffer(buf, count, ppos, monitor_on_buf, len); 9944bc05954SSeongJae Park } 9954bc05954SSeongJae Park 9964bc05954SSeongJae Park static ssize_t dbgfs_monitor_on_write(struct file *file, 9974bc05954SSeongJae Park const char __user *buf, size_t count, loff_t *ppos) 9984bc05954SSeongJae Park { 9999210622aSRongwei Wang ssize_t ret; 10004bc05954SSeongJae Park char *kbuf; 10014bc05954SSeongJae Park 10024bc05954SSeongJae Park kbuf = user_input_str(buf, count, ppos); 10034bc05954SSeongJae Park if (IS_ERR(kbuf)) 10044bc05954SSeongJae Park return PTR_ERR(kbuf); 10054bc05954SSeongJae Park 10064bc05954SSeongJae Park /* Remove white space */ 10074bc05954SSeongJae Park if (sscanf(kbuf, "%s", kbuf) != 1) { 10084bc05954SSeongJae Park kfree(kbuf); 10094bc05954SSeongJae Park return -EINVAL; 10104bc05954SSeongJae Park } 10114bc05954SSeongJae Park 1012d78f3853SSeongJae Park mutex_lock(&damon_dbgfs_lock); 1013b5ca3e83SXin Hao if (!strncmp(kbuf, "on", count)) { 1014b5ca3e83SXin Hao int i; 1015b5ca3e83SXin Hao 1016b5ca3e83SXin Hao for (i = 0; i < dbgfs_nr_ctxs; i++) { 1017b5ca3e83SXin Hao if (damon_targets_empty(dbgfs_ctxs[i])) { 1018b5ca3e83SXin Hao kfree(kbuf); 1019d78f3853SSeongJae Park mutex_unlock(&damon_dbgfs_lock); 1020b5ca3e83SXin Hao return -EINVAL; 1021b5ca3e83SXin Hao } 1022b5ca3e83SXin Hao } 10238b9b0d33SSeongJae Park ret = damon_start(dbgfs_ctxs, dbgfs_nr_ctxs, true); 1024b5ca3e83SXin Hao } else if (!strncmp(kbuf, "off", count)) { 10259210622aSRongwei Wang ret = damon_stop(dbgfs_ctxs, dbgfs_nr_ctxs); 1026b5ca3e83SXin Hao } else { 10279210622aSRongwei Wang ret = -EINVAL; 1028b5ca3e83SXin Hao } 1029d78f3853SSeongJae Park mutex_unlock(&damon_dbgfs_lock); 10304bc05954SSeongJae Park 10319210622aSRongwei Wang if (!ret) 10329210622aSRongwei Wang ret = count; 10334bc05954SSeongJae Park kfree(kbuf); 10344bc05954SSeongJae Park return ret; 10354bc05954SSeongJae Park } 10364bc05954SSeongJae Park 103775c1c2b5SSeongJae Park static const struct file_operations mk_contexts_fops = { 103875c1c2b5SSeongJae Park .write = dbgfs_mk_context_write, 103975c1c2b5SSeongJae Park }; 104075c1c2b5SSeongJae Park 104175c1c2b5SSeongJae Park static const struct file_operations rm_contexts_fops = { 104275c1c2b5SSeongJae Park .write = dbgfs_rm_context_write, 104375c1c2b5SSeongJae Park }; 104475c1c2b5SSeongJae Park 10454bc05954SSeongJae Park static const struct file_operations monitor_on_fops = { 10464bc05954SSeongJae Park .read = dbgfs_monitor_on_read, 10474bc05954SSeongJae Park .write = dbgfs_monitor_on_write, 10484bc05954SSeongJae Park }; 10494bc05954SSeongJae Park 10504bc05954SSeongJae Park static int __init __damon_dbgfs_init(void) 10514bc05954SSeongJae Park { 10524bc05954SSeongJae Park struct dentry *dbgfs_root; 105375c1c2b5SSeongJae Park const char * const file_names[] = {"mk_contexts", "rm_contexts", 105475c1c2b5SSeongJae Park "monitor_on"}; 105575c1c2b5SSeongJae Park const struct file_operations *fops[] = {&mk_contexts_fops, 105675c1c2b5SSeongJae Park &rm_contexts_fops, &monitor_on_fops}; 10574bc05954SSeongJae Park int i; 10584bc05954SSeongJae Park 10594bc05954SSeongJae Park dbgfs_root = debugfs_create_dir("damon", NULL); 10604bc05954SSeongJae Park 10614bc05954SSeongJae Park for (i = 0; i < ARRAY_SIZE(file_names); i++) 10624bc05954SSeongJae Park debugfs_create_file(file_names[i], 0600, dbgfs_root, NULL, 10634bc05954SSeongJae Park fops[i]); 10644bc05954SSeongJae Park dbgfs_fill_ctx_dir(dbgfs_root, dbgfs_ctxs[0]); 10654bc05954SSeongJae Park 1066b2d4c646SKenneth Lee dbgfs_dirs = kmalloc(sizeof(dbgfs_root), GFP_KERNEL); 10674bc05954SSeongJae Park if (!dbgfs_dirs) { 10684bc05954SSeongJae Park debugfs_remove(dbgfs_root); 10694bc05954SSeongJae Park return -ENOMEM; 10704bc05954SSeongJae Park } 10714bc05954SSeongJae Park dbgfs_dirs[0] = dbgfs_root; 10724bc05954SSeongJae Park 10734bc05954SSeongJae Park return 0; 10744bc05954SSeongJae Park } 10754bc05954SSeongJae Park 10764bc05954SSeongJae Park /* 10774bc05954SSeongJae Park * Functions for the initialization 10784bc05954SSeongJae Park */ 10794bc05954SSeongJae Park 10804bc05954SSeongJae Park static int __init damon_dbgfs_init(void) 10814bc05954SSeongJae Park { 1082d78f3853SSeongJae Park int rc = -ENOMEM; 10834bc05954SSeongJae Park 1084d78f3853SSeongJae Park mutex_lock(&damon_dbgfs_lock); 10854bc05954SSeongJae Park dbgfs_ctxs = kmalloc(sizeof(*dbgfs_ctxs), GFP_KERNEL); 10864bc05954SSeongJae Park if (!dbgfs_ctxs) 1087d78f3853SSeongJae Park goto out; 10884bc05954SSeongJae Park dbgfs_ctxs[0] = dbgfs_new_ctx(); 10894bc05954SSeongJae Park if (!dbgfs_ctxs[0]) { 10904bc05954SSeongJae Park kfree(dbgfs_ctxs); 1091d78f3853SSeongJae Park goto out; 10924bc05954SSeongJae Park } 10934bc05954SSeongJae Park dbgfs_nr_ctxs = 1; 10944bc05954SSeongJae Park 10954bc05954SSeongJae Park rc = __damon_dbgfs_init(); 10964bc05954SSeongJae Park if (rc) { 10974bc05954SSeongJae Park kfree(dbgfs_ctxs[0]); 10984bc05954SSeongJae Park kfree(dbgfs_ctxs); 10994bc05954SSeongJae Park pr_err("%s: dbgfs init failed\n", __func__); 11004bc05954SSeongJae Park } 11014bc05954SSeongJae Park 1102d78f3853SSeongJae Park out: 1103d78f3853SSeongJae Park mutex_unlock(&damon_dbgfs_lock); 11044bc05954SSeongJae Park return rc; 11054bc05954SSeongJae Park } 11064bc05954SSeongJae Park 11074bc05954SSeongJae Park module_init(damon_dbgfs_init); 110817ccae8bSSeongJae Park 110917ccae8bSSeongJae Park #include "dbgfs-test.h" 1110