mm/damon/dbgfs: support multiple contexts
authorSeongJae Park <sjpark@amazon.de>
Wed, 8 Sep 2021 02:57:01 +0000 (19:57 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 8 Sep 2021 18:50:25 +0000 (11:50 -0700)
In some use cases, users would want to run multiple monitoring context.
For example, if a user wants a high precision monitoring and dedicating
multiple CPUs for the job is ok, because DAMON creates one monitoring
thread per one context, the user can split the monitoring target regions
into multiple small regions and create one context for each region.  Or,
someone might want to simultaneously monitor different address spaces,
e.g., both virtual address space and physical address space.

The DAMON's API allows such usage, but 'damon-dbgfs' does not.  Therefore,
only kernel space DAMON users can do multiple contexts monitoring.

This commit allows the user space DAMON users to use multiple contexts
monitoring by introducing two new 'damon-dbgfs' debugfs files,
'mk_context' and 'rm_context'.  Users can create a new monitoring context
by writing the desired name of the new context to 'mk_context'.  Then, a
new directory with the name and having the files for setting of the
context ('attrs', 'target_ids' and 'record') will be created under the
debugfs directory.  Writing the name of the context to remove to
'rm_context' will remove the related context and directory.

Link: https://lkml.kernel.org/r/20210716081449.22187-10-sj38.park@gmail.com
Signed-off-by: SeongJae Park <sjpark@amazon.de>
Reviewed-by: Fernand Sieber <sieberf@amazon.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Amit Shah <amit@kernel.org>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Brendan Higgins <brendanhiggins@google.com>
Cc: David Hildenbrand <david@redhat.com>
Cc: David Rientjes <rientjes@google.com>
Cc: David Woodhouse <dwmw@amazon.com>
Cc: Fan Du <fan.du@intel.com>
Cc: Greg Kroah-Hartman <greg@kroah.com>
Cc: Greg Thelen <gthelen@google.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Joe Perches <joe@perches.com>
Cc: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Leonard Foerster <foersleo@amazon.de>
Cc: Marco Elver <elver@google.com>
Cc: Markus Boehme <markubo@amazon.de>
Cc: Maximilian Heyne <mheyne@amazon.de>
Cc: Mel Gorman <mgorman@suse.de>
Cc: Minchan Kim <minchan@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Rik van Riel <riel@surriel.com>
Cc: Shakeel Butt <shakeelb@google.com>
Cc: Shuah Khan <shuah@kernel.org>
Cc: Steven Rostedt (VMware) <rostedt@goodmis.org>
Cc: Vladimir Davydov <vdavydov.dev@gmail.com>
Cc: Vlastimil Babka <vbabka@suse.cz>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
mm/damon/dbgfs.c

index e850be4..31ad550 100644 (file)
@@ -18,6 +18,7 @@
 static struct damon_ctx **dbgfs_ctxs;
 static int dbgfs_nr_ctxs;
 static struct dentry **dbgfs_dirs;
+static DEFINE_MUTEX(damon_dbgfs_lock);
 
 /*
  * Returns non-empty string on success, negative error code otherwise.
@@ -328,6 +329,186 @@ static struct damon_ctx *dbgfs_new_ctx(void)
        return ctx;
 }
 
+static void dbgfs_destroy_ctx(struct damon_ctx *ctx)
+{
+       damon_destroy_ctx(ctx);
+}
+
+/*
+ * Make a context of @name and create a debugfs directory for it.
+ *
+ * This function should be called while holding damon_dbgfs_lock.
+ *
+ * Returns 0 on success, negative error code otherwise.
+ */
+static int dbgfs_mk_context(char *name)
+{
+       struct dentry *root, **new_dirs, *new_dir;
+       struct damon_ctx **new_ctxs, *new_ctx;
+
+       if (damon_nr_running_ctxs())
+               return -EBUSY;
+
+       new_ctxs = krealloc(dbgfs_ctxs, sizeof(*dbgfs_ctxs) *
+                       (dbgfs_nr_ctxs + 1), GFP_KERNEL);
+       if (!new_ctxs)
+               return -ENOMEM;
+       dbgfs_ctxs = new_ctxs;
+
+       new_dirs = krealloc(dbgfs_dirs, sizeof(*dbgfs_dirs) *
+                       (dbgfs_nr_ctxs + 1), GFP_KERNEL);
+       if (!new_dirs)
+               return -ENOMEM;
+       dbgfs_dirs = new_dirs;
+
+       root = dbgfs_dirs[0];
+       if (!root)
+               return -ENOENT;
+
+       new_dir = debugfs_create_dir(name, root);
+       dbgfs_dirs[dbgfs_nr_ctxs] = new_dir;
+
+       new_ctx = dbgfs_new_ctx();
+       if (!new_ctx) {
+               debugfs_remove(new_dir);
+               dbgfs_dirs[dbgfs_nr_ctxs] = NULL;
+               return -ENOMEM;
+       }
+
+       dbgfs_ctxs[dbgfs_nr_ctxs] = new_ctx;
+       dbgfs_fill_ctx_dir(dbgfs_dirs[dbgfs_nr_ctxs],
+                       dbgfs_ctxs[dbgfs_nr_ctxs]);
+       dbgfs_nr_ctxs++;
+
+       return 0;
+}
+
+static ssize_t dbgfs_mk_context_write(struct file *file,
+               const char __user *buf, size_t count, loff_t *ppos)
+{
+       char *kbuf;
+       char *ctx_name;
+       ssize_t ret = count;
+       int err;
+
+       kbuf = user_input_str(buf, count, ppos);
+       if (IS_ERR(kbuf))
+               return PTR_ERR(kbuf);
+       ctx_name = kmalloc(count + 1, GFP_KERNEL);
+       if (!ctx_name) {
+               kfree(kbuf);
+               return -ENOMEM;
+       }
+
+       /* Trim white space */
+       if (sscanf(kbuf, "%s", ctx_name) != 1) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       mutex_lock(&damon_dbgfs_lock);
+       err = dbgfs_mk_context(ctx_name);
+       if (err)
+               ret = err;
+       mutex_unlock(&damon_dbgfs_lock);
+
+out:
+       kfree(kbuf);
+       kfree(ctx_name);
+       return ret;
+}
+
+/*
+ * Remove a context of @name and its debugfs directory.
+ *
+ * This function should be called while holding damon_dbgfs_lock.
+ *
+ * Return 0 on success, negative error code otherwise.
+ */
+static int dbgfs_rm_context(char *name)
+{
+       struct dentry *root, *dir, **new_dirs;
+       struct damon_ctx **new_ctxs;
+       int i, j;
+
+       if (damon_nr_running_ctxs())
+               return -EBUSY;
+
+       root = dbgfs_dirs[0];
+       if (!root)
+               return -ENOENT;
+
+       dir = debugfs_lookup(name, root);
+       if (!dir)
+               return -ENOENT;
+
+       new_dirs = kmalloc_array(dbgfs_nr_ctxs - 1, sizeof(*dbgfs_dirs),
+                       GFP_KERNEL);
+       if (!new_dirs)
+               return -ENOMEM;
+
+       new_ctxs = kmalloc_array(dbgfs_nr_ctxs - 1, sizeof(*dbgfs_ctxs),
+                       GFP_KERNEL);
+       if (!new_ctxs) {
+               kfree(new_dirs);
+               return -ENOMEM;
+       }
+
+       for (i = 0, j = 0; i < dbgfs_nr_ctxs; i++) {
+               if (dbgfs_dirs[i] == dir) {
+                       debugfs_remove(dbgfs_dirs[i]);
+                       dbgfs_destroy_ctx(dbgfs_ctxs[i]);
+                       continue;
+               }
+               new_dirs[j] = dbgfs_dirs[i];
+               new_ctxs[j++] = dbgfs_ctxs[i];
+       }
+
+       kfree(dbgfs_dirs);
+       kfree(dbgfs_ctxs);
+
+       dbgfs_dirs = new_dirs;
+       dbgfs_ctxs = new_ctxs;
+       dbgfs_nr_ctxs--;
+
+       return 0;
+}
+
+static ssize_t dbgfs_rm_context_write(struct file *file,
+               const char __user *buf, size_t count, loff_t *ppos)
+{
+       char *kbuf;
+       ssize_t ret = count;
+       int err;
+       char *ctx_name;
+
+       kbuf = user_input_str(buf, count, ppos);
+       if (IS_ERR(kbuf))
+               return PTR_ERR(kbuf);
+       ctx_name = kmalloc(count + 1, GFP_KERNEL);
+       if (!ctx_name) {
+               kfree(kbuf);
+               return -ENOMEM;
+       }
+
+       /* Trim white space */
+       if (sscanf(kbuf, "%s", ctx_name) != 1) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       mutex_lock(&damon_dbgfs_lock);
+       err = dbgfs_rm_context(ctx_name);
+       if (err)
+               ret = err;
+       mutex_unlock(&damon_dbgfs_lock);
+
+out:
+       kfree(kbuf);
+       kfree(ctx_name);
+       return ret;
+}
+
 static ssize_t dbgfs_monitor_on_read(struct file *file,
                char __user *buf, size_t count, loff_t *ppos)
 {
@@ -370,6 +551,14 @@ static ssize_t dbgfs_monitor_on_write(struct file *file,
        return ret;
 }
 
+static const struct file_operations mk_contexts_fops = {
+       .write = dbgfs_mk_context_write,
+};
+
+static const struct file_operations rm_contexts_fops = {
+       .write = dbgfs_rm_context_write,
+};
+
 static const struct file_operations monitor_on_fops = {
        .read = dbgfs_monitor_on_read,
        .write = dbgfs_monitor_on_write,
@@ -378,8 +567,10 @@ static const struct file_operations monitor_on_fops = {
 static int __init __damon_dbgfs_init(void)
 {
        struct dentry *dbgfs_root;
-       const char * const file_names[] = {"monitor_on"};
-       const struct file_operations *fops[] = {&monitor_on_fops};
+       const char * const file_names[] = {"mk_contexts", "rm_contexts",
+               "monitor_on"};
+       const struct file_operations *fops[] = {&mk_contexts_fops,
+               &rm_contexts_fops, &monitor_on_fops};
        int i;
 
        dbgfs_root = debugfs_create_dir("damon", NULL);