14bc05954SSeongJae Park // SPDX-License-Identifier: GPL-2.0 24bc05954SSeongJae Park /* 34bc05954SSeongJae Park * DAMON Debugfs Interface 44bc05954SSeongJae Park * 54bc05954SSeongJae Park * Author: SeongJae Park <sjpark@amazon.de> 64bc05954SSeongJae Park */ 74bc05954SSeongJae Park 84bc05954SSeongJae Park #define pr_fmt(fmt) "damon-dbgfs: " fmt 94bc05954SSeongJae Park 104bc05954SSeongJae Park #include <linux/damon.h> 114bc05954SSeongJae Park #include <linux/debugfs.h> 124bc05954SSeongJae Park #include <linux/file.h> 134bc05954SSeongJae Park #include <linux/mm.h> 144bc05954SSeongJae Park #include <linux/module.h> 154bc05954SSeongJae Park #include <linux/page_idle.h> 164bc05954SSeongJae Park #include <linux/slab.h> 174bc05954SSeongJae Park 184bc05954SSeongJae Park static struct damon_ctx **dbgfs_ctxs; 194bc05954SSeongJae Park static int dbgfs_nr_ctxs; 204bc05954SSeongJae Park static struct dentry **dbgfs_dirs; 2175c1c2b5SSeongJae Park static DEFINE_MUTEX(damon_dbgfs_lock); 224bc05954SSeongJae Park 234bc05954SSeongJae Park /* 244bc05954SSeongJae Park * Returns non-empty string on success, negative error code otherwise. 254bc05954SSeongJae Park */ 264bc05954SSeongJae Park static char *user_input_str(const char __user *buf, size_t count, loff_t *ppos) 274bc05954SSeongJae Park { 284bc05954SSeongJae Park char *kbuf; 294bc05954SSeongJae Park ssize_t ret; 304bc05954SSeongJae Park 314bc05954SSeongJae Park /* We do not accept continuous write */ 324bc05954SSeongJae Park if (*ppos) 334bc05954SSeongJae Park return ERR_PTR(-EINVAL); 344bc05954SSeongJae Park 35db7a347bSSeongJae Park kbuf = kmalloc(count + 1, GFP_KERNEL | __GFP_NOWARN); 364bc05954SSeongJae Park if (!kbuf) 374bc05954SSeongJae Park return ERR_PTR(-ENOMEM); 384bc05954SSeongJae Park 394bc05954SSeongJae Park ret = simple_write_to_buffer(kbuf, count + 1, ppos, buf, count); 404bc05954SSeongJae Park if (ret != count) { 414bc05954SSeongJae Park kfree(kbuf); 424bc05954SSeongJae Park return ERR_PTR(-EIO); 434bc05954SSeongJae Park } 444bc05954SSeongJae Park kbuf[ret] = '\0'; 454bc05954SSeongJae Park 464bc05954SSeongJae Park return kbuf; 474bc05954SSeongJae Park } 484bc05954SSeongJae Park 494bc05954SSeongJae Park static ssize_t dbgfs_attrs_read(struct file *file, 504bc05954SSeongJae Park char __user *buf, size_t count, loff_t *ppos) 514bc05954SSeongJae Park { 524bc05954SSeongJae Park struct damon_ctx *ctx = file->private_data; 534bc05954SSeongJae Park char kbuf[128]; 544bc05954SSeongJae Park int ret; 554bc05954SSeongJae Park 564bc05954SSeongJae Park mutex_lock(&ctx->kdamond_lock); 574bc05954SSeongJae Park ret = scnprintf(kbuf, ARRAY_SIZE(kbuf), "%lu %lu %lu %lu %lu\n", 584bc05954SSeongJae Park ctx->sample_interval, ctx->aggr_interval, 594bc05954SSeongJae Park ctx->primitive_update_interval, ctx->min_nr_regions, 604bc05954SSeongJae Park ctx->max_nr_regions); 614bc05954SSeongJae Park mutex_unlock(&ctx->kdamond_lock); 624bc05954SSeongJae Park 634bc05954SSeongJae Park return simple_read_from_buffer(buf, count, ppos, kbuf, ret); 644bc05954SSeongJae Park } 654bc05954SSeongJae Park 664bc05954SSeongJae Park static ssize_t dbgfs_attrs_write(struct file *file, 674bc05954SSeongJae Park const char __user *buf, size_t count, loff_t *ppos) 684bc05954SSeongJae Park { 694bc05954SSeongJae Park struct damon_ctx *ctx = file->private_data; 704bc05954SSeongJae Park unsigned long s, a, r, minr, maxr; 714bc05954SSeongJae Park char *kbuf; 729210622aSRongwei Wang ssize_t ret; 734bc05954SSeongJae Park 744bc05954SSeongJae Park kbuf = user_input_str(buf, count, ppos); 754bc05954SSeongJae Park if (IS_ERR(kbuf)) 764bc05954SSeongJae Park return PTR_ERR(kbuf); 774bc05954SSeongJae Park 784bc05954SSeongJae Park if (sscanf(kbuf, "%lu %lu %lu %lu %lu", 794bc05954SSeongJae Park &s, &a, &r, &minr, &maxr) != 5) { 804bc05954SSeongJae Park ret = -EINVAL; 814bc05954SSeongJae Park goto out; 824bc05954SSeongJae Park } 834bc05954SSeongJae Park 844bc05954SSeongJae Park mutex_lock(&ctx->kdamond_lock); 854bc05954SSeongJae Park if (ctx->kdamond) { 864bc05954SSeongJae Park ret = -EBUSY; 874bc05954SSeongJae Park goto unlock_out; 884bc05954SSeongJae Park } 894bc05954SSeongJae Park 909210622aSRongwei Wang ret = damon_set_attrs(ctx, s, a, r, minr, maxr); 919210622aSRongwei Wang if (!ret) 929210622aSRongwei Wang ret = count; 934bc05954SSeongJae Park unlock_out: 944bc05954SSeongJae Park mutex_unlock(&ctx->kdamond_lock); 954bc05954SSeongJae Park out: 964bc05954SSeongJae Park kfree(kbuf); 974bc05954SSeongJae Park return ret; 984bc05954SSeongJae Park } 994bc05954SSeongJae Park 100af122dd8SSeongJae Park static ssize_t sprint_schemes(struct damon_ctx *c, char *buf, ssize_t len) 101af122dd8SSeongJae Park { 102af122dd8SSeongJae Park struct damos *s; 103af122dd8SSeongJae Park int written = 0; 104af122dd8SSeongJae Park int rc; 105af122dd8SSeongJae Park 106af122dd8SSeongJae Park damon_for_each_scheme(s, c) { 107af122dd8SSeongJae Park rc = scnprintf(&buf[written], len - written, 1083a619fdbSSeongJae Park "%lu %lu %u %u %u %u %d %lu %lu %lu %u %u %u %d %lu %lu %lu %lu %lu %lu %lu %lu %lu\n", 109af122dd8SSeongJae Park s->min_sz_region, s->max_sz_region, 110af122dd8SSeongJae Park s->min_nr_accesses, s->max_nr_accesses, 111af122dd8SSeongJae Park s->min_age_region, s->max_age_region, 112d7d0ec85SSeongJae Park s->action, 113d7d0ec85SSeongJae Park s->quota.ms, s->quota.sz, 114d7d0ec85SSeongJae Park s->quota.reset_interval, 115f4a68b4aSSeongJae Park s->quota.weight_sz, 116f4a68b4aSSeongJae Park s->quota.weight_nr_accesses, 117f4a68b4aSSeongJae Park s->quota.weight_age, 118ae666a6dSSeongJae Park s->wmarks.metric, s->wmarks.interval, 119ae666a6dSSeongJae Park s->wmarks.high, s->wmarks.mid, s->wmarks.low, 1203a619fdbSSeongJae Park s->stat.nr_tried, s->stat.sz_tried, 1213a619fdbSSeongJae Park s->stat.nr_applied, s->stat.sz_applied, 1223a619fdbSSeongJae Park s->stat.qt_exceeds); 123af122dd8SSeongJae Park if (!rc) 124af122dd8SSeongJae Park return -ENOMEM; 125af122dd8SSeongJae Park 126af122dd8SSeongJae Park written += rc; 127af122dd8SSeongJae Park } 128af122dd8SSeongJae Park return written; 129af122dd8SSeongJae Park } 130af122dd8SSeongJae Park 131af122dd8SSeongJae Park static ssize_t dbgfs_schemes_read(struct file *file, char __user *buf, 132af122dd8SSeongJae Park size_t count, loff_t *ppos) 133af122dd8SSeongJae Park { 134af122dd8SSeongJae Park struct damon_ctx *ctx = file->private_data; 135af122dd8SSeongJae Park char *kbuf; 136af122dd8SSeongJae Park ssize_t len; 137af122dd8SSeongJae Park 138db7a347bSSeongJae Park kbuf = kmalloc(count, GFP_KERNEL | __GFP_NOWARN); 139af122dd8SSeongJae Park if (!kbuf) 140af122dd8SSeongJae Park return -ENOMEM; 141af122dd8SSeongJae Park 142af122dd8SSeongJae Park mutex_lock(&ctx->kdamond_lock); 143af122dd8SSeongJae Park len = sprint_schemes(ctx, kbuf, count); 144af122dd8SSeongJae Park mutex_unlock(&ctx->kdamond_lock); 145af122dd8SSeongJae Park if (len < 0) 146af122dd8SSeongJae Park goto out; 147af122dd8SSeongJae Park len = simple_read_from_buffer(buf, count, ppos, kbuf, len); 148af122dd8SSeongJae Park 149af122dd8SSeongJae Park out: 150af122dd8SSeongJae Park kfree(kbuf); 151af122dd8SSeongJae Park return len; 152af122dd8SSeongJae Park } 153af122dd8SSeongJae Park 154af122dd8SSeongJae Park static void free_schemes_arr(struct damos **schemes, ssize_t nr_schemes) 155af122dd8SSeongJae Park { 156af122dd8SSeongJae Park ssize_t i; 157af122dd8SSeongJae Park 158af122dd8SSeongJae Park for (i = 0; i < nr_schemes; i++) 159af122dd8SSeongJae Park kfree(schemes[i]); 160af122dd8SSeongJae Park kfree(schemes); 161af122dd8SSeongJae Park } 162af122dd8SSeongJae Park 163af122dd8SSeongJae Park static bool damos_action_valid(int action) 164af122dd8SSeongJae Park { 165af122dd8SSeongJae Park switch (action) { 166af122dd8SSeongJae Park case DAMOS_WILLNEED: 167af122dd8SSeongJae Park case DAMOS_COLD: 168af122dd8SSeongJae Park case DAMOS_PAGEOUT: 169af122dd8SSeongJae Park case DAMOS_HUGEPAGE: 170af122dd8SSeongJae Park case DAMOS_NOHUGEPAGE: 1712f0b548cSSeongJae Park case DAMOS_STAT: 172af122dd8SSeongJae Park return true; 173af122dd8SSeongJae Park default: 174af122dd8SSeongJae Park return false; 175af122dd8SSeongJae Park } 176af122dd8SSeongJae Park } 177af122dd8SSeongJae Park 178af122dd8SSeongJae Park /* 179af122dd8SSeongJae Park * Converts a string into an array of struct damos pointers 180af122dd8SSeongJae Park * 181af122dd8SSeongJae Park * Returns an array of struct damos pointers that converted if the conversion 182af122dd8SSeongJae Park * success, or NULL otherwise. 183af122dd8SSeongJae Park */ 184af122dd8SSeongJae Park static struct damos **str_to_schemes(const char *str, ssize_t len, 185af122dd8SSeongJae Park ssize_t *nr_schemes) 186af122dd8SSeongJae Park { 187af122dd8SSeongJae Park struct damos *scheme, **schemes; 188af122dd8SSeongJae Park const int max_nr_schemes = 256; 189af122dd8SSeongJae Park int pos = 0, parsed, ret; 190af122dd8SSeongJae Park unsigned long min_sz, max_sz; 191af122dd8SSeongJae Park unsigned int min_nr_a, max_nr_a, min_age, max_age; 192af122dd8SSeongJae Park unsigned int action; 193af122dd8SSeongJae Park 194af122dd8SSeongJae Park schemes = kmalloc_array(max_nr_schemes, sizeof(scheme), 195af122dd8SSeongJae Park GFP_KERNEL); 196af122dd8SSeongJae Park if (!schemes) 197af122dd8SSeongJae Park return NULL; 198af122dd8SSeongJae Park 199af122dd8SSeongJae Park *nr_schemes = 0; 200af122dd8SSeongJae Park while (pos < len && *nr_schemes < max_nr_schemes) { 2012b8a248dSSeongJae Park struct damos_quota quota = {}; 202ae666a6dSSeongJae Park struct damos_watermarks wmarks; 2032b8a248dSSeongJae Park 204f4a68b4aSSeongJae Park ret = sscanf(&str[pos], 205ae666a6dSSeongJae Park "%lu %lu %u %u %u %u %u %lu %lu %lu %u %u %u %u %lu %lu %lu %lu%n", 206af122dd8SSeongJae Park &min_sz, &max_sz, &min_nr_a, &max_nr_a, 207d7d0ec85SSeongJae Park &min_age, &max_age, &action, "a.ms, 208f4a68b4aSSeongJae Park "a.sz, "a.reset_interval, 209f4a68b4aSSeongJae Park "a.weight_sz, "a.weight_nr_accesses, 210ae666a6dSSeongJae Park "a.weight_age, &wmarks.metric, 211ae666a6dSSeongJae Park &wmarks.interval, &wmarks.high, &wmarks.mid, 212ae666a6dSSeongJae Park &wmarks.low, &parsed); 213ae666a6dSSeongJae Park if (ret != 18) 214af122dd8SSeongJae Park break; 2150bceffa2SSeongJae Park if (!damos_action_valid(action)) 216af122dd8SSeongJae Park goto fail; 217af122dd8SSeongJae Park 218c89ae63eSXin Hao if (min_sz > max_sz || min_nr_a > max_nr_a || min_age > max_age) 219c89ae63eSXin Hao goto fail; 220c89ae63eSXin Hao 221c89ae63eSXin Hao if (wmarks.high < wmarks.mid || wmarks.high < wmarks.low || 222c89ae63eSXin Hao wmarks.mid < wmarks.low) 223c89ae63eSXin Hao goto fail; 224c89ae63eSXin Hao 225af122dd8SSeongJae Park pos += parsed; 226af122dd8SSeongJae Park scheme = damon_new_scheme(min_sz, max_sz, min_nr_a, max_nr_a, 227ee801b7dSSeongJae Park min_age, max_age, action, "a, &wmarks); 228af122dd8SSeongJae Park if (!scheme) 229af122dd8SSeongJae Park goto fail; 230af122dd8SSeongJae Park 231af122dd8SSeongJae Park schemes[*nr_schemes] = scheme; 232af122dd8SSeongJae Park *nr_schemes += 1; 233af122dd8SSeongJae Park } 234af122dd8SSeongJae Park return schemes; 235af122dd8SSeongJae Park fail: 236af122dd8SSeongJae Park free_schemes_arr(schemes, *nr_schemes); 237af122dd8SSeongJae Park return NULL; 238af122dd8SSeongJae Park } 239af122dd8SSeongJae Park 240af122dd8SSeongJae Park static ssize_t dbgfs_schemes_write(struct file *file, const char __user *buf, 241af122dd8SSeongJae Park size_t count, loff_t *ppos) 242af122dd8SSeongJae Park { 243af122dd8SSeongJae Park struct damon_ctx *ctx = file->private_data; 244af122dd8SSeongJae Park char *kbuf; 245af122dd8SSeongJae Park struct damos **schemes; 2469210622aSRongwei Wang ssize_t nr_schemes = 0, ret; 247af122dd8SSeongJae Park 248af122dd8SSeongJae Park kbuf = user_input_str(buf, count, ppos); 249af122dd8SSeongJae Park if (IS_ERR(kbuf)) 250af122dd8SSeongJae Park return PTR_ERR(kbuf); 251af122dd8SSeongJae Park 2529210622aSRongwei Wang schemes = str_to_schemes(kbuf, count, &nr_schemes); 253af122dd8SSeongJae Park if (!schemes) { 254af122dd8SSeongJae Park ret = -EINVAL; 255af122dd8SSeongJae Park goto out; 256af122dd8SSeongJae Park } 257af122dd8SSeongJae Park 258af122dd8SSeongJae Park mutex_lock(&ctx->kdamond_lock); 259af122dd8SSeongJae Park if (ctx->kdamond) { 260af122dd8SSeongJae Park ret = -EBUSY; 261af122dd8SSeongJae Park goto unlock_out; 262af122dd8SSeongJae Park } 263af122dd8SSeongJae Park 2649210622aSRongwei Wang ret = damon_set_schemes(ctx, schemes, nr_schemes); 2659210622aSRongwei Wang if (!ret) { 2669210622aSRongwei Wang ret = count; 267af122dd8SSeongJae Park nr_schemes = 0; 2689210622aSRongwei Wang } 2699210622aSRongwei Wang 270af122dd8SSeongJae Park unlock_out: 271af122dd8SSeongJae Park mutex_unlock(&ctx->kdamond_lock); 272af122dd8SSeongJae Park free_schemes_arr(schemes, nr_schemes); 273af122dd8SSeongJae Park out: 274af122dd8SSeongJae Park kfree(kbuf); 275af122dd8SSeongJae Park return ret; 276af122dd8SSeongJae Park } 277af122dd8SSeongJae Park 278*1971bd63SSeongJae Park static inline bool target_has_pid(const struct damon_ctx *ctx) 2794bc05954SSeongJae Park { 2804bc05954SSeongJae Park return ctx->primitive.target_valid == damon_va_target_valid; 2814bc05954SSeongJae Park } 2824bc05954SSeongJae Park 2834bc05954SSeongJae Park static ssize_t sprint_target_ids(struct damon_ctx *ctx, char *buf, ssize_t len) 2844bc05954SSeongJae Park { 2854bc05954SSeongJae Park struct damon_target *t; 286*1971bd63SSeongJae Park int id; 2874bc05954SSeongJae Park int written = 0; 2884bc05954SSeongJae Park int rc; 2894bc05954SSeongJae Park 2904bc05954SSeongJae Park damon_for_each_target(t, ctx) { 291*1971bd63SSeongJae Park if (target_has_pid(ctx)) 2924bc05954SSeongJae Park /* Show pid numbers to debugfs users */ 293*1971bd63SSeongJae Park id = pid_vnr(t->pid); 294*1971bd63SSeongJae Park else 295*1971bd63SSeongJae Park /* Show 42 for physical address space, just for fun */ 296*1971bd63SSeongJae Park id = 42; 2974bc05954SSeongJae Park 298*1971bd63SSeongJae Park rc = scnprintf(&buf[written], len - written, "%d ", id); 2994bc05954SSeongJae Park if (!rc) 3004bc05954SSeongJae Park return -ENOMEM; 3014bc05954SSeongJae Park written += rc; 3024bc05954SSeongJae Park } 3034bc05954SSeongJae Park if (written) 3044bc05954SSeongJae Park written -= 1; 3054bc05954SSeongJae Park written += scnprintf(&buf[written], len - written, "\n"); 3064bc05954SSeongJae Park return written; 3074bc05954SSeongJae Park } 3084bc05954SSeongJae Park 3094bc05954SSeongJae Park static ssize_t dbgfs_target_ids_read(struct file *file, 3104bc05954SSeongJae Park char __user *buf, size_t count, loff_t *ppos) 3114bc05954SSeongJae Park { 3124bc05954SSeongJae Park struct damon_ctx *ctx = file->private_data; 3134bc05954SSeongJae Park ssize_t len; 3144bc05954SSeongJae Park char ids_buf[320]; 3154bc05954SSeongJae Park 3164bc05954SSeongJae Park mutex_lock(&ctx->kdamond_lock); 3174bc05954SSeongJae Park len = sprint_target_ids(ctx, ids_buf, 320); 3184bc05954SSeongJae Park mutex_unlock(&ctx->kdamond_lock); 3194bc05954SSeongJae Park if (len < 0) 3204bc05954SSeongJae Park return len; 3214bc05954SSeongJae Park 3224bc05954SSeongJae Park return simple_read_from_buffer(buf, count, ppos, ids_buf, len); 3234bc05954SSeongJae Park } 3244bc05954SSeongJae Park 3254bc05954SSeongJae Park /* 326*1971bd63SSeongJae Park * Converts a string into an integers array 3274bc05954SSeongJae Park * 328*1971bd63SSeongJae Park * Returns an array of integers array if the conversion success, or NULL 329*1971bd63SSeongJae Park * otherwise. 3304bc05954SSeongJae Park */ 331*1971bd63SSeongJae Park static int *str_to_ints(const char *str, ssize_t len, ssize_t *nr_ints) 3324bc05954SSeongJae Park { 333*1971bd63SSeongJae Park int *array; 334*1971bd63SSeongJae Park const int max_nr_ints = 32; 335*1971bd63SSeongJae Park int nr; 3364bc05954SSeongJae Park int pos = 0, parsed, ret; 3374bc05954SSeongJae Park 338*1971bd63SSeongJae Park *nr_ints = 0; 339*1971bd63SSeongJae Park array = kmalloc_array(max_nr_ints, sizeof(*array), GFP_KERNEL); 340*1971bd63SSeongJae Park if (!array) 3414bc05954SSeongJae Park return NULL; 342*1971bd63SSeongJae Park while (*nr_ints < max_nr_ints && pos < len) { 343*1971bd63SSeongJae Park ret = sscanf(&str[pos], "%d%n", &nr, &parsed); 3444bc05954SSeongJae Park pos += parsed; 3454bc05954SSeongJae Park if (ret != 1) 3464bc05954SSeongJae Park break; 347*1971bd63SSeongJae Park array[*nr_ints] = nr; 348*1971bd63SSeongJae Park *nr_ints += 1; 3494bc05954SSeongJae Park } 3504bc05954SSeongJae Park 351*1971bd63SSeongJae Park return array; 3524bc05954SSeongJae Park } 3534bc05954SSeongJae Park 354*1971bd63SSeongJae Park static void dbgfs_put_pids(struct pid **pids, int nr_pids) 3554bc05954SSeongJae Park { 3564bc05954SSeongJae Park int i; 3574bc05954SSeongJae Park 358*1971bd63SSeongJae Park for (i = 0; i < nr_pids; i++) 359*1971bd63SSeongJae Park put_pid(pids[i]); 360*1971bd63SSeongJae Park } 361*1971bd63SSeongJae Park 362*1971bd63SSeongJae Park /* 363*1971bd63SSeongJae Park * Converts a string into an struct pid pointers array 364*1971bd63SSeongJae Park * 365*1971bd63SSeongJae Park * Returns an array of struct pid pointers if the conversion success, or NULL 366*1971bd63SSeongJae Park * otherwise. 367*1971bd63SSeongJae Park */ 368*1971bd63SSeongJae Park static struct pid **str_to_pids(const char *str, ssize_t len, ssize_t *nr_pids) 369*1971bd63SSeongJae Park { 370*1971bd63SSeongJae Park int *ints; 371*1971bd63SSeongJae Park ssize_t nr_ints; 372*1971bd63SSeongJae Park struct pid **pids; 373*1971bd63SSeongJae Park 374*1971bd63SSeongJae Park *nr_pids = 0; 375*1971bd63SSeongJae Park 376*1971bd63SSeongJae Park ints = str_to_ints(str, len, &nr_ints); 377*1971bd63SSeongJae Park if (!ints) 378*1971bd63SSeongJae Park return NULL; 379*1971bd63SSeongJae Park 380*1971bd63SSeongJae Park pids = kmalloc_array(nr_ints, sizeof(*pids), GFP_KERNEL); 381*1971bd63SSeongJae Park if (!pids) 382*1971bd63SSeongJae Park goto out; 383*1971bd63SSeongJae Park 384*1971bd63SSeongJae Park for (; *nr_pids < nr_ints; (*nr_pids)++) { 385*1971bd63SSeongJae Park pids[*nr_pids] = find_get_pid(ints[*nr_pids]); 386*1971bd63SSeongJae Park if (!pids[*nr_pids]) { 387*1971bd63SSeongJae Park dbgfs_put_pids(pids, *nr_pids); 388*1971bd63SSeongJae Park kfree(ints); 389*1971bd63SSeongJae Park kfree(pids); 390*1971bd63SSeongJae Park return NULL; 391*1971bd63SSeongJae Park } 392*1971bd63SSeongJae Park } 393*1971bd63SSeongJae Park 394*1971bd63SSeongJae Park out: 395*1971bd63SSeongJae Park kfree(ints); 396*1971bd63SSeongJae Park return pids; 3974bc05954SSeongJae Park } 3984bc05954SSeongJae Park 39943642825SSeongJae Park /* 40043642825SSeongJae Park * dbgfs_set_targets() - Set monitoring targets. 40143642825SSeongJae Park * @ctx: monitoring context 402*1971bd63SSeongJae Park * @nr_targets: number of targets 403*1971bd63SSeongJae Park * @pids: array of target pids (size is same to @nr_targets) 40443642825SSeongJae Park * 405*1971bd63SSeongJae Park * This function should not be called while the kdamond is running. @pids is 406*1971bd63SSeongJae Park * ignored if the context is not configured to have pid in each target. On 407*1971bd63SSeongJae Park * failure, reference counts of all pids in @pids are decremented. 40843642825SSeongJae Park * 40943642825SSeongJae Park * Return: 0 on success, negative error code otherwise. 41043642825SSeongJae Park */ 411*1971bd63SSeongJae Park static int dbgfs_set_targets(struct damon_ctx *ctx, ssize_t nr_targets, 412*1971bd63SSeongJae Park struct pid **pids) 41343642825SSeongJae Park { 41443642825SSeongJae Park ssize_t i; 41543642825SSeongJae Park struct damon_target *t, *next; 41643642825SSeongJae Park 41743642825SSeongJae Park damon_for_each_target_safe(t, next, ctx) { 418*1971bd63SSeongJae Park if (target_has_pid(ctx)) 419*1971bd63SSeongJae Park put_pid(t->pid); 42043642825SSeongJae Park damon_destroy_target(t); 42143642825SSeongJae Park } 42243642825SSeongJae Park 423*1971bd63SSeongJae Park for (i = 0; i < nr_targets; i++) { 424*1971bd63SSeongJae Park t = damon_new_target(); 42543642825SSeongJae Park if (!t) { 42643642825SSeongJae Park damon_for_each_target_safe(t, next, ctx) 42743642825SSeongJae Park damon_destroy_target(t); 428*1971bd63SSeongJae Park if (target_has_pid(ctx)) 429*1971bd63SSeongJae Park dbgfs_put_pids(pids, nr_targets); 43043642825SSeongJae Park return -ENOMEM; 43143642825SSeongJae Park } 432*1971bd63SSeongJae Park if (target_has_pid(ctx)) 433*1971bd63SSeongJae Park t->pid = pids[i]; 43443642825SSeongJae Park damon_add_target(ctx, t); 43543642825SSeongJae Park } 43643642825SSeongJae Park 43743642825SSeongJae Park return 0; 43843642825SSeongJae Park } 43943642825SSeongJae Park 4404bc05954SSeongJae Park static ssize_t dbgfs_target_ids_write(struct file *file, 4414bc05954SSeongJae Park const char __user *buf, size_t count, loff_t *ppos) 4424bc05954SSeongJae Park { 4434bc05954SSeongJae Park struct damon_ctx *ctx = file->private_data; 444c026291aSSeongJae Park bool id_is_pid = true; 44570b84808SSeongJae Park char *kbuf; 446*1971bd63SSeongJae Park struct pid **target_pids = NULL; 4474bc05954SSeongJae Park ssize_t nr_targets; 4489210622aSRongwei Wang ssize_t ret; 4494bc05954SSeongJae Park 4504bc05954SSeongJae Park kbuf = user_input_str(buf, count, ppos); 4514bc05954SSeongJae Park if (IS_ERR(kbuf)) 4524bc05954SSeongJae Park return PTR_ERR(kbuf); 4534bc05954SSeongJae Park 454c026291aSSeongJae Park if (!strncmp(kbuf, "paddr\n", count)) { 455c026291aSSeongJae Park id_is_pid = false; 456*1971bd63SSeongJae Park nr_targets = 1; 4574bc05954SSeongJae Park } 4584bc05954SSeongJae Park 459c026291aSSeongJae Park if (id_is_pid) { 460*1971bd63SSeongJae Park target_pids = str_to_pids(kbuf, count, &nr_targets); 461*1971bd63SSeongJae Park if (!target_pids) { 462*1971bd63SSeongJae Park ret = -ENOMEM; 463*1971bd63SSeongJae Park goto out; 4644bc05954SSeongJae Park } 4654bc05954SSeongJae Park } 4664bc05954SSeongJae Park 4674bc05954SSeongJae Park mutex_lock(&ctx->kdamond_lock); 4684bc05954SSeongJae Park if (ctx->kdamond) { 469c026291aSSeongJae Park if (id_is_pid) 470*1971bd63SSeongJae Park dbgfs_put_pids(target_pids, nr_targets); 4714bc05954SSeongJae Park ret = -EBUSY; 4724bc05954SSeongJae Park goto unlock_out; 4734bc05954SSeongJae Park } 4744bc05954SSeongJae Park 475ebb3f994SSeongJae Park /* remove previously set targets */ 476*1971bd63SSeongJae Park dbgfs_set_targets(ctx, 0, NULL); 477c026291aSSeongJae Park 478c026291aSSeongJae Park /* Configure the context for the address space type */ 479c026291aSSeongJae Park if (id_is_pid) 480c026291aSSeongJae Park damon_va_set_primitives(ctx); 481c026291aSSeongJae Park else 482c026291aSSeongJae Park damon_pa_set_primitives(ctx); 483c026291aSSeongJae Park 484*1971bd63SSeongJae Park ret = dbgfs_set_targets(ctx, nr_targets, target_pids); 48543642825SSeongJae Park if (!ret) 4869210622aSRongwei Wang ret = count; 4874bc05954SSeongJae Park 4884bc05954SSeongJae Park unlock_out: 4894bc05954SSeongJae Park mutex_unlock(&ctx->kdamond_lock); 490*1971bd63SSeongJae Park kfree(target_pids); 4914bc05954SSeongJae Park out: 4924bc05954SSeongJae Park kfree(kbuf); 4934bc05954SSeongJae Park return ret; 4944bc05954SSeongJae Park } 4954bc05954SSeongJae Park 49690bebce9SSeongJae Park static ssize_t sprint_init_regions(struct damon_ctx *c, char *buf, ssize_t len) 49790bebce9SSeongJae Park { 49890bebce9SSeongJae Park struct damon_target *t; 49990bebce9SSeongJae Park struct damon_region *r; 500144760f8SSeongJae Park int target_idx = 0; 50190bebce9SSeongJae Park int written = 0; 50290bebce9SSeongJae Park int rc; 50390bebce9SSeongJae Park 50490bebce9SSeongJae Park damon_for_each_target(t, c) { 50590bebce9SSeongJae Park damon_for_each_region(r, t) { 50690bebce9SSeongJae Park rc = scnprintf(&buf[written], len - written, 507144760f8SSeongJae Park "%d %lu %lu\n", 508144760f8SSeongJae Park target_idx, r->ar.start, r->ar.end); 50990bebce9SSeongJae Park if (!rc) 51090bebce9SSeongJae Park return -ENOMEM; 51190bebce9SSeongJae Park written += rc; 51290bebce9SSeongJae Park } 513144760f8SSeongJae Park target_idx++; 51490bebce9SSeongJae Park } 51590bebce9SSeongJae Park return written; 51690bebce9SSeongJae Park } 51790bebce9SSeongJae Park 51890bebce9SSeongJae Park static ssize_t dbgfs_init_regions_read(struct file *file, char __user *buf, 51990bebce9SSeongJae Park size_t count, loff_t *ppos) 52090bebce9SSeongJae Park { 52190bebce9SSeongJae Park struct damon_ctx *ctx = file->private_data; 52290bebce9SSeongJae Park char *kbuf; 52390bebce9SSeongJae Park ssize_t len; 52490bebce9SSeongJae Park 525db7a347bSSeongJae Park kbuf = kmalloc(count, GFP_KERNEL | __GFP_NOWARN); 52690bebce9SSeongJae Park if (!kbuf) 52790bebce9SSeongJae Park return -ENOMEM; 52890bebce9SSeongJae Park 52990bebce9SSeongJae Park mutex_lock(&ctx->kdamond_lock); 53090bebce9SSeongJae Park if (ctx->kdamond) { 53190bebce9SSeongJae Park mutex_unlock(&ctx->kdamond_lock); 53290bebce9SSeongJae Park len = -EBUSY; 53390bebce9SSeongJae Park goto out; 53490bebce9SSeongJae Park } 53590bebce9SSeongJae Park 53690bebce9SSeongJae Park len = sprint_init_regions(ctx, kbuf, count); 53790bebce9SSeongJae Park mutex_unlock(&ctx->kdamond_lock); 53890bebce9SSeongJae Park if (len < 0) 53990bebce9SSeongJae Park goto out; 54090bebce9SSeongJae Park len = simple_read_from_buffer(buf, count, ppos, kbuf, len); 54190bebce9SSeongJae Park 54290bebce9SSeongJae Park out: 54390bebce9SSeongJae Park kfree(kbuf); 54490bebce9SSeongJae Park return len; 54590bebce9SSeongJae Park } 54690bebce9SSeongJae Park 547144760f8SSeongJae Park static int add_init_region(struct damon_ctx *c, int target_idx, 548144760f8SSeongJae Park struct damon_addr_range *ar) 54990bebce9SSeongJae Park { 55090bebce9SSeongJae Park struct damon_target *t; 55190bebce9SSeongJae Park struct damon_region *r, *prev; 552144760f8SSeongJae Park unsigned long idx = 0; 55390bebce9SSeongJae Park int rc = -EINVAL; 55490bebce9SSeongJae Park 55590bebce9SSeongJae Park if (ar->start >= ar->end) 55690bebce9SSeongJae Park return -EINVAL; 55790bebce9SSeongJae Park 55890bebce9SSeongJae Park damon_for_each_target(t, c) { 559144760f8SSeongJae Park if (idx++ == target_idx) { 56090bebce9SSeongJae Park r = damon_new_region(ar->start, ar->end); 56190bebce9SSeongJae Park if (!r) 56290bebce9SSeongJae Park return -ENOMEM; 56390bebce9SSeongJae Park damon_add_region(r, t); 56490bebce9SSeongJae Park if (damon_nr_regions(t) > 1) { 56590bebce9SSeongJae Park prev = damon_prev_region(r); 56690bebce9SSeongJae Park if (prev->ar.end > r->ar.start) { 56790bebce9SSeongJae Park damon_destroy_region(r, t); 56890bebce9SSeongJae Park return -EINVAL; 56990bebce9SSeongJae Park } 57090bebce9SSeongJae Park } 57190bebce9SSeongJae Park rc = 0; 57290bebce9SSeongJae Park } 57390bebce9SSeongJae Park } 57490bebce9SSeongJae Park return rc; 57590bebce9SSeongJae Park } 57690bebce9SSeongJae Park 57790bebce9SSeongJae Park static int set_init_regions(struct damon_ctx *c, const char *str, ssize_t len) 57890bebce9SSeongJae Park { 57990bebce9SSeongJae Park struct damon_target *t; 58090bebce9SSeongJae Park struct damon_region *r, *next; 58190bebce9SSeongJae Park int pos = 0, parsed, ret; 582144760f8SSeongJae Park int target_idx; 58390bebce9SSeongJae Park struct damon_addr_range ar; 58490bebce9SSeongJae Park int err; 58590bebce9SSeongJae Park 58690bebce9SSeongJae Park damon_for_each_target(t, c) { 58790bebce9SSeongJae Park damon_for_each_region_safe(r, next, t) 58890bebce9SSeongJae Park damon_destroy_region(r, t); 58990bebce9SSeongJae Park } 59090bebce9SSeongJae Park 59190bebce9SSeongJae Park while (pos < len) { 592144760f8SSeongJae Park ret = sscanf(&str[pos], "%d %lu %lu%n", 593144760f8SSeongJae Park &target_idx, &ar.start, &ar.end, &parsed); 59490bebce9SSeongJae Park if (ret != 3) 59590bebce9SSeongJae Park break; 596144760f8SSeongJae Park err = add_init_region(c, target_idx, &ar); 59790bebce9SSeongJae Park if (err) 59890bebce9SSeongJae Park goto fail; 59990bebce9SSeongJae Park pos += parsed; 60090bebce9SSeongJae Park } 60190bebce9SSeongJae Park 60290bebce9SSeongJae Park return 0; 60390bebce9SSeongJae Park 60490bebce9SSeongJae Park fail: 60590bebce9SSeongJae Park damon_for_each_target(t, c) { 60690bebce9SSeongJae Park damon_for_each_region_safe(r, next, t) 60790bebce9SSeongJae Park damon_destroy_region(r, t); 60890bebce9SSeongJae Park } 60990bebce9SSeongJae Park return err; 61090bebce9SSeongJae Park } 61190bebce9SSeongJae Park 61290bebce9SSeongJae Park static ssize_t dbgfs_init_regions_write(struct file *file, 61390bebce9SSeongJae Park const char __user *buf, size_t count, 61490bebce9SSeongJae Park loff_t *ppos) 61590bebce9SSeongJae Park { 61690bebce9SSeongJae Park struct damon_ctx *ctx = file->private_data; 61790bebce9SSeongJae Park char *kbuf; 61890bebce9SSeongJae Park ssize_t ret = count; 61990bebce9SSeongJae Park int err; 62090bebce9SSeongJae Park 62190bebce9SSeongJae Park kbuf = user_input_str(buf, count, ppos); 62290bebce9SSeongJae Park if (IS_ERR(kbuf)) 62390bebce9SSeongJae Park return PTR_ERR(kbuf); 62490bebce9SSeongJae Park 62590bebce9SSeongJae Park mutex_lock(&ctx->kdamond_lock); 62690bebce9SSeongJae Park if (ctx->kdamond) { 62790bebce9SSeongJae Park ret = -EBUSY; 62890bebce9SSeongJae Park goto unlock_out; 62990bebce9SSeongJae Park } 63090bebce9SSeongJae Park 63190bebce9SSeongJae Park err = set_init_regions(ctx, kbuf, ret); 63290bebce9SSeongJae Park if (err) 63390bebce9SSeongJae Park ret = err; 63490bebce9SSeongJae Park 63590bebce9SSeongJae Park unlock_out: 63690bebce9SSeongJae Park mutex_unlock(&ctx->kdamond_lock); 63790bebce9SSeongJae Park kfree(kbuf); 63890bebce9SSeongJae Park return ret; 63990bebce9SSeongJae Park } 64090bebce9SSeongJae Park 641429538e8SSeongJae Park static ssize_t dbgfs_kdamond_pid_read(struct file *file, 642429538e8SSeongJae Park char __user *buf, size_t count, loff_t *ppos) 643429538e8SSeongJae Park { 644429538e8SSeongJae Park struct damon_ctx *ctx = file->private_data; 645429538e8SSeongJae Park char *kbuf; 646429538e8SSeongJae Park ssize_t len; 647429538e8SSeongJae Park 648db7a347bSSeongJae Park kbuf = kmalloc(count, GFP_KERNEL | __GFP_NOWARN); 649429538e8SSeongJae Park if (!kbuf) 650429538e8SSeongJae Park return -ENOMEM; 651429538e8SSeongJae Park 652429538e8SSeongJae Park mutex_lock(&ctx->kdamond_lock); 653429538e8SSeongJae Park if (ctx->kdamond) 654429538e8SSeongJae Park len = scnprintf(kbuf, count, "%d\n", ctx->kdamond->pid); 655429538e8SSeongJae Park else 656429538e8SSeongJae Park len = scnprintf(kbuf, count, "none\n"); 657429538e8SSeongJae Park mutex_unlock(&ctx->kdamond_lock); 658429538e8SSeongJae Park if (!len) 659429538e8SSeongJae Park goto out; 660429538e8SSeongJae Park len = simple_read_from_buffer(buf, count, ppos, kbuf, len); 661429538e8SSeongJae Park 662429538e8SSeongJae Park out: 663429538e8SSeongJae Park kfree(kbuf); 664429538e8SSeongJae Park return len; 665429538e8SSeongJae Park } 666429538e8SSeongJae Park 6674bc05954SSeongJae Park static int damon_dbgfs_open(struct inode *inode, struct file *file) 6684bc05954SSeongJae Park { 6694bc05954SSeongJae Park file->private_data = inode->i_private; 6704bc05954SSeongJae Park 6714bc05954SSeongJae Park return nonseekable_open(inode, file); 6724bc05954SSeongJae Park } 6734bc05954SSeongJae Park 6744bc05954SSeongJae Park static const struct file_operations attrs_fops = { 6754bc05954SSeongJae Park .open = damon_dbgfs_open, 6764bc05954SSeongJae Park .read = dbgfs_attrs_read, 6774bc05954SSeongJae Park .write = dbgfs_attrs_write, 6784bc05954SSeongJae Park }; 6794bc05954SSeongJae Park 680af122dd8SSeongJae Park static const struct file_operations schemes_fops = { 681af122dd8SSeongJae Park .open = damon_dbgfs_open, 682af122dd8SSeongJae Park .read = dbgfs_schemes_read, 683af122dd8SSeongJae Park .write = dbgfs_schemes_write, 684af122dd8SSeongJae Park }; 685af122dd8SSeongJae Park 6864bc05954SSeongJae Park static const struct file_operations target_ids_fops = { 6874bc05954SSeongJae Park .open = damon_dbgfs_open, 6884bc05954SSeongJae Park .read = dbgfs_target_ids_read, 6894bc05954SSeongJae Park .write = dbgfs_target_ids_write, 6904bc05954SSeongJae Park }; 6914bc05954SSeongJae Park 69290bebce9SSeongJae Park static const struct file_operations init_regions_fops = { 69390bebce9SSeongJae Park .open = damon_dbgfs_open, 69490bebce9SSeongJae Park .read = dbgfs_init_regions_read, 69590bebce9SSeongJae Park .write = dbgfs_init_regions_write, 69690bebce9SSeongJae Park }; 69790bebce9SSeongJae Park 698429538e8SSeongJae Park static const struct file_operations kdamond_pid_fops = { 699429538e8SSeongJae Park .open = damon_dbgfs_open, 700429538e8SSeongJae Park .read = dbgfs_kdamond_pid_read, 701429538e8SSeongJae Park }; 702429538e8SSeongJae Park 7034bc05954SSeongJae Park static void dbgfs_fill_ctx_dir(struct dentry *dir, struct damon_ctx *ctx) 7044bc05954SSeongJae Park { 705af122dd8SSeongJae Park const char * const file_names[] = {"attrs", "schemes", "target_ids", 70690bebce9SSeongJae Park "init_regions", "kdamond_pid"}; 707af122dd8SSeongJae Park const struct file_operations *fops[] = {&attrs_fops, &schemes_fops, 70890bebce9SSeongJae Park &target_ids_fops, &init_regions_fops, &kdamond_pid_fops}; 7094bc05954SSeongJae Park int i; 7104bc05954SSeongJae Park 7114bc05954SSeongJae Park for (i = 0; i < ARRAY_SIZE(file_names); i++) 7124bc05954SSeongJae Park debugfs_create_file(file_names[i], 0600, dir, ctx, fops[i]); 7134bc05954SSeongJae Park } 7144bc05954SSeongJae Park 715658f9ae7SChangbin Du static void dbgfs_before_terminate(struct damon_ctx *ctx) 7164bc05954SSeongJae Park { 7174bc05954SSeongJae Park struct damon_target *t, *next; 7184bc05954SSeongJae Park 719*1971bd63SSeongJae Park if (!target_has_pid(ctx)) 720658f9ae7SChangbin Du return; 7214bc05954SSeongJae Park 72234796417SSeongJae Park mutex_lock(&ctx->kdamond_lock); 7234bc05954SSeongJae Park damon_for_each_target_safe(t, next, ctx) { 724*1971bd63SSeongJae Park put_pid(t->pid); 7254bc05954SSeongJae Park damon_destroy_target(t); 7264bc05954SSeongJae Park } 72734796417SSeongJae Park mutex_unlock(&ctx->kdamond_lock); 7284bc05954SSeongJae Park } 7294bc05954SSeongJae Park 7304bc05954SSeongJae Park static struct damon_ctx *dbgfs_new_ctx(void) 7314bc05954SSeongJae Park { 7324bc05954SSeongJae Park struct damon_ctx *ctx; 7334bc05954SSeongJae Park 7344bc05954SSeongJae Park ctx = damon_new_ctx(); 7354bc05954SSeongJae Park if (!ctx) 7364bc05954SSeongJae Park return NULL; 7374bc05954SSeongJae Park 7384bc05954SSeongJae Park damon_va_set_primitives(ctx); 7394bc05954SSeongJae Park ctx->callback.before_terminate = dbgfs_before_terminate; 7404bc05954SSeongJae Park return ctx; 7414bc05954SSeongJae Park } 7424bc05954SSeongJae Park 74375c1c2b5SSeongJae Park static void dbgfs_destroy_ctx(struct damon_ctx *ctx) 74475c1c2b5SSeongJae Park { 74575c1c2b5SSeongJae Park damon_destroy_ctx(ctx); 74675c1c2b5SSeongJae Park } 74775c1c2b5SSeongJae Park 74875c1c2b5SSeongJae Park /* 74975c1c2b5SSeongJae Park * Make a context of @name and create a debugfs directory for it. 75075c1c2b5SSeongJae Park * 75175c1c2b5SSeongJae Park * This function should be called while holding damon_dbgfs_lock. 75275c1c2b5SSeongJae Park * 75375c1c2b5SSeongJae Park * Returns 0 on success, negative error code otherwise. 75475c1c2b5SSeongJae Park */ 75575c1c2b5SSeongJae Park static int dbgfs_mk_context(char *name) 75675c1c2b5SSeongJae Park { 75775c1c2b5SSeongJae Park struct dentry *root, **new_dirs, *new_dir; 75875c1c2b5SSeongJae Park struct damon_ctx **new_ctxs, *new_ctx; 75975c1c2b5SSeongJae Park 76075c1c2b5SSeongJae Park if (damon_nr_running_ctxs()) 76175c1c2b5SSeongJae Park return -EBUSY; 76275c1c2b5SSeongJae Park 76375c1c2b5SSeongJae Park new_ctxs = krealloc(dbgfs_ctxs, sizeof(*dbgfs_ctxs) * 76475c1c2b5SSeongJae Park (dbgfs_nr_ctxs + 1), GFP_KERNEL); 76575c1c2b5SSeongJae Park if (!new_ctxs) 76675c1c2b5SSeongJae Park return -ENOMEM; 76775c1c2b5SSeongJae Park dbgfs_ctxs = new_ctxs; 76875c1c2b5SSeongJae Park 76975c1c2b5SSeongJae Park new_dirs = krealloc(dbgfs_dirs, sizeof(*dbgfs_dirs) * 77075c1c2b5SSeongJae Park (dbgfs_nr_ctxs + 1), GFP_KERNEL); 77175c1c2b5SSeongJae Park if (!new_dirs) 77275c1c2b5SSeongJae Park return -ENOMEM; 77375c1c2b5SSeongJae Park dbgfs_dirs = new_dirs; 77475c1c2b5SSeongJae Park 77575c1c2b5SSeongJae Park root = dbgfs_dirs[0]; 77675c1c2b5SSeongJae Park if (!root) 77775c1c2b5SSeongJae Park return -ENOENT; 77875c1c2b5SSeongJae Park 77975c1c2b5SSeongJae Park new_dir = debugfs_create_dir(name, root); 78075c1c2b5SSeongJae Park dbgfs_dirs[dbgfs_nr_ctxs] = new_dir; 78175c1c2b5SSeongJae Park 78275c1c2b5SSeongJae Park new_ctx = dbgfs_new_ctx(); 78375c1c2b5SSeongJae Park if (!new_ctx) { 78475c1c2b5SSeongJae Park debugfs_remove(new_dir); 78575c1c2b5SSeongJae Park dbgfs_dirs[dbgfs_nr_ctxs] = NULL; 78675c1c2b5SSeongJae Park return -ENOMEM; 78775c1c2b5SSeongJae Park } 78875c1c2b5SSeongJae Park 78975c1c2b5SSeongJae Park dbgfs_ctxs[dbgfs_nr_ctxs] = new_ctx; 79075c1c2b5SSeongJae Park dbgfs_fill_ctx_dir(dbgfs_dirs[dbgfs_nr_ctxs], 79175c1c2b5SSeongJae Park dbgfs_ctxs[dbgfs_nr_ctxs]); 79275c1c2b5SSeongJae Park dbgfs_nr_ctxs++; 79375c1c2b5SSeongJae Park 79475c1c2b5SSeongJae Park return 0; 79575c1c2b5SSeongJae Park } 79675c1c2b5SSeongJae Park 79775c1c2b5SSeongJae Park static ssize_t dbgfs_mk_context_write(struct file *file, 79875c1c2b5SSeongJae Park const char __user *buf, size_t count, loff_t *ppos) 79975c1c2b5SSeongJae Park { 80075c1c2b5SSeongJae Park char *kbuf; 80175c1c2b5SSeongJae Park char *ctx_name; 8029210622aSRongwei Wang ssize_t ret; 80375c1c2b5SSeongJae Park 80475c1c2b5SSeongJae Park kbuf = user_input_str(buf, count, ppos); 80575c1c2b5SSeongJae Park if (IS_ERR(kbuf)) 80675c1c2b5SSeongJae Park return PTR_ERR(kbuf); 80775c1c2b5SSeongJae Park ctx_name = kmalloc(count + 1, GFP_KERNEL); 80875c1c2b5SSeongJae Park if (!ctx_name) { 80975c1c2b5SSeongJae Park kfree(kbuf); 81075c1c2b5SSeongJae Park return -ENOMEM; 81175c1c2b5SSeongJae Park } 81275c1c2b5SSeongJae Park 81375c1c2b5SSeongJae Park /* Trim white space */ 81475c1c2b5SSeongJae Park if (sscanf(kbuf, "%s", ctx_name) != 1) { 81575c1c2b5SSeongJae Park ret = -EINVAL; 81675c1c2b5SSeongJae Park goto out; 81775c1c2b5SSeongJae Park } 81875c1c2b5SSeongJae Park 81975c1c2b5SSeongJae Park mutex_lock(&damon_dbgfs_lock); 8209210622aSRongwei Wang ret = dbgfs_mk_context(ctx_name); 8219210622aSRongwei Wang if (!ret) 8229210622aSRongwei Wang ret = count; 82375c1c2b5SSeongJae Park mutex_unlock(&damon_dbgfs_lock); 82475c1c2b5SSeongJae Park 82575c1c2b5SSeongJae Park out: 82675c1c2b5SSeongJae Park kfree(kbuf); 82775c1c2b5SSeongJae Park kfree(ctx_name); 82875c1c2b5SSeongJae Park return ret; 82975c1c2b5SSeongJae Park } 83075c1c2b5SSeongJae Park 83175c1c2b5SSeongJae Park /* 83275c1c2b5SSeongJae Park * Remove a context of @name and its debugfs directory. 83375c1c2b5SSeongJae Park * 83475c1c2b5SSeongJae Park * This function should be called while holding damon_dbgfs_lock. 83575c1c2b5SSeongJae Park * 83675c1c2b5SSeongJae Park * Return 0 on success, negative error code otherwise. 83775c1c2b5SSeongJae Park */ 83875c1c2b5SSeongJae Park static int dbgfs_rm_context(char *name) 83975c1c2b5SSeongJae Park { 84075c1c2b5SSeongJae Park struct dentry *root, *dir, **new_dirs; 84175c1c2b5SSeongJae Park struct damon_ctx **new_ctxs; 84275c1c2b5SSeongJae Park int i, j; 84375c1c2b5SSeongJae Park 84475c1c2b5SSeongJae Park if (damon_nr_running_ctxs()) 84575c1c2b5SSeongJae Park return -EBUSY; 84675c1c2b5SSeongJae Park 84775c1c2b5SSeongJae Park root = dbgfs_dirs[0]; 84875c1c2b5SSeongJae Park if (!root) 84975c1c2b5SSeongJae Park return -ENOENT; 85075c1c2b5SSeongJae Park 85175c1c2b5SSeongJae Park dir = debugfs_lookup(name, root); 85275c1c2b5SSeongJae Park if (!dir) 85375c1c2b5SSeongJae Park return -ENOENT; 85475c1c2b5SSeongJae Park 85575c1c2b5SSeongJae Park new_dirs = kmalloc_array(dbgfs_nr_ctxs - 1, sizeof(*dbgfs_dirs), 85675c1c2b5SSeongJae Park GFP_KERNEL); 85775c1c2b5SSeongJae Park if (!new_dirs) 85875c1c2b5SSeongJae Park return -ENOMEM; 85975c1c2b5SSeongJae Park 86075c1c2b5SSeongJae Park new_ctxs = kmalloc_array(dbgfs_nr_ctxs - 1, sizeof(*dbgfs_ctxs), 86175c1c2b5SSeongJae Park GFP_KERNEL); 86275c1c2b5SSeongJae Park if (!new_ctxs) { 86375c1c2b5SSeongJae Park kfree(new_dirs); 86475c1c2b5SSeongJae Park return -ENOMEM; 86575c1c2b5SSeongJae Park } 86675c1c2b5SSeongJae Park 86775c1c2b5SSeongJae Park for (i = 0, j = 0; i < dbgfs_nr_ctxs; i++) { 86875c1c2b5SSeongJae Park if (dbgfs_dirs[i] == dir) { 86975c1c2b5SSeongJae Park debugfs_remove(dbgfs_dirs[i]); 87075c1c2b5SSeongJae Park dbgfs_destroy_ctx(dbgfs_ctxs[i]); 87175c1c2b5SSeongJae Park continue; 87275c1c2b5SSeongJae Park } 87375c1c2b5SSeongJae Park new_dirs[j] = dbgfs_dirs[i]; 87475c1c2b5SSeongJae Park new_ctxs[j++] = dbgfs_ctxs[i]; 87575c1c2b5SSeongJae Park } 87675c1c2b5SSeongJae Park 87775c1c2b5SSeongJae Park kfree(dbgfs_dirs); 87875c1c2b5SSeongJae Park kfree(dbgfs_ctxs); 87975c1c2b5SSeongJae Park 88075c1c2b5SSeongJae Park dbgfs_dirs = new_dirs; 88175c1c2b5SSeongJae Park dbgfs_ctxs = new_ctxs; 88275c1c2b5SSeongJae Park dbgfs_nr_ctxs--; 88375c1c2b5SSeongJae Park 88475c1c2b5SSeongJae Park return 0; 88575c1c2b5SSeongJae Park } 88675c1c2b5SSeongJae Park 88775c1c2b5SSeongJae Park static ssize_t dbgfs_rm_context_write(struct file *file, 88875c1c2b5SSeongJae Park const char __user *buf, size_t count, loff_t *ppos) 88975c1c2b5SSeongJae Park { 89075c1c2b5SSeongJae Park char *kbuf; 8919210622aSRongwei Wang ssize_t ret; 89275c1c2b5SSeongJae Park char *ctx_name; 89375c1c2b5SSeongJae Park 89475c1c2b5SSeongJae Park kbuf = user_input_str(buf, count, ppos); 89575c1c2b5SSeongJae Park if (IS_ERR(kbuf)) 89675c1c2b5SSeongJae Park return PTR_ERR(kbuf); 89775c1c2b5SSeongJae Park ctx_name = kmalloc(count + 1, GFP_KERNEL); 89875c1c2b5SSeongJae Park if (!ctx_name) { 89975c1c2b5SSeongJae Park kfree(kbuf); 90075c1c2b5SSeongJae Park return -ENOMEM; 90175c1c2b5SSeongJae Park } 90275c1c2b5SSeongJae Park 90375c1c2b5SSeongJae Park /* Trim white space */ 90475c1c2b5SSeongJae Park if (sscanf(kbuf, "%s", ctx_name) != 1) { 90575c1c2b5SSeongJae Park ret = -EINVAL; 90675c1c2b5SSeongJae Park goto out; 90775c1c2b5SSeongJae Park } 90875c1c2b5SSeongJae Park 90975c1c2b5SSeongJae Park mutex_lock(&damon_dbgfs_lock); 9109210622aSRongwei Wang ret = dbgfs_rm_context(ctx_name); 9119210622aSRongwei Wang if (!ret) 9129210622aSRongwei Wang ret = count; 91375c1c2b5SSeongJae Park mutex_unlock(&damon_dbgfs_lock); 91475c1c2b5SSeongJae Park 91575c1c2b5SSeongJae Park out: 91675c1c2b5SSeongJae Park kfree(kbuf); 91775c1c2b5SSeongJae Park kfree(ctx_name); 91875c1c2b5SSeongJae Park return ret; 91975c1c2b5SSeongJae Park } 92075c1c2b5SSeongJae Park 9214bc05954SSeongJae Park static ssize_t dbgfs_monitor_on_read(struct file *file, 9224bc05954SSeongJae Park char __user *buf, size_t count, loff_t *ppos) 9234bc05954SSeongJae Park { 9244bc05954SSeongJae Park char monitor_on_buf[5]; 9254bc05954SSeongJae Park bool monitor_on = damon_nr_running_ctxs() != 0; 9264bc05954SSeongJae Park int len; 9274bc05954SSeongJae Park 9284bc05954SSeongJae Park len = scnprintf(monitor_on_buf, 5, monitor_on ? "on\n" : "off\n"); 9294bc05954SSeongJae Park 9304bc05954SSeongJae Park return simple_read_from_buffer(buf, count, ppos, monitor_on_buf, len); 9314bc05954SSeongJae Park } 9324bc05954SSeongJae Park 9334bc05954SSeongJae Park static ssize_t dbgfs_monitor_on_write(struct file *file, 9344bc05954SSeongJae Park const char __user *buf, size_t count, loff_t *ppos) 9354bc05954SSeongJae Park { 9369210622aSRongwei Wang ssize_t ret; 9374bc05954SSeongJae Park char *kbuf; 9384bc05954SSeongJae Park 9394bc05954SSeongJae Park kbuf = user_input_str(buf, count, ppos); 9404bc05954SSeongJae Park if (IS_ERR(kbuf)) 9414bc05954SSeongJae Park return PTR_ERR(kbuf); 9424bc05954SSeongJae Park 9434bc05954SSeongJae Park /* Remove white space */ 9444bc05954SSeongJae Park if (sscanf(kbuf, "%s", kbuf) != 1) { 9454bc05954SSeongJae Park kfree(kbuf); 9464bc05954SSeongJae Park return -EINVAL; 9474bc05954SSeongJae Park } 9484bc05954SSeongJae Park 949d78f3853SSeongJae Park mutex_lock(&damon_dbgfs_lock); 950b5ca3e83SXin Hao if (!strncmp(kbuf, "on", count)) { 951b5ca3e83SXin Hao int i; 952b5ca3e83SXin Hao 953b5ca3e83SXin Hao for (i = 0; i < dbgfs_nr_ctxs; i++) { 954b5ca3e83SXin Hao if (damon_targets_empty(dbgfs_ctxs[i])) { 955b5ca3e83SXin Hao kfree(kbuf); 956d78f3853SSeongJae Park mutex_unlock(&damon_dbgfs_lock); 957b5ca3e83SXin Hao return -EINVAL; 958b5ca3e83SXin Hao } 959b5ca3e83SXin Hao } 9609210622aSRongwei Wang ret = damon_start(dbgfs_ctxs, dbgfs_nr_ctxs); 961b5ca3e83SXin Hao } else if (!strncmp(kbuf, "off", count)) { 9629210622aSRongwei Wang ret = damon_stop(dbgfs_ctxs, dbgfs_nr_ctxs); 963b5ca3e83SXin Hao } else { 9649210622aSRongwei Wang ret = -EINVAL; 965b5ca3e83SXin Hao } 966d78f3853SSeongJae Park mutex_unlock(&damon_dbgfs_lock); 9674bc05954SSeongJae Park 9689210622aSRongwei Wang if (!ret) 9699210622aSRongwei Wang ret = count; 9704bc05954SSeongJae Park kfree(kbuf); 9714bc05954SSeongJae Park return ret; 9724bc05954SSeongJae Park } 9734bc05954SSeongJae Park 97475c1c2b5SSeongJae Park static const struct file_operations mk_contexts_fops = { 97575c1c2b5SSeongJae Park .write = dbgfs_mk_context_write, 97675c1c2b5SSeongJae Park }; 97775c1c2b5SSeongJae Park 97875c1c2b5SSeongJae Park static const struct file_operations rm_contexts_fops = { 97975c1c2b5SSeongJae Park .write = dbgfs_rm_context_write, 98075c1c2b5SSeongJae Park }; 98175c1c2b5SSeongJae Park 9824bc05954SSeongJae Park static const struct file_operations monitor_on_fops = { 9834bc05954SSeongJae Park .read = dbgfs_monitor_on_read, 9844bc05954SSeongJae Park .write = dbgfs_monitor_on_write, 9854bc05954SSeongJae Park }; 9864bc05954SSeongJae Park 9874bc05954SSeongJae Park static int __init __damon_dbgfs_init(void) 9884bc05954SSeongJae Park { 9894bc05954SSeongJae Park struct dentry *dbgfs_root; 99075c1c2b5SSeongJae Park const char * const file_names[] = {"mk_contexts", "rm_contexts", 99175c1c2b5SSeongJae Park "monitor_on"}; 99275c1c2b5SSeongJae Park const struct file_operations *fops[] = {&mk_contexts_fops, 99375c1c2b5SSeongJae Park &rm_contexts_fops, &monitor_on_fops}; 9944bc05954SSeongJae Park int i; 9954bc05954SSeongJae Park 9964bc05954SSeongJae Park dbgfs_root = debugfs_create_dir("damon", NULL); 9974bc05954SSeongJae Park 9984bc05954SSeongJae Park for (i = 0; i < ARRAY_SIZE(file_names); i++) 9994bc05954SSeongJae Park debugfs_create_file(file_names[i], 0600, dbgfs_root, NULL, 10004bc05954SSeongJae Park fops[i]); 10014bc05954SSeongJae Park dbgfs_fill_ctx_dir(dbgfs_root, dbgfs_ctxs[0]); 10024bc05954SSeongJae Park 10034bc05954SSeongJae Park dbgfs_dirs = kmalloc_array(1, sizeof(dbgfs_root), GFP_KERNEL); 10044bc05954SSeongJae Park if (!dbgfs_dirs) { 10054bc05954SSeongJae Park debugfs_remove(dbgfs_root); 10064bc05954SSeongJae Park return -ENOMEM; 10074bc05954SSeongJae Park } 10084bc05954SSeongJae Park dbgfs_dirs[0] = dbgfs_root; 10094bc05954SSeongJae Park 10104bc05954SSeongJae Park return 0; 10114bc05954SSeongJae Park } 10124bc05954SSeongJae Park 10134bc05954SSeongJae Park /* 10144bc05954SSeongJae Park * Functions for the initialization 10154bc05954SSeongJae Park */ 10164bc05954SSeongJae Park 10174bc05954SSeongJae Park static int __init damon_dbgfs_init(void) 10184bc05954SSeongJae Park { 1019d78f3853SSeongJae Park int rc = -ENOMEM; 10204bc05954SSeongJae Park 1021d78f3853SSeongJae Park mutex_lock(&damon_dbgfs_lock); 10224bc05954SSeongJae Park dbgfs_ctxs = kmalloc(sizeof(*dbgfs_ctxs), GFP_KERNEL); 10234bc05954SSeongJae Park if (!dbgfs_ctxs) 1024d78f3853SSeongJae Park goto out; 10254bc05954SSeongJae Park dbgfs_ctxs[0] = dbgfs_new_ctx(); 10264bc05954SSeongJae Park if (!dbgfs_ctxs[0]) { 10274bc05954SSeongJae Park kfree(dbgfs_ctxs); 1028d78f3853SSeongJae Park goto out; 10294bc05954SSeongJae Park } 10304bc05954SSeongJae Park dbgfs_nr_ctxs = 1; 10314bc05954SSeongJae Park 10324bc05954SSeongJae Park rc = __damon_dbgfs_init(); 10334bc05954SSeongJae Park if (rc) { 10344bc05954SSeongJae Park kfree(dbgfs_ctxs[0]); 10354bc05954SSeongJae Park kfree(dbgfs_ctxs); 10364bc05954SSeongJae Park pr_err("%s: dbgfs init failed\n", __func__); 10374bc05954SSeongJae Park } 10384bc05954SSeongJae Park 1039d78f3853SSeongJae Park out: 1040d78f3853SSeongJae Park mutex_unlock(&damon_dbgfs_lock); 10414bc05954SSeongJae Park return rc; 10424bc05954SSeongJae Park } 10434bc05954SSeongJae Park 10444bc05954SSeongJae Park module_init(damon_dbgfs_init); 104517ccae8bSSeongJae Park 104617ccae8bSSeongJae Park #include "dbgfs-test.h" 1047