mm/damon/dbgfs: support multiple contexts
[linux-2.6-microblaze.git] / 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);