2022-02-17 19:35:45

by SeongJae Park

[permalink] [raw]
Subject: [RFC PATCH 0/4] Introduce DAMON sysfs interface

DAMON's debugfs-based user interface served very well, so far. However, it
unnecessarily depends on debugfs, while DAMON is not aimed to be used for only
debugging. Also, the interface receives multiple values via one file. For
example, schemes file receives 18 values. As a result, it is not only hard to
be used, but also difficult to be extended. Especially, keeping backward
compatibility of user space tools is getting only challenging. It would be
better to implement another reliable and flexible interface and deprecate the
debugfs interface in long term.

For the reason, this patchset introduces a sysfs-based new user interface of
DAMON. The idea of the new interface is, using directory hierarchies and
making one file for one value. For a short example, users can do the virtual
address monitoring via the interface as below:

# cd /sys/kernel/mm/damon/admin/
# echo 1 > kdamonds/nr
# echo 1 > kdamonds/0/contexts/nr
# echo vaddr > kdamonds/0/contexts/0/damon_type
# echo 1 > kdamonds/0/contexts/0/targets/nr
# echo $(pidof <workload>) > kdamonds/0/contexts/0/targets/0/pid
# echo on > kdamonds/0/state

Changes that will be made for dropping RFC tag
----------------------------------------------

As the RFC tag means, this is not for merged in the mainline but only for early
comments. Therefore, this version is not implementing all the features of
DAMON debugfs interface but only basic virtual address space monitoring. The
official version of the patchset will provide all the features that DAMON
debugfs interface is providing. Also, this RFC patchset lacks formal
documentation. It will be a part of the official version.

Future plan of DAMON debugfs interface deprecation
--------------------------------------------------

Once the official version of this patchset is merged, DAMON debugfs interface
development will be frozen. That is, we will keep the interface works as is
now, but it will not provide any new feature of DAMON. The support will be
continued only until next LTS release. After that, we will drop DAMON debugfs
interface. Changes to the documentation for explicitly announcing the
deprecation plan would be a part of the official version of this patchset.

Main difference between DAMON_DBGFS and DAMON_SYSFS
---------------------------------------------------

DAMON debugfs interface allows multiple monitoring contexts, but it asks users
to turn those all on and off at once. It's not a big problem but makes the
operation a little bit complex. DAMON_SYSFS allows users to turn on and off
monitoring contexts individually.

The Hierarchy
=============

In a glance, the files hierarchy of the sysfs interface is as below.

/sys/kernel/mm/damon/admin
│ kdamonds
│ │ nr
│ │ 0/
│ │ │ state,pid
│ │ │ contexts
│ │ │ │ nr
│ │ │ │ 0/
│ │ │ │ │ damon_type
│ │ │ │ │ monitoring_attrs/
│ │ │ │ │ │ intervals/sample_us,aggr_us,update_us
│ │ │ │ │ │ nr_regions/min,max
│ │ │ │ │ targets/
│ │ │ │ │ │ nr
│ │ │ │ │ │ 0/
│ │ │ │ │ │ │ pid
│ │ │ │ │ │ │ regions/
│ │ │ │ │ │ │ │ nr
│ │ │ │ │ │ │ │ 0/
│ │ │ │ │ │ │ │ │ start,end
│ │ │ │ │ │ │ │ ...
│ │ │ │ │ │ ...
│ │ │ │ │ ...
│ │ ...

Root
----

The root of the DAMON sysfs is /sys/kernel/mm/damon/, and it has one directory
named 'admin'. The directory contains the interface for privileged user space
programs. User space tools or deamons having root permission could use this
directory. In a future, sibling directories for non-root user space tools or
deamons (we could allow them to do monitong of their own virtual address space)
or control of in-kernel DAMON-based deamons could be created.

kdamonds/
---------

The monitoring-related information including request specifications and results
are called DAMON context. DAMON executes a set of the contexts with a kernel
thread called kdamond (for now, only one context per kdamond is supported), and
multiple kdamonds could run in parallel. This directory has files for
controlling the kdamonds.

