162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * DAMON Debugfs Interface 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Author: SeongJae Park <sjpark@amazon.de> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#define pr_fmt(fmt) "damon-dbgfs: " fmt 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/damon.h> 1162306a36Sopenharmony_ci#include <linux/debugfs.h> 1262306a36Sopenharmony_ci#include <linux/file.h> 1362306a36Sopenharmony_ci#include <linux/mm.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/page_idle.h> 1662306a36Sopenharmony_ci#include <linux/slab.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistatic struct damon_ctx **dbgfs_ctxs; 1962306a36Sopenharmony_cistatic int dbgfs_nr_ctxs; 2062306a36Sopenharmony_cistatic struct dentry **dbgfs_dirs; 2162306a36Sopenharmony_cistatic DEFINE_MUTEX(damon_dbgfs_lock); 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic void damon_dbgfs_warn_deprecation(void) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci pr_warn_once("DAMON debugfs interface is deprecated, " 2662306a36Sopenharmony_ci "so users should move to DAMON_SYSFS. If you cannot, " 2762306a36Sopenharmony_ci "please report your usecase to damon@lists.linux.dev and " 2862306a36Sopenharmony_ci "linux-mm@kvack.org.\n"); 2962306a36Sopenharmony_ci} 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci/* 3262306a36Sopenharmony_ci * Returns non-empty string on success, negative error code otherwise. 3362306a36Sopenharmony_ci */ 3462306a36Sopenharmony_cistatic char *user_input_str(const char __user *buf, size_t count, loff_t *ppos) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci char *kbuf; 3762306a36Sopenharmony_ci ssize_t ret; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci /* We do not accept continuous write */ 4062306a36Sopenharmony_ci if (*ppos) 4162306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci kbuf = kmalloc(count + 1, GFP_KERNEL | __GFP_NOWARN); 4462306a36Sopenharmony_ci if (!kbuf) 4562306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci ret = simple_write_to_buffer(kbuf, count + 1, ppos, buf, count); 4862306a36Sopenharmony_ci if (ret != count) { 4962306a36Sopenharmony_ci kfree(kbuf); 5062306a36Sopenharmony_ci return ERR_PTR(-EIO); 5162306a36Sopenharmony_ci } 5262306a36Sopenharmony_ci kbuf[ret] = '\0'; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci return kbuf; 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic ssize_t dbgfs_attrs_read(struct file *file, 5862306a36Sopenharmony_ci char __user *buf, size_t count, loff_t *ppos) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci struct damon_ctx *ctx = file->private_data; 6162306a36Sopenharmony_ci char kbuf[128]; 6262306a36Sopenharmony_ci int ret; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci mutex_lock(&ctx->kdamond_lock); 6562306a36Sopenharmony_ci ret = scnprintf(kbuf, ARRAY_SIZE(kbuf), "%lu %lu %lu %lu %lu\n", 6662306a36Sopenharmony_ci ctx->attrs.sample_interval, ctx->attrs.aggr_interval, 6762306a36Sopenharmony_ci ctx->attrs.ops_update_interval, 6862306a36Sopenharmony_ci ctx->attrs.min_nr_regions, ctx->attrs.max_nr_regions); 6962306a36Sopenharmony_ci mutex_unlock(&ctx->kdamond_lock); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci return simple_read_from_buffer(buf, count, ppos, kbuf, ret); 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic ssize_t dbgfs_attrs_write(struct file *file, 7562306a36Sopenharmony_ci const char __user *buf, size_t count, loff_t *ppos) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci struct damon_ctx *ctx = file->private_data; 7862306a36Sopenharmony_ci struct damon_attrs attrs; 7962306a36Sopenharmony_ci char *kbuf; 8062306a36Sopenharmony_ci ssize_t ret; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci kbuf = user_input_str(buf, count, ppos); 8362306a36Sopenharmony_ci if (IS_ERR(kbuf)) 8462306a36Sopenharmony_ci return PTR_ERR(kbuf); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci if (sscanf(kbuf, "%lu %lu %lu %lu %lu", 8762306a36Sopenharmony_ci &attrs.sample_interval, &attrs.aggr_interval, 8862306a36Sopenharmony_ci &attrs.ops_update_interval, 8962306a36Sopenharmony_ci &attrs.min_nr_regions, 9062306a36Sopenharmony_ci &attrs.max_nr_regions) != 5) { 9162306a36Sopenharmony_ci ret = -EINVAL; 9262306a36Sopenharmony_ci goto out; 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci mutex_lock(&ctx->kdamond_lock); 9662306a36Sopenharmony_ci if (ctx->kdamond) { 9762306a36Sopenharmony_ci ret = -EBUSY; 9862306a36Sopenharmony_ci goto unlock_out; 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci ret = damon_set_attrs(ctx, &attrs); 10262306a36Sopenharmony_ci if (!ret) 10362306a36Sopenharmony_ci ret = count; 10462306a36Sopenharmony_ciunlock_out: 10562306a36Sopenharmony_ci mutex_unlock(&ctx->kdamond_lock); 10662306a36Sopenharmony_ciout: 10762306a36Sopenharmony_ci kfree(kbuf); 10862306a36Sopenharmony_ci return ret; 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci/* 11262306a36Sopenharmony_ci * Return corresponding dbgfs' scheme action value (int) for the given 11362306a36Sopenharmony_ci * damos_action if the given damos_action value is valid and supported by 11462306a36Sopenharmony_ci * dbgfs, negative error code otherwise. 11562306a36Sopenharmony_ci */ 11662306a36Sopenharmony_cistatic int damos_action_to_dbgfs_scheme_action(enum damos_action action) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci switch (action) { 11962306a36Sopenharmony_ci case DAMOS_WILLNEED: 12062306a36Sopenharmony_ci return 0; 12162306a36Sopenharmony_ci case DAMOS_COLD: 12262306a36Sopenharmony_ci return 1; 12362306a36Sopenharmony_ci case DAMOS_PAGEOUT: 12462306a36Sopenharmony_ci return 2; 12562306a36Sopenharmony_ci case DAMOS_HUGEPAGE: 12662306a36Sopenharmony_ci return 3; 12762306a36Sopenharmony_ci case DAMOS_NOHUGEPAGE: 12862306a36Sopenharmony_ci return 4; 12962306a36Sopenharmony_ci case DAMOS_STAT: 13062306a36Sopenharmony_ci return 5; 13162306a36Sopenharmony_ci default: 13262306a36Sopenharmony_ci return -EINVAL; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic ssize_t sprint_schemes(struct damon_ctx *c, char *buf, ssize_t len) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci struct damos *s; 13962306a36Sopenharmony_ci int written = 0; 14062306a36Sopenharmony_ci int rc; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci damon_for_each_scheme(s, c) { 14362306a36Sopenharmony_ci rc = scnprintf(&buf[written], len - written, 14462306a36Sopenharmony_ci "%lu %lu %u %u %u %u %d %lu %lu %lu %u %u %u %d %lu %lu %lu %lu %lu %lu %lu %lu %lu\n", 14562306a36Sopenharmony_ci s->pattern.min_sz_region, 14662306a36Sopenharmony_ci s->pattern.max_sz_region, 14762306a36Sopenharmony_ci s->pattern.min_nr_accesses, 14862306a36Sopenharmony_ci s->pattern.max_nr_accesses, 14962306a36Sopenharmony_ci s->pattern.min_age_region, 15062306a36Sopenharmony_ci s->pattern.max_age_region, 15162306a36Sopenharmony_ci damos_action_to_dbgfs_scheme_action(s->action), 15262306a36Sopenharmony_ci s->quota.ms, s->quota.sz, 15362306a36Sopenharmony_ci s->quota.reset_interval, 15462306a36Sopenharmony_ci s->quota.weight_sz, 15562306a36Sopenharmony_ci s->quota.weight_nr_accesses, 15662306a36Sopenharmony_ci s->quota.weight_age, 15762306a36Sopenharmony_ci s->wmarks.metric, s->wmarks.interval, 15862306a36Sopenharmony_ci s->wmarks.high, s->wmarks.mid, s->wmarks.low, 15962306a36Sopenharmony_ci s->stat.nr_tried, s->stat.sz_tried, 16062306a36Sopenharmony_ci s->stat.nr_applied, s->stat.sz_applied, 16162306a36Sopenharmony_ci s->stat.qt_exceeds); 16262306a36Sopenharmony_ci if (!rc) 16362306a36Sopenharmony_ci return -ENOMEM; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci written += rc; 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci return written; 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic ssize_t dbgfs_schemes_read(struct file *file, char __user *buf, 17162306a36Sopenharmony_ci size_t count, loff_t *ppos) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci struct damon_ctx *ctx = file->private_data; 17462306a36Sopenharmony_ci char *kbuf; 17562306a36Sopenharmony_ci ssize_t len; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci kbuf = kmalloc(count, GFP_KERNEL | __GFP_NOWARN); 17862306a36Sopenharmony_ci if (!kbuf) 17962306a36Sopenharmony_ci return -ENOMEM; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci mutex_lock(&ctx->kdamond_lock); 18262306a36Sopenharmony_ci len = sprint_schemes(ctx, kbuf, count); 18362306a36Sopenharmony_ci mutex_unlock(&ctx->kdamond_lock); 18462306a36Sopenharmony_ci if (len < 0) 18562306a36Sopenharmony_ci goto out; 18662306a36Sopenharmony_ci len = simple_read_from_buffer(buf, count, ppos, kbuf, len); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ciout: 18962306a36Sopenharmony_ci kfree(kbuf); 19062306a36Sopenharmony_ci return len; 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistatic void free_schemes_arr(struct damos **schemes, ssize_t nr_schemes) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci ssize_t i; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci for (i = 0; i < nr_schemes; i++) 19862306a36Sopenharmony_ci kfree(schemes[i]); 19962306a36Sopenharmony_ci kfree(schemes); 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci/* 20362306a36Sopenharmony_ci * Return corresponding damos_action for the given dbgfs input for a scheme 20462306a36Sopenharmony_ci * action if the input is valid, negative error code otherwise. 20562306a36Sopenharmony_ci */ 20662306a36Sopenharmony_cistatic enum damos_action dbgfs_scheme_action_to_damos_action(int dbgfs_action) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci switch (dbgfs_action) { 20962306a36Sopenharmony_ci case 0: 21062306a36Sopenharmony_ci return DAMOS_WILLNEED; 21162306a36Sopenharmony_ci case 1: 21262306a36Sopenharmony_ci return DAMOS_COLD; 21362306a36Sopenharmony_ci case 2: 21462306a36Sopenharmony_ci return DAMOS_PAGEOUT; 21562306a36Sopenharmony_ci case 3: 21662306a36Sopenharmony_ci return DAMOS_HUGEPAGE; 21762306a36Sopenharmony_ci case 4: 21862306a36Sopenharmony_ci return DAMOS_NOHUGEPAGE; 21962306a36Sopenharmony_ci case 5: 22062306a36Sopenharmony_ci return DAMOS_STAT; 22162306a36Sopenharmony_ci default: 22262306a36Sopenharmony_ci return -EINVAL; 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci/* 22762306a36Sopenharmony_ci * Converts a string into an array of struct damos pointers 22862306a36Sopenharmony_ci * 22962306a36Sopenharmony_ci * Returns an array of struct damos pointers that converted if the conversion 23062306a36Sopenharmony_ci * success, or NULL otherwise. 23162306a36Sopenharmony_ci */ 23262306a36Sopenharmony_cistatic struct damos **str_to_schemes(const char *str, ssize_t len, 23362306a36Sopenharmony_ci ssize_t *nr_schemes) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci struct damos *scheme, **schemes; 23662306a36Sopenharmony_ci const int max_nr_schemes = 256; 23762306a36Sopenharmony_ci int pos = 0, parsed, ret; 23862306a36Sopenharmony_ci unsigned int action_input; 23962306a36Sopenharmony_ci enum damos_action action; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci schemes = kmalloc_array(max_nr_schemes, sizeof(scheme), 24262306a36Sopenharmony_ci GFP_KERNEL); 24362306a36Sopenharmony_ci if (!schemes) 24462306a36Sopenharmony_ci return NULL; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci *nr_schemes = 0; 24762306a36Sopenharmony_ci while (pos < len && *nr_schemes < max_nr_schemes) { 24862306a36Sopenharmony_ci struct damos_access_pattern pattern = {}; 24962306a36Sopenharmony_ci struct damos_quota quota = {}; 25062306a36Sopenharmony_ci struct damos_watermarks wmarks; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci ret = sscanf(&str[pos], 25362306a36Sopenharmony_ci "%lu %lu %u %u %u %u %u %lu %lu %lu %u %u %u %u %lu %lu %lu %lu%n", 25462306a36Sopenharmony_ci &pattern.min_sz_region, &pattern.max_sz_region, 25562306a36Sopenharmony_ci &pattern.min_nr_accesses, 25662306a36Sopenharmony_ci &pattern.max_nr_accesses, 25762306a36Sopenharmony_ci &pattern.min_age_region, 25862306a36Sopenharmony_ci &pattern.max_age_region, 25962306a36Sopenharmony_ci &action_input, "a.ms, 26062306a36Sopenharmony_ci "a.sz, "a.reset_interval, 26162306a36Sopenharmony_ci "a.weight_sz, "a.weight_nr_accesses, 26262306a36Sopenharmony_ci "a.weight_age, &wmarks.metric, 26362306a36Sopenharmony_ci &wmarks.interval, &wmarks.high, &wmarks.mid, 26462306a36Sopenharmony_ci &wmarks.low, &parsed); 26562306a36Sopenharmony_ci if (ret != 18) 26662306a36Sopenharmony_ci break; 26762306a36Sopenharmony_ci action = dbgfs_scheme_action_to_damos_action(action_input); 26862306a36Sopenharmony_ci if ((int)action < 0) 26962306a36Sopenharmony_ci goto fail; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci if (pattern.min_sz_region > pattern.max_sz_region || 27262306a36Sopenharmony_ci pattern.min_nr_accesses > pattern.max_nr_accesses || 27362306a36Sopenharmony_ci pattern.min_age_region > pattern.max_age_region) 27462306a36Sopenharmony_ci goto fail; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci if (wmarks.high < wmarks.mid || wmarks.high < wmarks.low || 27762306a36Sopenharmony_ci wmarks.mid < wmarks.low) 27862306a36Sopenharmony_ci goto fail; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci pos += parsed; 28162306a36Sopenharmony_ci scheme = damon_new_scheme(&pattern, action, "a, &wmarks); 28262306a36Sopenharmony_ci if (!scheme) 28362306a36Sopenharmony_ci goto fail; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci schemes[*nr_schemes] = scheme; 28662306a36Sopenharmony_ci *nr_schemes += 1; 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci return schemes; 28962306a36Sopenharmony_cifail: 29062306a36Sopenharmony_ci free_schemes_arr(schemes, *nr_schemes); 29162306a36Sopenharmony_ci return NULL; 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cistatic ssize_t dbgfs_schemes_write(struct file *file, const char __user *buf, 29562306a36Sopenharmony_ci size_t count, loff_t *ppos) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci struct damon_ctx *ctx = file->private_data; 29862306a36Sopenharmony_ci char *kbuf; 29962306a36Sopenharmony_ci struct damos **schemes; 30062306a36Sopenharmony_ci ssize_t nr_schemes = 0, ret; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci kbuf = user_input_str(buf, count, ppos); 30362306a36Sopenharmony_ci if (IS_ERR(kbuf)) 30462306a36Sopenharmony_ci return PTR_ERR(kbuf); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci schemes = str_to_schemes(kbuf, count, &nr_schemes); 30762306a36Sopenharmony_ci if (!schemes) { 30862306a36Sopenharmony_ci ret = -EINVAL; 30962306a36Sopenharmony_ci goto out; 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci mutex_lock(&ctx->kdamond_lock); 31362306a36Sopenharmony_ci if (ctx->kdamond) { 31462306a36Sopenharmony_ci ret = -EBUSY; 31562306a36Sopenharmony_ci goto unlock_out; 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci damon_set_schemes(ctx, schemes, nr_schemes); 31962306a36Sopenharmony_ci ret = count; 32062306a36Sopenharmony_ci nr_schemes = 0; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ciunlock_out: 32362306a36Sopenharmony_ci mutex_unlock(&ctx->kdamond_lock); 32462306a36Sopenharmony_ci free_schemes_arr(schemes, nr_schemes); 32562306a36Sopenharmony_ciout: 32662306a36Sopenharmony_ci kfree(kbuf); 32762306a36Sopenharmony_ci return ret; 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_cistatic ssize_t sprint_target_ids(struct damon_ctx *ctx, char *buf, ssize_t len) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci struct damon_target *t; 33362306a36Sopenharmony_ci int id; 33462306a36Sopenharmony_ci int written = 0; 33562306a36Sopenharmony_ci int rc; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci damon_for_each_target(t, ctx) { 33862306a36Sopenharmony_ci if (damon_target_has_pid(ctx)) 33962306a36Sopenharmony_ci /* Show pid numbers to debugfs users */ 34062306a36Sopenharmony_ci id = pid_vnr(t->pid); 34162306a36Sopenharmony_ci else 34262306a36Sopenharmony_ci /* Show 42 for physical address space, just for fun */ 34362306a36Sopenharmony_ci id = 42; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci rc = scnprintf(&buf[written], len - written, "%d ", id); 34662306a36Sopenharmony_ci if (!rc) 34762306a36Sopenharmony_ci return -ENOMEM; 34862306a36Sopenharmony_ci written += rc; 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci if (written) 35162306a36Sopenharmony_ci written -= 1; 35262306a36Sopenharmony_ci written += scnprintf(&buf[written], len - written, "\n"); 35362306a36Sopenharmony_ci return written; 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cistatic ssize_t dbgfs_target_ids_read(struct file *file, 35762306a36Sopenharmony_ci char __user *buf, size_t count, loff_t *ppos) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci struct damon_ctx *ctx = file->private_data; 36062306a36Sopenharmony_ci ssize_t len; 36162306a36Sopenharmony_ci char ids_buf[320]; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci mutex_lock(&ctx->kdamond_lock); 36462306a36Sopenharmony_ci len = sprint_target_ids(ctx, ids_buf, 320); 36562306a36Sopenharmony_ci mutex_unlock(&ctx->kdamond_lock); 36662306a36Sopenharmony_ci if (len < 0) 36762306a36Sopenharmony_ci return len; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci return simple_read_from_buffer(buf, count, ppos, ids_buf, len); 37062306a36Sopenharmony_ci} 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci/* 37362306a36Sopenharmony_ci * Converts a string into an integers array 37462306a36Sopenharmony_ci * 37562306a36Sopenharmony_ci * Returns an array of integers array if the conversion success, or NULL 37662306a36Sopenharmony_ci * otherwise. 37762306a36Sopenharmony_ci */ 37862306a36Sopenharmony_cistatic int *str_to_ints(const char *str, ssize_t len, ssize_t *nr_ints) 37962306a36Sopenharmony_ci{ 38062306a36Sopenharmony_ci int *array; 38162306a36Sopenharmony_ci const int max_nr_ints = 32; 38262306a36Sopenharmony_ci int nr; 38362306a36Sopenharmony_ci int pos = 0, parsed, ret; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci *nr_ints = 0; 38662306a36Sopenharmony_ci array = kmalloc_array(max_nr_ints, sizeof(*array), GFP_KERNEL); 38762306a36Sopenharmony_ci if (!array) 38862306a36Sopenharmony_ci return NULL; 38962306a36Sopenharmony_ci while (*nr_ints < max_nr_ints && pos < len) { 39062306a36Sopenharmony_ci ret = sscanf(&str[pos], "%d%n", &nr, &parsed); 39162306a36Sopenharmony_ci pos += parsed; 39262306a36Sopenharmony_ci if (ret != 1) 39362306a36Sopenharmony_ci break; 39462306a36Sopenharmony_ci array[*nr_ints] = nr; 39562306a36Sopenharmony_ci *nr_ints += 1; 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci return array; 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_cistatic void dbgfs_put_pids(struct pid **pids, int nr_pids) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci int i; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci for (i = 0; i < nr_pids; i++) 40662306a36Sopenharmony_ci put_pid(pids[i]); 40762306a36Sopenharmony_ci} 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci/* 41062306a36Sopenharmony_ci * Converts a string into an struct pid pointers array 41162306a36Sopenharmony_ci * 41262306a36Sopenharmony_ci * Returns an array of struct pid pointers if the conversion success, or NULL 41362306a36Sopenharmony_ci * otherwise. 41462306a36Sopenharmony_ci */ 41562306a36Sopenharmony_cistatic struct pid **str_to_pids(const char *str, ssize_t len, ssize_t *nr_pids) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci int *ints; 41862306a36Sopenharmony_ci ssize_t nr_ints; 41962306a36Sopenharmony_ci struct pid **pids; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci *nr_pids = 0; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci ints = str_to_ints(str, len, &nr_ints); 42462306a36Sopenharmony_ci if (!ints) 42562306a36Sopenharmony_ci return NULL; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci pids = kmalloc_array(nr_ints, sizeof(*pids), GFP_KERNEL); 42862306a36Sopenharmony_ci if (!pids) 42962306a36Sopenharmony_ci goto out; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci for (; *nr_pids < nr_ints; (*nr_pids)++) { 43262306a36Sopenharmony_ci pids[*nr_pids] = find_get_pid(ints[*nr_pids]); 43362306a36Sopenharmony_ci if (!pids[*nr_pids]) { 43462306a36Sopenharmony_ci dbgfs_put_pids(pids, *nr_pids); 43562306a36Sopenharmony_ci kfree(ints); 43662306a36Sopenharmony_ci kfree(pids); 43762306a36Sopenharmony_ci return NULL; 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci } 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ciout: 44262306a36Sopenharmony_ci kfree(ints); 44362306a36Sopenharmony_ci return pids; 44462306a36Sopenharmony_ci} 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci/* 44762306a36Sopenharmony_ci * dbgfs_set_targets() - Set monitoring targets. 44862306a36Sopenharmony_ci * @ctx: monitoring context 44962306a36Sopenharmony_ci * @nr_targets: number of targets 45062306a36Sopenharmony_ci * @pids: array of target pids (size is same to @nr_targets) 45162306a36Sopenharmony_ci * 45262306a36Sopenharmony_ci * This function should not be called while the kdamond is running. @pids is 45362306a36Sopenharmony_ci * ignored if the context is not configured to have pid in each target. On 45462306a36Sopenharmony_ci * failure, reference counts of all pids in @pids are decremented. 45562306a36Sopenharmony_ci * 45662306a36Sopenharmony_ci * Return: 0 on success, negative error code otherwise. 45762306a36Sopenharmony_ci */ 45862306a36Sopenharmony_cistatic int dbgfs_set_targets(struct damon_ctx *ctx, ssize_t nr_targets, 45962306a36Sopenharmony_ci struct pid **pids) 46062306a36Sopenharmony_ci{ 46162306a36Sopenharmony_ci ssize_t i; 46262306a36Sopenharmony_ci struct damon_target *t, *next; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci damon_for_each_target_safe(t, next, ctx) { 46562306a36Sopenharmony_ci if (damon_target_has_pid(ctx)) 46662306a36Sopenharmony_ci put_pid(t->pid); 46762306a36Sopenharmony_ci damon_destroy_target(t); 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci for (i = 0; i < nr_targets; i++) { 47162306a36Sopenharmony_ci t = damon_new_target(); 47262306a36Sopenharmony_ci if (!t) { 47362306a36Sopenharmony_ci damon_for_each_target_safe(t, next, ctx) 47462306a36Sopenharmony_ci damon_destroy_target(t); 47562306a36Sopenharmony_ci if (damon_target_has_pid(ctx)) 47662306a36Sopenharmony_ci dbgfs_put_pids(pids, nr_targets); 47762306a36Sopenharmony_ci return -ENOMEM; 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci if (damon_target_has_pid(ctx)) 48062306a36Sopenharmony_ci t->pid = pids[i]; 48162306a36Sopenharmony_ci damon_add_target(ctx, t); 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci return 0; 48562306a36Sopenharmony_ci} 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_cistatic ssize_t dbgfs_target_ids_write(struct file *file, 48862306a36Sopenharmony_ci const char __user *buf, size_t count, loff_t *ppos) 48962306a36Sopenharmony_ci{ 49062306a36Sopenharmony_ci struct damon_ctx *ctx = file->private_data; 49162306a36Sopenharmony_ci bool id_is_pid = true; 49262306a36Sopenharmony_ci char *kbuf; 49362306a36Sopenharmony_ci struct pid **target_pids = NULL; 49462306a36Sopenharmony_ci ssize_t nr_targets; 49562306a36Sopenharmony_ci ssize_t ret; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci kbuf = user_input_str(buf, count, ppos); 49862306a36Sopenharmony_ci if (IS_ERR(kbuf)) 49962306a36Sopenharmony_ci return PTR_ERR(kbuf); 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci if (!strncmp(kbuf, "paddr\n", count)) { 50262306a36Sopenharmony_ci id_is_pid = false; 50362306a36Sopenharmony_ci nr_targets = 1; 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci if (id_is_pid) { 50762306a36Sopenharmony_ci target_pids = str_to_pids(kbuf, count, &nr_targets); 50862306a36Sopenharmony_ci if (!target_pids) { 50962306a36Sopenharmony_ci ret = -ENOMEM; 51062306a36Sopenharmony_ci goto out; 51162306a36Sopenharmony_ci } 51262306a36Sopenharmony_ci } 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci mutex_lock(&ctx->kdamond_lock); 51562306a36Sopenharmony_ci if (ctx->kdamond) { 51662306a36Sopenharmony_ci if (id_is_pid) 51762306a36Sopenharmony_ci dbgfs_put_pids(target_pids, nr_targets); 51862306a36Sopenharmony_ci ret = -EBUSY; 51962306a36Sopenharmony_ci goto unlock_out; 52062306a36Sopenharmony_ci } 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci /* remove previously set targets */ 52362306a36Sopenharmony_ci dbgfs_set_targets(ctx, 0, NULL); 52462306a36Sopenharmony_ci if (!nr_targets) { 52562306a36Sopenharmony_ci ret = count; 52662306a36Sopenharmony_ci goto unlock_out; 52762306a36Sopenharmony_ci } 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci /* Configure the context for the address space type */ 53062306a36Sopenharmony_ci if (id_is_pid) 53162306a36Sopenharmony_ci ret = damon_select_ops(ctx, DAMON_OPS_VADDR); 53262306a36Sopenharmony_ci else 53362306a36Sopenharmony_ci ret = damon_select_ops(ctx, DAMON_OPS_PADDR); 53462306a36Sopenharmony_ci if (ret) 53562306a36Sopenharmony_ci goto unlock_out; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci ret = dbgfs_set_targets(ctx, nr_targets, target_pids); 53862306a36Sopenharmony_ci if (!ret) 53962306a36Sopenharmony_ci ret = count; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ciunlock_out: 54262306a36Sopenharmony_ci mutex_unlock(&ctx->kdamond_lock); 54362306a36Sopenharmony_ci kfree(target_pids); 54462306a36Sopenharmony_ciout: 54562306a36Sopenharmony_ci kfree(kbuf); 54662306a36Sopenharmony_ci return ret; 54762306a36Sopenharmony_ci} 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_cistatic ssize_t sprint_init_regions(struct damon_ctx *c, char *buf, ssize_t len) 55062306a36Sopenharmony_ci{ 55162306a36Sopenharmony_ci struct damon_target *t; 55262306a36Sopenharmony_ci struct damon_region *r; 55362306a36Sopenharmony_ci int target_idx = 0; 55462306a36Sopenharmony_ci int written = 0; 55562306a36Sopenharmony_ci int rc; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci damon_for_each_target(t, c) { 55862306a36Sopenharmony_ci damon_for_each_region(r, t) { 55962306a36Sopenharmony_ci rc = scnprintf(&buf[written], len - written, 56062306a36Sopenharmony_ci "%d %lu %lu\n", 56162306a36Sopenharmony_ci target_idx, r->ar.start, r->ar.end); 56262306a36Sopenharmony_ci if (!rc) 56362306a36Sopenharmony_ci return -ENOMEM; 56462306a36Sopenharmony_ci written += rc; 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci target_idx++; 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci return written; 56962306a36Sopenharmony_ci} 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_cistatic ssize_t dbgfs_init_regions_read(struct file *file, char __user *buf, 57262306a36Sopenharmony_ci size_t count, loff_t *ppos) 57362306a36Sopenharmony_ci{ 57462306a36Sopenharmony_ci struct damon_ctx *ctx = file->private_data; 57562306a36Sopenharmony_ci char *kbuf; 57662306a36Sopenharmony_ci ssize_t len; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci kbuf = kmalloc(count, GFP_KERNEL | __GFP_NOWARN); 57962306a36Sopenharmony_ci if (!kbuf) 58062306a36Sopenharmony_ci return -ENOMEM; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci mutex_lock(&ctx->kdamond_lock); 58362306a36Sopenharmony_ci if (ctx->kdamond) { 58462306a36Sopenharmony_ci mutex_unlock(&ctx->kdamond_lock); 58562306a36Sopenharmony_ci len = -EBUSY; 58662306a36Sopenharmony_ci goto out; 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci len = sprint_init_regions(ctx, kbuf, count); 59062306a36Sopenharmony_ci mutex_unlock(&ctx->kdamond_lock); 59162306a36Sopenharmony_ci if (len < 0) 59262306a36Sopenharmony_ci goto out; 59362306a36Sopenharmony_ci len = simple_read_from_buffer(buf, count, ppos, kbuf, len); 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ciout: 59662306a36Sopenharmony_ci kfree(kbuf); 59762306a36Sopenharmony_ci return len; 59862306a36Sopenharmony_ci} 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_cistatic int add_init_region(struct damon_ctx *c, int target_idx, 60162306a36Sopenharmony_ci struct damon_addr_range *ar) 60262306a36Sopenharmony_ci{ 60362306a36Sopenharmony_ci struct damon_target *t; 60462306a36Sopenharmony_ci struct damon_region *r, *prev; 60562306a36Sopenharmony_ci unsigned long idx = 0; 60662306a36Sopenharmony_ci int rc = -EINVAL; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci if (ar->start >= ar->end) 60962306a36Sopenharmony_ci return -EINVAL; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci damon_for_each_target(t, c) { 61262306a36Sopenharmony_ci if (idx++ == target_idx) { 61362306a36Sopenharmony_ci r = damon_new_region(ar->start, ar->end); 61462306a36Sopenharmony_ci if (!r) 61562306a36Sopenharmony_ci return -ENOMEM; 61662306a36Sopenharmony_ci damon_add_region(r, t); 61762306a36Sopenharmony_ci if (damon_nr_regions(t) > 1) { 61862306a36Sopenharmony_ci prev = damon_prev_region(r); 61962306a36Sopenharmony_ci if (prev->ar.end > r->ar.start) { 62062306a36Sopenharmony_ci damon_destroy_region(r, t); 62162306a36Sopenharmony_ci return -EINVAL; 62262306a36Sopenharmony_ci } 62362306a36Sopenharmony_ci } 62462306a36Sopenharmony_ci rc = 0; 62562306a36Sopenharmony_ci } 62662306a36Sopenharmony_ci } 62762306a36Sopenharmony_ci return rc; 62862306a36Sopenharmony_ci} 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_cistatic int set_init_regions(struct damon_ctx *c, const char *str, ssize_t len) 63162306a36Sopenharmony_ci{ 63262306a36Sopenharmony_ci struct damon_target *t; 63362306a36Sopenharmony_ci struct damon_region *r, *next; 63462306a36Sopenharmony_ci int pos = 0, parsed, ret; 63562306a36Sopenharmony_ci int target_idx; 63662306a36Sopenharmony_ci struct damon_addr_range ar; 63762306a36Sopenharmony_ci int err; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci damon_for_each_target(t, c) { 64062306a36Sopenharmony_ci damon_for_each_region_safe(r, next, t) 64162306a36Sopenharmony_ci damon_destroy_region(r, t); 64262306a36Sopenharmony_ci } 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci while (pos < len) { 64562306a36Sopenharmony_ci ret = sscanf(&str[pos], "%d %lu %lu%n", 64662306a36Sopenharmony_ci &target_idx, &ar.start, &ar.end, &parsed); 64762306a36Sopenharmony_ci if (ret != 3) 64862306a36Sopenharmony_ci break; 64962306a36Sopenharmony_ci err = add_init_region(c, target_idx, &ar); 65062306a36Sopenharmony_ci if (err) 65162306a36Sopenharmony_ci goto fail; 65262306a36Sopenharmony_ci pos += parsed; 65362306a36Sopenharmony_ci } 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci return 0; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_cifail: 65862306a36Sopenharmony_ci damon_for_each_target(t, c) { 65962306a36Sopenharmony_ci damon_for_each_region_safe(r, next, t) 66062306a36Sopenharmony_ci damon_destroy_region(r, t); 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci return err; 66362306a36Sopenharmony_ci} 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_cistatic ssize_t dbgfs_init_regions_write(struct file *file, 66662306a36Sopenharmony_ci const char __user *buf, size_t count, 66762306a36Sopenharmony_ci loff_t *ppos) 66862306a36Sopenharmony_ci{ 66962306a36Sopenharmony_ci struct damon_ctx *ctx = file->private_data; 67062306a36Sopenharmony_ci char *kbuf; 67162306a36Sopenharmony_ci ssize_t ret = count; 67262306a36Sopenharmony_ci int err; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci kbuf = user_input_str(buf, count, ppos); 67562306a36Sopenharmony_ci if (IS_ERR(kbuf)) 67662306a36Sopenharmony_ci return PTR_ERR(kbuf); 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci mutex_lock(&ctx->kdamond_lock); 67962306a36Sopenharmony_ci if (ctx->kdamond) { 68062306a36Sopenharmony_ci ret = -EBUSY; 68162306a36Sopenharmony_ci goto unlock_out; 68262306a36Sopenharmony_ci } 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci err = set_init_regions(ctx, kbuf, ret); 68562306a36Sopenharmony_ci if (err) 68662306a36Sopenharmony_ci ret = err; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ciunlock_out: 68962306a36Sopenharmony_ci mutex_unlock(&ctx->kdamond_lock); 69062306a36Sopenharmony_ci kfree(kbuf); 69162306a36Sopenharmony_ci return ret; 69262306a36Sopenharmony_ci} 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_cistatic ssize_t dbgfs_kdamond_pid_read(struct file *file, 69562306a36Sopenharmony_ci char __user *buf, size_t count, loff_t *ppos) 69662306a36Sopenharmony_ci{ 69762306a36Sopenharmony_ci struct damon_ctx *ctx = file->private_data; 69862306a36Sopenharmony_ci char *kbuf; 69962306a36Sopenharmony_ci ssize_t len; 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci kbuf = kmalloc(count, GFP_KERNEL | __GFP_NOWARN); 70262306a36Sopenharmony_ci if (!kbuf) 70362306a36Sopenharmony_ci return -ENOMEM; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci mutex_lock(&ctx->kdamond_lock); 70662306a36Sopenharmony_ci if (ctx->kdamond) 70762306a36Sopenharmony_ci len = scnprintf(kbuf, count, "%d\n", ctx->kdamond->pid); 70862306a36Sopenharmony_ci else 70962306a36Sopenharmony_ci len = scnprintf(kbuf, count, "none\n"); 71062306a36Sopenharmony_ci mutex_unlock(&ctx->kdamond_lock); 71162306a36Sopenharmony_ci if (!len) 71262306a36Sopenharmony_ci goto out; 71362306a36Sopenharmony_ci len = simple_read_from_buffer(buf, count, ppos, kbuf, len); 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ciout: 71662306a36Sopenharmony_ci kfree(kbuf); 71762306a36Sopenharmony_ci return len; 71862306a36Sopenharmony_ci} 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_cistatic int damon_dbgfs_open(struct inode *inode, struct file *file) 72162306a36Sopenharmony_ci{ 72262306a36Sopenharmony_ci damon_dbgfs_warn_deprecation(); 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci file->private_data = inode->i_private; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci return nonseekable_open(inode, file); 72762306a36Sopenharmony_ci} 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_cistatic const struct file_operations attrs_fops = { 73062306a36Sopenharmony_ci .open = damon_dbgfs_open, 73162306a36Sopenharmony_ci .read = dbgfs_attrs_read, 73262306a36Sopenharmony_ci .write = dbgfs_attrs_write, 73362306a36Sopenharmony_ci}; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_cistatic const struct file_operations schemes_fops = { 73662306a36Sopenharmony_ci .open = damon_dbgfs_open, 73762306a36Sopenharmony_ci .read = dbgfs_schemes_read, 73862306a36Sopenharmony_ci .write = dbgfs_schemes_write, 73962306a36Sopenharmony_ci}; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_cistatic const struct file_operations target_ids_fops = { 74262306a36Sopenharmony_ci .open = damon_dbgfs_open, 74362306a36Sopenharmony_ci .read = dbgfs_target_ids_read, 74462306a36Sopenharmony_ci .write = dbgfs_target_ids_write, 74562306a36Sopenharmony_ci}; 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_cistatic const struct file_operations init_regions_fops = { 74862306a36Sopenharmony_ci .open = damon_dbgfs_open, 74962306a36Sopenharmony_ci .read = dbgfs_init_regions_read, 75062306a36Sopenharmony_ci .write = dbgfs_init_regions_write, 75162306a36Sopenharmony_ci}; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_cistatic const struct file_operations kdamond_pid_fops = { 75462306a36Sopenharmony_ci .open = damon_dbgfs_open, 75562306a36Sopenharmony_ci .read = dbgfs_kdamond_pid_read, 75662306a36Sopenharmony_ci}; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_cistatic void dbgfs_fill_ctx_dir(struct dentry *dir, struct damon_ctx *ctx) 75962306a36Sopenharmony_ci{ 76062306a36Sopenharmony_ci const char * const file_names[] = {"attrs", "schemes", "target_ids", 76162306a36Sopenharmony_ci "init_regions", "kdamond_pid"}; 76262306a36Sopenharmony_ci const struct file_operations *fops[] = {&attrs_fops, &schemes_fops, 76362306a36Sopenharmony_ci &target_ids_fops, &init_regions_fops, &kdamond_pid_fops}; 76462306a36Sopenharmony_ci int i; 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(file_names); i++) 76762306a36Sopenharmony_ci debugfs_create_file(file_names[i], 0600, dir, ctx, fops[i]); 76862306a36Sopenharmony_ci} 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_cistatic void dbgfs_before_terminate(struct damon_ctx *ctx) 77162306a36Sopenharmony_ci{ 77262306a36Sopenharmony_ci struct damon_target *t, *next; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci if (!damon_target_has_pid(ctx)) 77562306a36Sopenharmony_ci return; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci mutex_lock(&ctx->kdamond_lock); 77862306a36Sopenharmony_ci damon_for_each_target_safe(t, next, ctx) { 77962306a36Sopenharmony_ci put_pid(t->pid); 78062306a36Sopenharmony_ci damon_destroy_target(t); 78162306a36Sopenharmony_ci } 78262306a36Sopenharmony_ci mutex_unlock(&ctx->kdamond_lock); 78362306a36Sopenharmony_ci} 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_cistatic struct damon_ctx *dbgfs_new_ctx(void) 78662306a36Sopenharmony_ci{ 78762306a36Sopenharmony_ci struct damon_ctx *ctx; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci ctx = damon_new_ctx(); 79062306a36Sopenharmony_ci if (!ctx) 79162306a36Sopenharmony_ci return NULL; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci if (damon_select_ops(ctx, DAMON_OPS_VADDR) && 79462306a36Sopenharmony_ci damon_select_ops(ctx, DAMON_OPS_PADDR)) { 79562306a36Sopenharmony_ci damon_destroy_ctx(ctx); 79662306a36Sopenharmony_ci return NULL; 79762306a36Sopenharmony_ci } 79862306a36Sopenharmony_ci ctx->callback.before_terminate = dbgfs_before_terminate; 79962306a36Sopenharmony_ci return ctx; 80062306a36Sopenharmony_ci} 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_cistatic void dbgfs_destroy_ctx(struct damon_ctx *ctx) 80362306a36Sopenharmony_ci{ 80462306a36Sopenharmony_ci damon_destroy_ctx(ctx); 80562306a36Sopenharmony_ci} 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci/* 80862306a36Sopenharmony_ci * Make a context of @name and create a debugfs directory for it. 80962306a36Sopenharmony_ci * 81062306a36Sopenharmony_ci * This function should be called while holding damon_dbgfs_lock. 81162306a36Sopenharmony_ci * 81262306a36Sopenharmony_ci * Returns 0 on success, negative error code otherwise. 81362306a36Sopenharmony_ci */ 81462306a36Sopenharmony_cistatic int dbgfs_mk_context(char *name) 81562306a36Sopenharmony_ci{ 81662306a36Sopenharmony_ci struct dentry *root, **new_dirs, *new_dir; 81762306a36Sopenharmony_ci struct damon_ctx **new_ctxs, *new_ctx; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci if (damon_nr_running_ctxs()) 82062306a36Sopenharmony_ci return -EBUSY; 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci new_ctxs = krealloc(dbgfs_ctxs, sizeof(*dbgfs_ctxs) * 82362306a36Sopenharmony_ci (dbgfs_nr_ctxs + 1), GFP_KERNEL); 82462306a36Sopenharmony_ci if (!new_ctxs) 82562306a36Sopenharmony_ci return -ENOMEM; 82662306a36Sopenharmony_ci dbgfs_ctxs = new_ctxs; 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci new_dirs = krealloc(dbgfs_dirs, sizeof(*dbgfs_dirs) * 82962306a36Sopenharmony_ci (dbgfs_nr_ctxs + 1), GFP_KERNEL); 83062306a36Sopenharmony_ci if (!new_dirs) 83162306a36Sopenharmony_ci return -ENOMEM; 83262306a36Sopenharmony_ci dbgfs_dirs = new_dirs; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci root = dbgfs_dirs[0]; 83562306a36Sopenharmony_ci if (!root) 83662306a36Sopenharmony_ci return -ENOENT; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci new_dir = debugfs_create_dir(name, root); 83962306a36Sopenharmony_ci /* Below check is required for a potential duplicated name case */ 84062306a36Sopenharmony_ci if (IS_ERR(new_dir)) 84162306a36Sopenharmony_ci return PTR_ERR(new_dir); 84262306a36Sopenharmony_ci dbgfs_dirs[dbgfs_nr_ctxs] = new_dir; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci new_ctx = dbgfs_new_ctx(); 84562306a36Sopenharmony_ci if (!new_ctx) { 84662306a36Sopenharmony_ci debugfs_remove(new_dir); 84762306a36Sopenharmony_ci dbgfs_dirs[dbgfs_nr_ctxs] = NULL; 84862306a36Sopenharmony_ci return -ENOMEM; 84962306a36Sopenharmony_ci } 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci dbgfs_ctxs[dbgfs_nr_ctxs] = new_ctx; 85262306a36Sopenharmony_ci dbgfs_fill_ctx_dir(dbgfs_dirs[dbgfs_nr_ctxs], 85362306a36Sopenharmony_ci dbgfs_ctxs[dbgfs_nr_ctxs]); 85462306a36Sopenharmony_ci dbgfs_nr_ctxs++; 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci return 0; 85762306a36Sopenharmony_ci} 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_cistatic ssize_t dbgfs_mk_context_write(struct file *file, 86062306a36Sopenharmony_ci const char __user *buf, size_t count, loff_t *ppos) 86162306a36Sopenharmony_ci{ 86262306a36Sopenharmony_ci char *kbuf; 86362306a36Sopenharmony_ci char *ctx_name; 86462306a36Sopenharmony_ci ssize_t ret; 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci kbuf = user_input_str(buf, count, ppos); 86762306a36Sopenharmony_ci if (IS_ERR(kbuf)) 86862306a36Sopenharmony_ci return PTR_ERR(kbuf); 86962306a36Sopenharmony_ci ctx_name = kmalloc(count + 1, GFP_KERNEL); 87062306a36Sopenharmony_ci if (!ctx_name) { 87162306a36Sopenharmony_ci kfree(kbuf); 87262306a36Sopenharmony_ci return -ENOMEM; 87362306a36Sopenharmony_ci } 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci /* Trim white space */ 87662306a36Sopenharmony_ci if (sscanf(kbuf, "%s", ctx_name) != 1) { 87762306a36Sopenharmony_ci ret = -EINVAL; 87862306a36Sopenharmony_ci goto out; 87962306a36Sopenharmony_ci } 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci mutex_lock(&damon_dbgfs_lock); 88262306a36Sopenharmony_ci ret = dbgfs_mk_context(ctx_name); 88362306a36Sopenharmony_ci if (!ret) 88462306a36Sopenharmony_ci ret = count; 88562306a36Sopenharmony_ci mutex_unlock(&damon_dbgfs_lock); 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ciout: 88862306a36Sopenharmony_ci kfree(kbuf); 88962306a36Sopenharmony_ci kfree(ctx_name); 89062306a36Sopenharmony_ci return ret; 89162306a36Sopenharmony_ci} 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci/* 89462306a36Sopenharmony_ci * Remove a context of @name and its debugfs directory. 89562306a36Sopenharmony_ci * 89662306a36Sopenharmony_ci * This function should be called while holding damon_dbgfs_lock. 89762306a36Sopenharmony_ci * 89862306a36Sopenharmony_ci * Return 0 on success, negative error code otherwise. 89962306a36Sopenharmony_ci */ 90062306a36Sopenharmony_cistatic int dbgfs_rm_context(char *name) 90162306a36Sopenharmony_ci{ 90262306a36Sopenharmony_ci struct dentry *root, *dir, **new_dirs; 90362306a36Sopenharmony_ci struct inode *inode; 90462306a36Sopenharmony_ci struct damon_ctx **new_ctxs; 90562306a36Sopenharmony_ci int i, j; 90662306a36Sopenharmony_ci int ret = 0; 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci if (damon_nr_running_ctxs()) 90962306a36Sopenharmony_ci return -EBUSY; 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci root = dbgfs_dirs[0]; 91262306a36Sopenharmony_ci if (!root) 91362306a36Sopenharmony_ci return -ENOENT; 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci dir = debugfs_lookup(name, root); 91662306a36Sopenharmony_ci if (!dir) 91762306a36Sopenharmony_ci return -ENOENT; 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci inode = d_inode(dir); 92062306a36Sopenharmony_ci if (!S_ISDIR(inode->i_mode)) { 92162306a36Sopenharmony_ci ret = -EINVAL; 92262306a36Sopenharmony_ci goto out_dput; 92362306a36Sopenharmony_ci } 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci new_dirs = kmalloc_array(dbgfs_nr_ctxs - 1, sizeof(*dbgfs_dirs), 92662306a36Sopenharmony_ci GFP_KERNEL); 92762306a36Sopenharmony_ci if (!new_dirs) { 92862306a36Sopenharmony_ci ret = -ENOMEM; 92962306a36Sopenharmony_ci goto out_dput; 93062306a36Sopenharmony_ci } 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci new_ctxs = kmalloc_array(dbgfs_nr_ctxs - 1, sizeof(*dbgfs_ctxs), 93362306a36Sopenharmony_ci GFP_KERNEL); 93462306a36Sopenharmony_ci if (!new_ctxs) { 93562306a36Sopenharmony_ci ret = -ENOMEM; 93662306a36Sopenharmony_ci goto out_new_dirs; 93762306a36Sopenharmony_ci } 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci for (i = 0, j = 0; i < dbgfs_nr_ctxs; i++) { 94062306a36Sopenharmony_ci if (dbgfs_dirs[i] == dir) { 94162306a36Sopenharmony_ci debugfs_remove(dbgfs_dirs[i]); 94262306a36Sopenharmony_ci dbgfs_destroy_ctx(dbgfs_ctxs[i]); 94362306a36Sopenharmony_ci continue; 94462306a36Sopenharmony_ci } 94562306a36Sopenharmony_ci new_dirs[j] = dbgfs_dirs[i]; 94662306a36Sopenharmony_ci new_ctxs[j++] = dbgfs_ctxs[i]; 94762306a36Sopenharmony_ci } 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci kfree(dbgfs_dirs); 95062306a36Sopenharmony_ci kfree(dbgfs_ctxs); 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci dbgfs_dirs = new_dirs; 95362306a36Sopenharmony_ci dbgfs_ctxs = new_ctxs; 95462306a36Sopenharmony_ci dbgfs_nr_ctxs--; 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci goto out_dput; 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ciout_new_dirs: 95962306a36Sopenharmony_ci kfree(new_dirs); 96062306a36Sopenharmony_ciout_dput: 96162306a36Sopenharmony_ci dput(dir); 96262306a36Sopenharmony_ci return ret; 96362306a36Sopenharmony_ci} 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_cistatic ssize_t dbgfs_rm_context_write(struct file *file, 96662306a36Sopenharmony_ci const char __user *buf, size_t count, loff_t *ppos) 96762306a36Sopenharmony_ci{ 96862306a36Sopenharmony_ci char *kbuf; 96962306a36Sopenharmony_ci ssize_t ret; 97062306a36Sopenharmony_ci char *ctx_name; 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci kbuf = user_input_str(buf, count, ppos); 97362306a36Sopenharmony_ci if (IS_ERR(kbuf)) 97462306a36Sopenharmony_ci return PTR_ERR(kbuf); 97562306a36Sopenharmony_ci ctx_name = kmalloc(count + 1, GFP_KERNEL); 97662306a36Sopenharmony_ci if (!ctx_name) { 97762306a36Sopenharmony_ci kfree(kbuf); 97862306a36Sopenharmony_ci return -ENOMEM; 97962306a36Sopenharmony_ci } 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci /* Trim white space */ 98262306a36Sopenharmony_ci if (sscanf(kbuf, "%s", ctx_name) != 1) { 98362306a36Sopenharmony_ci ret = -EINVAL; 98462306a36Sopenharmony_ci goto out; 98562306a36Sopenharmony_ci } 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci mutex_lock(&damon_dbgfs_lock); 98862306a36Sopenharmony_ci ret = dbgfs_rm_context(ctx_name); 98962306a36Sopenharmony_ci if (!ret) 99062306a36Sopenharmony_ci ret = count; 99162306a36Sopenharmony_ci mutex_unlock(&damon_dbgfs_lock); 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ciout: 99462306a36Sopenharmony_ci kfree(kbuf); 99562306a36Sopenharmony_ci kfree(ctx_name); 99662306a36Sopenharmony_ci return ret; 99762306a36Sopenharmony_ci} 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_cistatic ssize_t dbgfs_monitor_on_read(struct file *file, 100062306a36Sopenharmony_ci char __user *buf, size_t count, loff_t *ppos) 100162306a36Sopenharmony_ci{ 100262306a36Sopenharmony_ci char monitor_on_buf[5]; 100362306a36Sopenharmony_ci bool monitor_on = damon_nr_running_ctxs() != 0; 100462306a36Sopenharmony_ci int len; 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci len = scnprintf(monitor_on_buf, 5, monitor_on ? "on\n" : "off\n"); 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci return simple_read_from_buffer(buf, count, ppos, monitor_on_buf, len); 100962306a36Sopenharmony_ci} 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_cistatic ssize_t dbgfs_monitor_on_write(struct file *file, 101262306a36Sopenharmony_ci const char __user *buf, size_t count, loff_t *ppos) 101362306a36Sopenharmony_ci{ 101462306a36Sopenharmony_ci ssize_t ret; 101562306a36Sopenharmony_ci char *kbuf; 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci kbuf = user_input_str(buf, count, ppos); 101862306a36Sopenharmony_ci if (IS_ERR(kbuf)) 101962306a36Sopenharmony_ci return PTR_ERR(kbuf); 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci /* Remove white space */ 102262306a36Sopenharmony_ci if (sscanf(kbuf, "%s", kbuf) != 1) { 102362306a36Sopenharmony_ci kfree(kbuf); 102462306a36Sopenharmony_ci return -EINVAL; 102562306a36Sopenharmony_ci } 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci mutex_lock(&damon_dbgfs_lock); 102862306a36Sopenharmony_ci if (!strncmp(kbuf, "on", count)) { 102962306a36Sopenharmony_ci int i; 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci for (i = 0; i < dbgfs_nr_ctxs; i++) { 103262306a36Sopenharmony_ci if (damon_targets_empty(dbgfs_ctxs[i])) { 103362306a36Sopenharmony_ci kfree(kbuf); 103462306a36Sopenharmony_ci mutex_unlock(&damon_dbgfs_lock); 103562306a36Sopenharmony_ci return -EINVAL; 103662306a36Sopenharmony_ci } 103762306a36Sopenharmony_ci } 103862306a36Sopenharmony_ci ret = damon_start(dbgfs_ctxs, dbgfs_nr_ctxs, true); 103962306a36Sopenharmony_ci } else if (!strncmp(kbuf, "off", count)) { 104062306a36Sopenharmony_ci ret = damon_stop(dbgfs_ctxs, dbgfs_nr_ctxs); 104162306a36Sopenharmony_ci } else { 104262306a36Sopenharmony_ci ret = -EINVAL; 104362306a36Sopenharmony_ci } 104462306a36Sopenharmony_ci mutex_unlock(&damon_dbgfs_lock); 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci if (!ret) 104762306a36Sopenharmony_ci ret = count; 104862306a36Sopenharmony_ci kfree(kbuf); 104962306a36Sopenharmony_ci return ret; 105062306a36Sopenharmony_ci} 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_cistatic int damon_dbgfs_static_file_open(struct inode *inode, struct file *file) 105362306a36Sopenharmony_ci{ 105462306a36Sopenharmony_ci damon_dbgfs_warn_deprecation(); 105562306a36Sopenharmony_ci return nonseekable_open(inode, file); 105662306a36Sopenharmony_ci} 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_cistatic const struct file_operations mk_contexts_fops = { 105962306a36Sopenharmony_ci .open = damon_dbgfs_static_file_open, 106062306a36Sopenharmony_ci .write = dbgfs_mk_context_write, 106162306a36Sopenharmony_ci}; 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_cistatic const struct file_operations rm_contexts_fops = { 106462306a36Sopenharmony_ci .open = damon_dbgfs_static_file_open, 106562306a36Sopenharmony_ci .write = dbgfs_rm_context_write, 106662306a36Sopenharmony_ci}; 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_cistatic const struct file_operations monitor_on_fops = { 106962306a36Sopenharmony_ci .open = damon_dbgfs_static_file_open, 107062306a36Sopenharmony_ci .read = dbgfs_monitor_on_read, 107162306a36Sopenharmony_ci .write = dbgfs_monitor_on_write, 107262306a36Sopenharmony_ci}; 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_cistatic int __init __damon_dbgfs_init(void) 107562306a36Sopenharmony_ci{ 107662306a36Sopenharmony_ci struct dentry *dbgfs_root; 107762306a36Sopenharmony_ci const char * const file_names[] = {"mk_contexts", "rm_contexts", 107862306a36Sopenharmony_ci "monitor_on"}; 107962306a36Sopenharmony_ci const struct file_operations *fops[] = {&mk_contexts_fops, 108062306a36Sopenharmony_ci &rm_contexts_fops, &monitor_on_fops}; 108162306a36Sopenharmony_ci int i; 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci dbgfs_root = debugfs_create_dir("damon", NULL); 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(file_names); i++) 108662306a36Sopenharmony_ci debugfs_create_file(file_names[i], 0600, dbgfs_root, NULL, 108762306a36Sopenharmony_ci fops[i]); 108862306a36Sopenharmony_ci dbgfs_fill_ctx_dir(dbgfs_root, dbgfs_ctxs[0]); 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci dbgfs_dirs = kmalloc(sizeof(dbgfs_root), GFP_KERNEL); 109162306a36Sopenharmony_ci if (!dbgfs_dirs) { 109262306a36Sopenharmony_ci debugfs_remove(dbgfs_root); 109362306a36Sopenharmony_ci return -ENOMEM; 109462306a36Sopenharmony_ci } 109562306a36Sopenharmony_ci dbgfs_dirs[0] = dbgfs_root; 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci return 0; 109862306a36Sopenharmony_ci} 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci/* 110162306a36Sopenharmony_ci * Functions for the initialization 110262306a36Sopenharmony_ci */ 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_cistatic int __init damon_dbgfs_init(void) 110562306a36Sopenharmony_ci{ 110662306a36Sopenharmony_ci int rc = -ENOMEM; 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci mutex_lock(&damon_dbgfs_lock); 110962306a36Sopenharmony_ci dbgfs_ctxs = kmalloc(sizeof(*dbgfs_ctxs), GFP_KERNEL); 111062306a36Sopenharmony_ci if (!dbgfs_ctxs) 111162306a36Sopenharmony_ci goto out; 111262306a36Sopenharmony_ci dbgfs_ctxs[0] = dbgfs_new_ctx(); 111362306a36Sopenharmony_ci if (!dbgfs_ctxs[0]) { 111462306a36Sopenharmony_ci kfree(dbgfs_ctxs); 111562306a36Sopenharmony_ci goto out; 111662306a36Sopenharmony_ci } 111762306a36Sopenharmony_ci dbgfs_nr_ctxs = 1; 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci rc = __damon_dbgfs_init(); 112062306a36Sopenharmony_ci if (rc) { 112162306a36Sopenharmony_ci kfree(dbgfs_ctxs[0]); 112262306a36Sopenharmony_ci kfree(dbgfs_ctxs); 112362306a36Sopenharmony_ci pr_err("%s: dbgfs init failed\n", __func__); 112462306a36Sopenharmony_ci } 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ciout: 112762306a36Sopenharmony_ci mutex_unlock(&damon_dbgfs_lock); 112862306a36Sopenharmony_ci return rc; 112962306a36Sopenharmony_ci} 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_cimodule_init(damon_dbgfs_init); 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci#include "dbgfs-test.h" 1134