[RFC PATCH 3/3] mm/damon/rules: Implement a debugfs interface
From: sjpark
Date: Mon Feb 10 2020 - 10:10:31 EST
From: SeongJae Park <sjpark@xxxxxxxxx>
This commit implements a debugfs interface for the DAMON's access
pattern based memory management rules. It is supposed to be used by
administrators and privileged user space programs. Users read and
update the rules using ``<debugfs>/damon/rules`` file. The format is::
<min/max size> <min/max access frequency> <min/max duration> <hint>
Signed-off-by: SeongJae Park <sjpark@xxxxxxxxx>
---
mm/damon.c | 157 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 155 insertions(+), 2 deletions(-)
diff --git a/mm/damon.c b/mm/damon.c
index 5d33b5d6504b..efb85bdf9400 100644
--- a/mm/damon.c
+++ b/mm/damon.c
@@ -195,6 +195,29 @@ static void damon_destroy_task(struct damon_task *t)
damon_free_task(t);
}
+static struct damon_rule *damon_new_rule(
+ unsigned int min_sz_region, unsigned int max_sz_region,
+ unsigned int min_nr_accesses, unsigned int max_nr_accesses,
+ unsigned int min_age_region, unsigned int max_age_region,
+ enum damon_action action)
+{
+ struct damon_rule *ret;
+
+ ret = kmalloc(sizeof(struct damon_rule), GFP_KERNEL);
+ if (!ret)
+ return NULL;
+ ret->min_sz_region = min_sz_region;
+ ret->max_sz_region = max_sz_region;
+ ret->min_nr_accesses = min_nr_accesses;
+ ret->max_nr_accesses = max_nr_accesses;
+ ret->min_age_region = min_age_region;
+ ret->max_age_region = max_age_region;
+ ret->action = action;
+ INIT_LIST_HEAD(&ret->list);
+
+ return ret;
+}
+
static void damon_add_rule(struct damon_ctx *ctx, struct damon_rule *r)
{
list_add_tail(&r->list, &ctx->rules_list);
@@ -1266,6 +1289,130 @@ static ssize_t debugfs_monitor_on_write(struct file *file,
return ret;
}
+static ssize_t damon_sprint_rules(struct damon_ctx *c, char *buf, ssize_t len)
+{
+ char *cursor = buf;
+ struct damon_rule *r;
+ int ret;
+
+ damon_for_each_rule(c, r) {
+ ret = snprintf(cursor, len, "%u %u %u %u %u %u %d\n",
+ r->min_sz_region, r->max_sz_region,
+ r->min_nr_accesses, r->max_nr_accesses,
+ r->min_age_region, r->max_age_region,
+ r->action);
+ cursor += ret;
+ }
+ return cursor - buf;
+}
+
+static ssize_t debugfs_rules_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct damon_ctx *ctx = &damon_user_ctx;
+ ssize_t len;
+ char *rules_buf;
+
+ rules_buf = kmalloc(sizeof(char) * 1024, GFP_KERNEL);
+
+ len = damon_sprint_rules(ctx, rules_buf, 1024);
+ len = simple_read_from_buffer(buf, count, ppos, rules_buf, len);
+
+ kfree(rules_buf);
+ return len;
+}
+
+static void damon_free_rules(struct damon_rule **rules, ssize_t nr_rules)
+{
+ ssize_t i;
+
+ for (i = 0; i < nr_rules; i++)
+ kfree(rules[i]);
+ kfree(rules);
+}
+
+/*
+ * Converts a string into an array of struct damon_rule pointers
+ *
+ * Returns an array of struct damon_rule pointers that converted, or NULL
+ * otherwise.
+ */
+static struct damon_rule **str_to_rules(const char *str, ssize_t len,
+ ssize_t *nr_rules)
+{
+ struct damon_rule *rule, **rules;
+ int pos = 0, parsed, ret;
+ unsigned int min_sz, max_sz, min_nr_a, max_nr_a, min_age, max_age;
+ int action;
+
+ rules = kmalloc_array(256, sizeof(struct damon_rule *), GFP_KERNEL);
+ if (!rules)
+ return NULL;
+
+ *nr_rules = 0;
+ while (pos < len && *nr_rules < 256) {
+ ret = sscanf(&str[pos], "%u %u %u %u %u %u %d%n",
+ &min_sz, &max_sz, &min_nr_a, &max_nr_a,
+ &min_age, &max_age, &action, &parsed);
+ pos += parsed;
+ if (ret != 7)
+ break;
+ if (action >= DAMON_ACTION_LEN) {
+ pr_err("wrong action %d\n", action);
+ goto error;
+ }
+
+ rule = damon_new_rule(min_sz, max_sz, min_nr_a, max_nr_a,
+ min_age, max_age, action);
+ if (!rule)
+ goto error;
+
+ rules[*nr_rules] = rule;
+ *nr_rules += 1;
+ }
+ return rules;
+error:
+ damon_free_rules(rules, *nr_rules);
+ return NULL;
+}
+
+static ssize_t debugfs_rules_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct damon_ctx *ctx = &damon_user_ctx;
+ char *rules_buf;
+ struct damon_rule **rules;
+ ssize_t nr_rules, ret;
+
+ rules_buf = kmalloc(sizeof(char) * 1024, GFP_KERNEL);
+ ret = simple_write_to_buffer(rules_buf, 1024, ppos, buf, count);
+ if (ret < 0) {
+ kfree(rules_buf);
+ return ret;
+ }
+
+ rules = str_to_rules(rules_buf, ret, &nr_rules);
+ if (!rules)
+ return -EINVAL;
+
+ spin_lock(&ctx->kdamond_lock);
+ if (ctx->kdamond)
+ goto monitor_running;
+
+ damon_set_rules(ctx, rules, nr_rules);
+ spin_unlock(&ctx->kdamond_lock);
+ kfree(rules_buf);
+ return ret;
+
+monitor_running:
+ spin_unlock(&ctx->kdamond_lock);
+ pr_err("%s: kdamond is running. Turn it off first.\n", __func__);
+ ret = -EINVAL;
+ damon_free_rules(rules, nr_rules);
+ kfree(rules_buf);
+ return ret;
+}
+
static ssize_t damon_sprint_pids(struct damon_ctx *ctx, char *buf, ssize_t len)
{
char *cursor = buf;
@@ -1468,6 +1615,12 @@ static const struct file_operations pids_fops = {
.write = debugfs_pids_write,
};
+static const struct file_operations rules_fops = {
+ .owner = THIS_MODULE,
+ .read = debugfs_rules_read,
+ .write = debugfs_rules_write,
+};
+
static const struct file_operations record_fops = {
.owner = THIS_MODULE,
.read = debugfs_record_read,
@@ -1484,10 +1637,10 @@ static struct dentry *debugfs_root;
static int __init debugfs_init(void)
{
- const char * const file_names[] = {"attrs", "record",
+ const char * const file_names[] = {"attrs", "record", "rules",
"pids", "monitor_on"};
const struct file_operations *fops[] = {&attrs_fops, &record_fops,
- &pids_fops, &monitor_on_fops};
+ &rules_fops, &pids_fops, &monitor_on_fops};
int i;
debugfs_root = debugfs_create_dir("damon", NULL);
--
2.17.1