In the beginning, this directory has only one file, 'nr'. Writing a number
(`N`) to the file creates the number of child directories named `0` to `N-1`.
Each directory represents each kdamond.

kdamonds/<N>/
-------------

In each kdamond directory, two files (`state` and `pid`) and one directory
(`contexts`) reside.

Reading `state` returns `on` if the kdamond is currently running, or `off` if
it is not running. Writing `on` or `off` makes the kdamond be in the state.
If the state is `on`, reading `pid` shows the pid of the kdamond thread.

`contexts` directory resembles `kdamonds`. It contains files for controlling
the monitoring contexts that this kdamond will execute.

kdamonds/<N>/contexts/
----------------------

In the beginning, this directory has only one file, 'nr'. Writing a number
(`N`) to the file creates the number of child directories named as `0` to
`N-1`. Each directory represents each monitoring context. At the moment, only
one context per kdamond is supported, so only `0` or `1` can be written to the
file.

contexts/<N>/
-------------

In each context directory, one file (`operations`) and two directories
(`monitoring_attrs` and `targets`) reside.

DAMON supports multiple types of monitoring operations, including those for
virtual address space and physical address space. You can set and show what
type of monitoring operations you want to use with the context by writing one
of below keywords to, and reading the file.

- vaddr: Monitor virtual address spaces of specific processes

Files for specifying attributes of the monitoring including required quality
and efficiency of the monitoring are in `monitoring_attrs` directory, while
files for specifying to what memory regions the monitoring should be done are
in `targets` directory.

contexts/<N>/monitoring_attrs/
------------------------------

In this directory, you can show two directories, `intervals` and `nr_regions`.

Under `intervals` directory, three files for DAMON's sampling interval
(`sample_us`), aggregation interval (`aggr_us`) and update interval
(`update_us`) exist. You can set and get the values by writing to and reading
from the files.

Under `nr_regions` directory, two files for the lower-bound and upper-bound of
DAMON's monitoring regions (`min` and `max`, respectively), which controls the
monitoring overhead, reside. You can set and get the values by writing to and
rading from the files.

For more details about the intervals and monitoring regions range, please read
the Design document[1].

[1] https://docs.kernel.org/vm/damon/design.html

contexts/<N>/targets/
---------------------

In the beginning, this directory has only one file, 'nr'. Writing a number
(`N`) to the file creates the number of child directories named `0` to `N-1`.
Each directory represents each monitoring target.

targets/<N>/
------------

In each target directory, one file (`pid`) exists.

You can make the context to monitor the virtual address space of a process by
writing the pid of the process to the file, and show what process's virtual
address space the context is set to monitor by reading the file.

SeongJae Park (4):
mm/damon: Implement a sysfs-based DAMON user interface
mm/damon/core: Allow non-exclusive DAMON start/stop
mm/damon/sysfs: Link DAMON to 'state' file read/write functions
selftests/damon: Add a test for DAMON sysfs interface

include/linux/damon.h | 2 +-
mm/damon/Kconfig | 7 +
mm/damon/Makefile | 1 +
mm/damon/core.c | 22 +-
mm/damon/dbgfs.c | 2 +-
mm/damon/reclaim.c | 2 +-
mm/damon/sysfs.c | 1258 ++++++++++++++++++++++++
tools/testing/selftests/damon/Makefile | 1 +
tools/testing/selftests/damon/sysfs.sh | 200 ++++
9 files changed, 1486 insertions(+), 9 deletions(-)
create mode 100644 mm/damon/sysfs.c
create mode 100755 tools/testing/selftests/damon/sysfs.sh

--
2.17.1


2022-02-17 23:42:09

by SeongJae Park

[permalink] [raw]
Subject: [RFC PATCH 4/4] selftests/damon: Add a test for DAMON sysfs interface

This commit adds a selftest for DAMON sysfs interface. It tests
functionality of 'nr' files and existence of files in each directory of
the hierarchy.

Signed-off-by: SeongJae Park <[email protected]>
---
tools/testing/selftests/damon/Makefile | 1 +
tools/testing/selftests/damon/sysfs.sh | 200 +++++++++++++++++++++++++
2 files changed, 201 insertions(+)
create mode 100755 tools/testing/selftests/damon/sysfs.sh

diff --git a/tools/testing/selftests/damon/Makefile b/tools/testing/selftests/damon/Makefile
index 937d36ae9a69..0470c5f3e690 100644
--- a/tools/testing/selftests/damon/Makefile
+++ b/tools/testing/selftests/damon/Makefile
@@ -6,5 +6,6 @@ TEST_GEN_FILES += huge_count_read_write
TEST_FILES = _chk_dependency.sh _debugfs_common.sh
TEST_PROGS = debugfs_attrs.sh debugfs_schemes.sh debugfs_target_ids.sh
TEST_PROGS += debugfs_empty_targets.sh debugfs_huge_count_read_write.sh
+TEST_PROGS += sysfs.sh

include ../lib.mk
diff --git a/tools/testing/selftests/damon/sysfs.sh b/tools/testing/selftests/damon/sysfs.sh
new file mode 100755
index 000000000000..280fd6fabcd7
--- /dev/null
+++ b/tools/testing/selftests/damon/sysfs.sh
@@ -0,0 +1,200 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+# Kselftest frmework requirement - SKIP code is 4.
+ksft_skip=4
+
+ensure_write_succ()
+{
+ file=$1
+ content=$2
+ reason=$3
+
+ if ! echo "$content" > "$file"
+ then
+ echo "writing $content to $file failed"
+ echo "expected success because $reason"
+ exit 1
+ fi
+}
+
+ensure_write_fail()
+{
+ file=$1
+ content=$2
+ reason=$3
+
+ if echo "$content" > "$file"
+ then
+ echo "writing $content to $file succeed ($fail_reason)"
+ echo "expected failure because $reason"
+ exit 1
+ fi
+}
+
+ensure_dir()
+{
+ dir=$1
+ to_ensure=$2
+ if [ "$to_ensure" = "exist" ] && [ ! -d "$dir" ]
+ then
+ echo "$dir dir is expected but not found"
+ exit 1
+ elif [ "$to_ensure" = "not_exist" ] && [ -d "$dir" ]
+ then
+ echo "$dir dir is not expected but found"
+ exit 1
+ fi
+}
+
+ensure_file()
+{
+ file=$1
+ to_ensure=$2
+ permission=$3
+ if [ "$to_ensure" = "exist" ]
+ then
+ if [ ! -f "$file" ]
+ then
+ echo "$file is expected but not found"
+ exit 1
+ fi
+ perm=$(stat -c "%a" "$file")
+ if [ ! "$perm" = "$permission" ]
+ then
+ echo "$file permission: expected $permission but $perm"
+ exit 1
+ fi
+ elif [ "$to_ensure" = "not_exist" ] && [ -f "$dir" ]
+ then
+ echo "$file is not expected but found"
+ exit 1
+ fi
+}
+
+test_range()
+{
+ range_dir=$1
+ ensure_dir "$range_dir" "exist"
+ ensure_file "$range_dir/min" "exist" 600
+ ensure_file "$range_dir/max" "exist" 600
+}
+
+test_target()
+{
+ target_dir=$1
+ ensure_dir "$target_dir" "exist"
+ ensure_file "$target_dir/pid" "exist" "600"
+}
+
+test_targets()
+{
+ targets_dir=$1
+ ensure_dir "$targets_dir" "exist"
+ ensure_file "$targets_dir/nr" "exist" 600
+
+ ensure_write_succ "$targets_dir/nr" "1" "valid input"
+ test_target "$targets_dir/0"
+
+ ensure_write_succ "$targets_dir/nr" "2" "valid input"
+ test_target "$targets_dir/0"
+ test_target "$targets_dir/1"
+
+ ensure_write_succ "$targets_dir/nr" "0" "valid input"
+ ensure_dir "$targets_dir/0" "not_exist"
+ ensure_dir "$targets_dir/1" "not_exist"
+}
+
+test_intervals()
+{
+ intervals_dir=$1
+ ensure_dir "$intervals_dir" "exist"
+ ensure_file "$intervals_dir/aggr_us" "exist" "600"
+ ensure_file "$intervals_dir/sample_us" "exist" "600"
+ ensure_file "$intervals_dir/update_us" "exist" "600"
+}
+
+test_monitoring_attrs()
+{
+ monitoring_attrs_dir=$1
+ ensure_dir "$monitoring_attrs_dir" "exist"
+ test_intervals "$monitoring_attrs_dir/intervals"
+ test_range "$monitoring_attrs_dir/nr_regions"
+}
+
+test_context()
+{
+ context_dir=$1
+ ensure_dir "$context_dir" "exist"
+ ensure_file "$context_dir/operations" "exist" 600
+ test_monitoring_attrs "$context_dir/monitoring_attrs"
+ test_targets "$context_dir/targets"
+}
+
+test_contexts()
+{
+ contexts_dir=$1
+ ensure_dir "$contexts_dir" "exist"
+ ensure_file "$contexts_dir/nr" "exist" 600
+
+ ensure_write_succ "$contexts_dir/nr" "1" "valid input"
+ test_context "$contexts_dir/0"
+
+ ensure_write_fail "$contexts_dir/nr" "2" "only 0/1 are supported"
+ test_context "$contexts_dir/0"
+
+ ensure_write_succ "$contexts_dir/nr" "0" "valid input"
+ ensure_dir "$contexts_dir/0" "not_exist"
+}
+
+test_kdamond()
+{
+ kdamond_dir=$1
+ ensure_dir "$kdamond_dir" "exist"
+ ensure_file "$kdamond_dir/state" "exist" "600"
+ ensure_file "$kdamond_dir/pid" "exist" 400
+ test_contexts "$kdamond_dir/contexts"
+}
+
+test_kdamonds()
+{
+ kdamonds_dir=$1
+ ensure_dir "$kdamonds_dir" "exist"
+
+ ensure_file "$kdamonds_dir/nr" "exist" "600"
+
+ ensure_write_succ "$kdamonds_dir/nr" "1" "valid input"
+ test_kdamond "$kdamonds_dir/0"
+
+ ensure_write_succ "$kdamonds_dir/nr" "2" "valid input"
+ test_kdamond "$kdamonds_dir/0"
+ test_kdamond "$kdamonds_dir/1"
+
+ ensure_write_succ "$kdamonds_dir/nr" "0" "valid input"
+ ensure_dir "$kdamonds_dir/0" "not_exist"
+ ensure_dir "$kdamonds_dir/1" "not_exist"
+}
+
+test_damon_sysfs()
+{
+ damon_sysfs=$1
+ if [ ! -d "$damon_sysfs" ]
+ then
+ echo "$damon_sysfs not found"
+ exit $ksft_skip
+ fi
+
+ test_kdamonds "$damon_sysfs/kdamonds"
+}
+
+check_dependencies()
+{
+ if [ $EUID -ne 0 ]
+ then
+ echo "Run as root"
+ exit $ksft_skip
+ fi
+}
+
+check_dependencies
+test_damon_sysfs "/sys/kernel/mm/damon/admin"
--
2.17.1

2022-02-17 23:42:28

by SeongJae Park

[permalink] [raw]
Subject: [RFC PATCH 2/4] mm/damon/core: Allow non-exclusive DAMON start/stop

To avoid interference between DAMON users, current start/stop functions
of DAMON allows only exclusive groups. This makes use of DAMON a little
bit restrictive. Meanwhile, admins could somehow aware each DAMON usage
and therefore could address the interference on their own. This commit
hence allows callers of the functions to specify if the contexts should
run in an exclusive mode or not.

Signed-off-by: SeongJae Park <[email protected]>
---
include/linux/damon.h | 2 +-
mm/damon/core.c | 22 ++++++++++++++++------
mm/damon/dbgfs.c | 2 +-
mm/damon/reclaim.c | 2 +-
4 files changed, 19 insertions(+), 9 deletions(-)

diff --git a/include/linux/damon.h b/include/linux/damon.h
index 49c4a11ecf20..f8e99e47d747 100644
--- a/include/linux/damon.h
+++ b/include/linux/damon.h
@@ -508,7 +508,7 @@ int damon_nr_running_ctxs(void);
int damon_register_ops(struct damon_operations *ops);
int damon_select_ops(struct damon_ctx *ctx, enum damon_ops_id id);

-int damon_start(struct damon_ctx **ctxs, int nr_ctxs);
+int damon_start(struct damon_ctx **ctxs, int nr_ctxs, bool exclusive);
int damon_stop(struct damon_ctx **ctxs, int nr_ctxs);

#endif /* CONFIG_DAMON */
diff --git a/mm/damon/core.c b/mm/damon/core.c
index 82e0a4620c4f..08c5e4dd2ed9 100644
--- a/mm/damon/core.c
+++ b/mm/damon/core.c
@@ -24,6 +24,7 @@

static DEFINE_MUTEX(damon_lock);
static int nr_running_ctxs;
+static bool running_exclusive_ctxs;

static DEFINE_MUTEX(damon_ops_lock);
static struct damon_operations damon_registered_ops[NR_DAMON_OPS];
@@ -434,22 +435,25 @@ static int __damon_start(struct damon_ctx *ctx)
* damon_start() - Starts the monitorings for a given group of contexts.
* @ctxs: an array of the pointers for contexts to start monitoring
* @nr_ctxs: size of @ctxs
+ * @exclusive: exclusiveness of this contexts group
*
* This function starts a group of monitoring threads for a group of monitoring
* contexts. One thread per each context is created and run in parallel. The
- * caller should handle synchronization between the threads by itself. If a
- * group of threads that created by other 'damon_start()' call is currently
- * running, this function does nothing but returns -EBUSY.
+ * caller should handle synchronization between the threads by itself. If
+ * @exclusive is true and a group of threads that created by other
+ * 'damon_start()' call is currently running, this function does nothing but
+ * returns -EBUSY.
*
* Return: 0 on success, negative error code otherwise.
*/
-int damon_start(struct damon_ctx **ctxs, int nr_ctxs)
+int damon_start(struct damon_ctx **ctxs, int nr_ctxs, bool exclusive)
{
int i;
int err = 0;

mutex_lock(&damon_lock);
- if (nr_running_ctxs) {
+ if ((exclusive && nr_running_ctxs) ||
+ (!exclusive && running_exclusive_ctxs)) {
mutex_unlock(&damon_lock);
return -EBUSY;
}
@@ -460,13 +464,15 @@ int damon_start(struct damon_ctx **ctxs, int nr_ctxs)
break;
nr_running_ctxs++;
}
+ if (exclusive && nr_running_ctxs)
+ running_exclusive_ctxs = true;
mutex_unlock(&damon_lock);

return err;
}

/*
- * __damon_stop() - Stops monitoring of given context.
+ * __damon_stop() - Stops monitoring of a given context.
* @ctx: monitoring context
*
* Return: 0 on success, negative error code otherwise.
@@ -506,6 +512,10 @@ int damon_stop(struct damon_ctx **ctxs, int nr_ctxs)
if (err)
return err;
}
+ mutex_lock(&damon_lock);
+ if (!nr_running_ctxs && running_exclusive_ctxs)
+ running_exclusive_ctxs = false;
+ mutex_unlock(&damon_lock);

return err;
}
diff --git a/mm/damon/dbgfs.c b/mm/damon/dbgfs.c
index 05b574cbcea8..a0dab8b5e45f 100644
--- a/mm/damon/dbgfs.c
+++ b/mm/damon/dbgfs.c
@@ -967,7 +967,7 @@ static ssize_t dbgfs_monitor_on_write(struct file *file,
return -EINVAL;
}
}
- ret = damon_start(dbgfs_ctxs, dbgfs_nr_ctxs);
+ ret = damon_start(dbgfs_ctxs, dbgfs_nr_ctxs, true);
} else if (!strncmp(kbuf, "off", count)) {
ret = damon_stop(dbgfs_ctxs, dbgfs_nr_ctxs);
} else {
diff --git a/mm/damon/reclaim.c b/mm/damon/reclaim.c
index b53d9c22fad1..e34c4d0c4d93 100644
--- a/mm/damon/reclaim.c
+++ b/mm/damon/reclaim.c
@@ -330,7 +330,7 @@ static int damon_reclaim_turn(bool on)
if (err)
goto free_scheme_out;

- err = damon_start(&ctx, 1);
+ err = damon_start(&ctx, 1, true);
if (!err) {
kdamond_pid = ctx->kdamond->pid;
return 0;
--
2.17.1

2022-02-18 00:24:04

by SeongJae Park

[permalink] [raw]
Subject: [RFC PATCH 3/4] mm/damon/sysfs: Link DAMON to 'state' file read/write functions

This commit implements 'state' DAMON sysfs interface file read/write
functions. In detail, writing 'on' or 'off' to the 'state' file turns
DAMON for the context on or off, accordingly. Reading the file shows
the state.

Signed-off-by: SeongJae Park <[email protected]>
---
mm/damon/sysfs.c | 166 +++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 162 insertions(+), 4 deletions(-)

diff --git a/mm/damon/sysfs.c b/mm/damon/sysfs.c
index 59bdc7268dc6..721ee086265f 100644
--- a/mm/damon/sysfs.c
+++ b/mm/damon/sysfs.c
@@ -793,18 +793,176 @@ static void damon_sysfs_kdamond_rm_dirs(struct damon_sysfs_kdamond *kdamond)
kobject_put(&kdamond->contexts->kobj);
}

+static bool damon_sysfs_ctx_running(struct damon_ctx *ctx)
+{
+ bool running;
+
+ mutex_lock(&ctx->kdamond_lock);
+ running = ctx->kdamond != NULL;
+ mutex_unlock(&ctx->kdamond_lock);
+ return running;
+}
+
static ssize_t damon_sysfs_kdamond_state_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
- /* TODO: Link with DAMON */
- return sysfs_emit(buf, "off\n");
+ struct damon_sysfs_kdamond *kdamond = container_of(kobj,
+ struct damon_sysfs_kdamond, kobj);
+ struct damon_ctx *ctx = kdamond->damon_ctx;
+ bool running;
+
+ if (!ctx)
+ running = false;
+ else
+ running = damon_sysfs_ctx_running(ctx);
+
+ return sysfs_emit(buf, "%s\n", running ? "on" : "off");
+}
+
+static void damon_sysfs_destroy_targets(struct damon_ctx *ctx)
+{
+ struct damon_target *t, *next;
+
+ damon_for_each_target_safe(t, next, ctx) {
+ if (ctx->ops.id == DAMON_OPS_VADDR)
+ put_pid(t->pid);
+ damon_destroy_target(t);
+ }
+}
+
+static int damon_sysfs_set_targets(struct damon_ctx *ctx,
+ struct damon_sysfs_context *sysfs_ctx)
+{
+ struct damon_sysfs_targets *targets = sysfs_ctx->targets;
+ int i;
+
+ for (i = 0; i < targets->nr_targets; i++) {
+ struct damon_target *t;
+
+ t = damon_new_target();
+ if (!t) {
+ damon_sysfs_destroy_targets(ctx);
+ return -ENOMEM;
+ }
+ if (ctx->ops.id == DAMON_OPS_VADDR) {
+ t->pid = find_get_pid(targets->targets_arr[i]->pid);
+ if (!t->pid) {
+ damon_sysfs_destroy_targets(ctx);
+ return -EINVAL;
+ }
+ }
+ damon_add_target(ctx, t);
+ }
+ return 0;
+}
+
+static inline bool target_has_pid(const struct damon_ctx *ctx)
+{
+ return ctx->ops.id == DAMON_OPS_VADDR;
+}
+
+static void damon_sysfs_before_terminate(struct damon_ctx *ctx)
+{
+ struct damon_target *t, *next;
+
+ if (!target_has_pid(ctx))
+ return;
+
+ mutex_lock(&ctx->kdamond_lock);
+ damon_for_each_target_safe(t, next, ctx) {
+ put_pid(t->pid);
+ damon_destroy_target(t);
+ }
+ mutex_unlock(&ctx->kdamond_lock);
+}
+
+static struct damon_ctx *damon_sysfs_build_ctx(
+ struct damon_sysfs_context *sys_ctx)
+{
+ struct damon_ctx *ctx = damon_new_ctx();
+ struct damon_sysfs_attrs *sys_attrs = sys_ctx->attrs;
+ struct damon_sysfs_ul_range *sys_nr_regions = sys_attrs->nr_regions;
+ struct damon_sysfs_intervals *sys_intervals = sys_attrs->intervals;
+ int err;
+
+ if (!ctx)
+ return ERR_PTR(-ENOMEM);
+
+ err = damon_select_ops(ctx, sys_ctx->ops_id);
+ if (err)
+ goto out;
+
+ err = damon_set_attrs(ctx, sys_intervals->sample_us,
+ sys_intervals->aggr_us, sys_intervals->update_us,
+ sys_nr_regions->min, sys_nr_regions->max);
+ if (err)
+ goto out;
+ err = damon_sysfs_set_targets(ctx, sys_ctx);
+ if (err)
+ goto out;
+ ctx->callback.before_terminate = damon_sysfs_before_terminate;
+ return ctx;
+
+out:
+ damon_destroy_ctx(ctx);
+ return ERR_PTR(err);
}

static ssize_t damon_sysfs_kdamond_state_store(struct kobject *kobj,
struct kobj_attribute *attr, const char *buf, size_t count)
{
- /* TODO: Link with DAMON */
- return count;
+ struct damon_sysfs_kdamond *kdamond = container_of(kobj,
+ struct damon_sysfs_kdamond, kobj);
+ struct damon_ctx *ctx;
+ ssize_t ret;
+
+ if (!mutex_trylock(&damon_sysfs_lock))
+ return -EBUSY;
+ if (!strncmp(buf, "on\n", count)) {
+ if (kdamond->damon_ctx &&
+ damon_sysfs_ctx_running(kdamond->damon_ctx)) {
+ ret = -EBUSY;
+ goto out;
+ }
+ if (kdamond->contexts->nr != 1) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (kdamond->damon_ctx)
+ damon_destroy_ctx(kdamond->damon_ctx);
+ kdamond->damon_ctx = NULL;
+
+ ctx = damon_sysfs_build_ctx(
+ kdamond->contexts->contexts_arr[0]);
+ if (IS_ERR(ctx)) {
+ ret = PTR_ERR(ctx);
+ goto out;
+ }
+ ret = damon_start(&ctx, 1, false);
+ if (ret) {
+ damon_destroy_ctx(ctx);
+ goto out;
+ }
+ kdamond->damon_ctx = ctx;
+ } else if (!strncmp(buf, "off\n", count)) {
+ if (!kdamond->damon_ctx) {
+ ret = -EINVAL;
+ goto out;
+ }
+ ret = damon_stop(&kdamond->damon_ctx, 1);
+ /*
+ * kdamond->damon_ctx will be freed in next on, or
+ * kdamonds_nr_store()
+ */
+ } else {
+ ret = -EINVAL;
+ }
+out:
+ mutex_unlock(&damon_sysfs_lock);
+ if (!ret)
+ ret = count;
+ return ret;
}

static ssize_t damon_sysfs_kdamond_pid_show(struct kobject *kobj,
--
2.17.1