linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 00/12] Introduce DAMON sysfs interface
@ 2022-02-23 15:20 SeongJae Park
  2022-02-23 15:20 ` [PATCH 01/12] mm/damon/core: Allow non-exclusive DAMON start/stop SeongJae Park
                   ` (13 more replies)
  0 siblings, 14 replies; 24+ messages in thread
From: SeongJae Park @ 2022-02-23 15:20 UTC (permalink / raw)
  To: akpm
  Cc: corbet, skhan, rientjes, xhao, linux-damon, linux-mm, linux-doc,
	linux-kselftest, linux-kernel, SeongJae Park

Chages from Previous Version (RFC)
==================================

Compared to the RFC version of this patchset
(https://lore.kernel.org/linux-mm/20220217161938.8874-1-sj@kernel.org/), this
version contains below changes.

- Implement all DAMON debugfs interface providing features
- Writeup documents
- Add more selftests

Introduction
============

DAMON's debugfs-based user interface (DAMON_DBGFS) 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
inefficient, hard to be used, and 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 DAMON_DBGFS 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
having one dedicated file for each 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/operations
    # echo 1 > kdamonds/0/contexts/0/targets/nr
    # echo $(pidof <workload>) > kdamonds/0/contexts/0/targets/0/pid
    # echo on > kdamonds/0/state

A brief representation of the files hierarchy of DAMON sysfs interface is as
below.  Childs are represented with indentation, directories are having '/'
suffix, and files in each directory are separated by comma.

    /sys/kernel/mm/damon/admin
    │ kdamonds/nr
    │ │ 0/state,pid
    │ │ │ contexts/nr
    │ │ │ │ 0/operations
    │ │ │ │ │ monitoring_attrs/
    │ │ │ │ │ │ intervals/sample_us,aggr_us,update_us
    │ │ │ │ │ │ nr_regions/min,max
    │ │ │ │ │ targets/nr
    │ │ │ │ │ │ 0/pid
    │ │ │ │ │ │ │ regions/nr
    │ │ │ │ │ │ │ │ 0/start,end
    │ │ │ │ │ │ │ │ ...
    │ │ │ │ │ │ ...
    │ │ │ │ │ schemes/nr
    │ │ │ │ │ 0/action
    │ │ │ │ │ │ access_pattern/
    │ │ │ │ │ │ │ sz/min,max
    │ │ │ │ │ │ │ nr_accesses/min,max
    │ │ │ │ │ │ │ age/min,max
    │ │ │ │ │ │ quotas/ms,sz,reset_interval_ms
    │ │ │ │ │ │ │ weights/sz,nr_accesses,age
    │ │ │ │ │ │ watermarks/metric,interval_us,high,mid,low
    │ │ │ │ │ │ stats/nr_tried,sz_tried,nr_applied,sz_applied,qt_exceeds
    │ │ │ │ │ ...
    │ │ ...

Detailed usage of the files will be described in the final Documentation patch
of this patchset.

Main Difference Between DAMON_DBGFS and DAMON_SYSFS
---------------------------------------------------

At the moment, DAMON_DBGFS and DAMON_SYSFS provides same features.  One
important difference between them is their exclusiveness.  DAMON_DBGFS works in
an exclusive manner, so that no DAMON worker thread (kdamond) in the system can
run concurrently and interfere somehow.  For the reason, DAMON_DBGFS asks users
to construct all monitoring contexts and start them at once.  It's not a big
problem but makes the operation a little bit complex and unflexible.

For more flexible usage, DAMON_SYSFS moves the responsibility of preventing any
possible interference to the admins and work in a non-exclusive manner.  That
is, users can configure and start contexts one by one.  Note that DAMON
respects both exclusive groups and non-exclusive groups of contexts, in a
manner similar to that of reader-writer locks.  That is, if any exclusive
monitoring contexts (e.g., contexts that started via DAMON_DBGFS) are running,
DAMON_SYSFS does not start new contexts, and vice versa.

Future Plan of DAMON_DBGFS Deprecation
======================================

Once this patchset is merged, DAMON_DBGFS development will be frozen.  That is,
we will maintain it to work as is now so that no users will be break.  But, it
will not be extended to provide any new feature of DAMON.  The support will be
continued only until next LTS release.  After that, we will drop DAMON_DBGFS.

User-space Tooling Compatibility
--------------------------------

As DAMON_SYSFS provides all features of DAMON_DBGFS, all user space tooling can
move to DAMON_SYSFS.  As we will continue supporting DAMON_DBGFS until next LTS
kernel release, user space tools would have enough time to move to DAMON_SYSFS.

The official user space tool, damo[1], is already supporting both DAMON_SYSFS
and DAMON_DBGFS.  Both correctness tests[2] and performance tests[3] of DAMON
using DAMON_SYSFS also passed.

[1] https://github.com/awslabs/damo
[2] https://github.com/awslabs/damon-tests/tree/master/corr
[3] https://github.com/awslabs/damon-tests/tree/master/perf

Complete Git Tree
=================

You can get the complete git tree from
https://git.kernel.org/sj/h/damon/sysfs/patches/v1.

Sequence of Patches
===================

First two patches (patches 1-2) make core changes for DAMON_SYSFS.  The first
one (patch 1) allows non-exclusive DAMON contexts so that DAMON_SYSFS can work
in non-exclusive mode, while the second one (patch 2) adds size of DAMON enum
types so that DAMON API users can safely iterate the enums.

Third patch (patch 3) implements basic sysfs stub for virtual address spaces
monitoring.  Note that this implements only sysfs files and DAMON is not
linked.  Fourth patch (patch 4) links the DAMON_SYSFS to DAMON so that users
can control DAMON using the sysfs files.

Following six patches (patches 5-10) implements other DAMON features that
DAMON_DBGFS supports one by one (physical address space monitoring, DAMON-based
operation schemes, schemes quotas, schemes prioritization weights, schemes
watermarks, and schemes stats).

Following patch (patch 11) adds a simple selftest for DAMON_SYSFS, and the
final one (patch 12) documents DAMON_SYSFS.

SeongJae Park (12):
  mm/damon/core: Allow non-exclusive DAMON start/stop
  mm/damon/core: Add number of each enum type values
  mm/damon: Implement a minimal stub for sysfs-based DAMON interface
  mm/damon/sysfs: Link DAMON for virtual address spaces monitoring
  mm/damon/sysfs: Support physical address space monitoring
  mm/damon/sysfs: Support DAMON-based Operation Schemes
  mm/damon/sysfs: Support DAMOS quotas
  mm/damon/sysfs: Support schemes prioritization weights
  mm/damon/sysfs: Support DAMOS watermarks
  mm/damon/sysfs: Support DAMOS stats
  selftests/damon: Add a test for DAMON sysfs interface
  Docs/admin-guide/mm/damon/usage: Document DAMON sysfs interface

 Documentation/admin-guide/mm/damon/usage.rst |  349 ++-
 include/linux/damon.h                        |    6 +-
 mm/damon/Kconfig                             |    7 +
 mm/damon/Makefile                            |    1 +
 mm/damon/core.c                              |   23 +-
 mm/damon/dbgfs.c                             |    2 +-
 mm/damon/reclaim.c                           |    2 +-
 mm/damon/sysfs.c                             | 2684 ++++++++++++++++++
 tools/testing/selftests/damon/Makefile       |    1 +
 tools/testing/selftests/damon/sysfs.sh       |  306 ++
 10 files changed, 3364 insertions(+), 17 deletions(-)
 create mode 100644 mm/damon/sysfs.c
 create mode 100755 tools/testing/selftests/damon/sysfs.sh

-- 
2.17.1


^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH 01/12] mm/damon/core: Allow non-exclusive DAMON start/stop
  2022-02-23 15:20 [PATCH 00/12] Introduce DAMON sysfs interface SeongJae Park
@ 2022-02-23 15:20 ` SeongJae Park
  2022-02-23 15:20 ` [PATCH 02/12] mm/damon/core: Add number of each enum type values SeongJae Park
                   ` (12 subsequent siblings)
  13 siblings, 0 replies; 24+ messages in thread
From: SeongJae Park @ 2022-02-23 15:20 UTC (permalink / raw)
  To: akpm
  Cc: corbet, skhan, rientjes, xhao, linux-damon, linux-mm, linux-doc,
	linux-kselftest, linux-kernel, SeongJae Park

To avoid interference between DAMON contexts monitoring overlapping
memory regions, damon_start() works in an exclusive manner.  That is,
damon_start() does nothing bug fails if any context that started by
another instance of the function is still running.   This makes its
usage a little bit restrictive.  However, admins could aware each DAMON
usage and address such interferences on their own in some cases.

This commit hence implements non-exclusive mode of the function and
allows the callers to select the mode.  Note that the exclusive groups
and non-exclusive groups of contexts will respect each other in a manner
similar to that of reader-writer locks.  Therefore, this commit will not
cause any behavioral change to the exclusive groups.

Signed-off-by: SeongJae Park <sj@kernel.org>
---
 include/linux/damon.h |  2 +-
 mm/damon/core.c       | 23 +++++++++++++++--------
 mm/damon/dbgfs.c      |  2 +-
 mm/damon/reclaim.c    |  2 +-
 4 files changed, 18 insertions(+), 11 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..c1e0fed4e877 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.
@@ -504,9 +510,8 @@ int damon_stop(struct damon_ctx **ctxs, int nr_ctxs)
 		/* nr_running_ctxs is decremented in kdamond_fn */
 		err = __damon_stop(ctxs[i]);
 		if (err)
-			return err;
+			break;
 	}
-
 	return err;
 }
 
@@ -1102,6 +1107,8 @@ static int kdamond_fn(void *data)
 
 	mutex_lock(&damon_lock);
 	nr_running_ctxs--;
+	if (!nr_running_ctxs && running_exclusive_ctxs)
+		running_exclusive_ctxs = false;
 	mutex_unlock(&damon_lock);
 
 	return 0;
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


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH 02/12] mm/damon/core: Add number of each enum type values
  2022-02-23 15:20 [PATCH 00/12] Introduce DAMON sysfs interface SeongJae Park
  2022-02-23 15:20 ` [PATCH 01/12] mm/damon/core: Allow non-exclusive DAMON start/stop SeongJae Park
@ 2022-02-23 15:20 ` SeongJae Park
  2022-02-23 15:20 ` [PATCH 03/12] mm/damon: Implement a minimal stub for sysfs-based DAMON interface SeongJae Park
                   ` (11 subsequent siblings)
  13 siblings, 0 replies; 24+ messages in thread
From: SeongJae Park @ 2022-02-23 15:20 UTC (permalink / raw)
  To: akpm
  Cc: corbet, skhan, rientjes, xhao, linux-damon, linux-mm, linux-doc,
	linux-kselftest, linux-kernel, SeongJae Park

This commit declares the number of legal values for each DAMON enum
types to make traversals of such DAMON enum types easy and safe.

Signed-off-by: SeongJae Park <sj@kernel.org>
---
 include/linux/damon.h | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/include/linux/damon.h b/include/linux/damon.h
index f8e99e47d747..f23cbfa4248d 100644
--- a/include/linux/damon.h
+++ b/include/linux/damon.h
@@ -87,6 +87,7 @@ struct damon_target {
  * @DAMOS_HUGEPAGE:	Call ``madvise()`` for the region with MADV_HUGEPAGE.
  * @DAMOS_NOHUGEPAGE:	Call ``madvise()`` for the region with MADV_NOHUGEPAGE.
  * @DAMOS_STAT:		Do nothing but count the stat.
+ * @NR_DAMOS_ACTIONS:	Total number of DAMOS actions
  */
 enum damos_action {
 	DAMOS_WILLNEED,
@@ -95,6 +96,7 @@ enum damos_action {
 	DAMOS_HUGEPAGE,
 	DAMOS_NOHUGEPAGE,
 	DAMOS_STAT,		/* Do nothing but only record the stat */
+	NR_DAMOS_ACTIONS,
 };
 
 /**
@@ -157,10 +159,12 @@ struct damos_quota {
  *
  * @DAMOS_WMARK_NONE:		Ignore the watermarks of the given scheme.
  * @DAMOS_WMARK_FREE_MEM_RATE:	Free memory rate of the system in [0,1000].
+ * @NR_DAMOS_WMARK_METRICS:	Total number of DAMOS watermark metrics
  */
 enum damos_wmark_metric {
 	DAMOS_WMARK_NONE,
 	DAMOS_WMARK_FREE_MEM_RATE,
+	NR_DAMOS_WMARK_METRICS,
 };
 
 /**
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH 03/12] mm/damon: Implement a minimal stub for sysfs-based DAMON interface
  2022-02-23 15:20 [PATCH 00/12] Introduce DAMON sysfs interface SeongJae Park
  2022-02-23 15:20 ` [PATCH 01/12] mm/damon/core: Allow non-exclusive DAMON start/stop SeongJae Park
  2022-02-23 15:20 ` [PATCH 02/12] mm/damon/core: Add number of each enum type values SeongJae Park
@ 2022-02-23 15:20 ` SeongJae Park
  2022-02-23 16:09   ` Greg KH
  2022-02-25  7:21   ` xhao
  2022-02-23 15:20 ` [PATCH 04/12] mm/damon/sysfs: Link DAMON for virtual address spaces monitoring SeongJae Park
                   ` (10 subsequent siblings)
  13 siblings, 2 replies; 24+ messages in thread
From: SeongJae Park @ 2022-02-23 15:20 UTC (permalink / raw)
  To: akpm
  Cc: corbet, skhan, rientjes, xhao, linux-damon, linux-mm, linux-doc,
	linux-kselftest, linux-kernel, SeongJae Park

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 separated by
white spaces.  As a result, it is ineffient, hard to be used, and
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.

To this end, this commit implements a stub of a part of the new user
interface of DAMON using sysfs.  Specifically, this commit implements
the sysfs control parts for virtual address space monitoring.

More specifically, the idea of the new interface is, using directory
hierarchies and making one file for one value.  The hierarchy that this
commit is introducing is as below.  In the below figure,
parents-children relations are represented with indentations, each
directory is having ``/`` suffix, and files in each directory are
separated by comma (",").

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

Writing a number <N> to each 'nr' file makes directories of name <0> to
<N-1> in the directory of the 'nr' file.  That's all this commit does.
Writing proper values to relevant files will construct the DAMON
contexts, and writing a special keyword, 'on', to 'state' files for each
kdamond will ask DAMON to start the constructed contexts.

For a short example, using below commands for
monitoring virtual address spaces of a given workload is imaginable:

    # 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

Please note that this commit is implementing only the sysfs part stub as
abovely mentioned.  This commit doesn't implement the special keywords
for 'state' files.  Following commits will do that.

Signed-off-by: SeongJae Park <sj@kernel.org>
---
 mm/damon/Kconfig  |    7 +
 mm/damon/Makefile |    1 +
 mm/damon/sysfs.c  | 1059 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1067 insertions(+)
 create mode 100644 mm/damon/sysfs.c

diff --git a/mm/damon/Kconfig b/mm/damon/Kconfig
index 01bad77ad7ae..9b559c76d6dd 100644
--- a/mm/damon/Kconfig
+++ b/mm/damon/Kconfig
@@ -52,6 +52,13 @@ config DAMON_VADDR_KUNIT_TEST
 
 	  If unsure, say N.
 
+config DAMON_SYSFS
+	bool "DAMON sysfs interface"
+	depends on DAMON && SYSFS
+	help
+	  This builds the sysfs interface for DAMON.  The user space can use
+	  the interface for arbitrary data access monitoring.
+
 config DAMON_DBGFS
 	bool "DAMON debugfs interface"
 	depends on DAMON_VADDR && DAMON_PADDR && DEBUG_FS
diff --git a/mm/damon/Makefile b/mm/damon/Makefile
index aebbf6c14c51..dbf7190b4144 100644
--- a/mm/damon/Makefile
+++ b/mm/damon/Makefile
@@ -3,5 +3,6 @@
 obj-y				:= core.o
 obj-$(CONFIG_DAMON_VADDR)	+= ops-common.o vaddr.o
 obj-$(CONFIG_DAMON_PADDR)	+= ops-common.o paddr.o
+obj-$(CONFIG_DAMON_SYSFS)	+= sysfs.o
 obj-$(CONFIG_DAMON_DBGFS)	+= dbgfs.o
 obj-$(CONFIG_DAMON_RECLAIM)	+= reclaim.o
diff --git a/mm/damon/sysfs.c b/mm/damon/sysfs.c
new file mode 100644
index 000000000000..e8087ffd924c
--- /dev/null
+++ b/mm/damon/sysfs.c
@@ -0,0 +1,1059 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * DAMON sysfs Interface
+ *
+ * Copyright (c) 2022 SeongJae Park <sj@kernel.org>
+ */
+
+#include <linux/damon.h>
+#include <linux/kobject.h>
+#include <linux/pid.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+static DEFINE_MUTEX(damon_sysfs_lock);
+
+/*
+ * unsigned long range directory
+ */
+
+struct damon_sysfs_ul_range {
+	struct kobject kobj;
+	unsigned long min;
+	unsigned long max;
+};
+
+static struct damon_sysfs_ul_range *damon_sysfs_ul_range_alloc(
+		unsigned long min,
+		unsigned long max)
+{
+	struct damon_sysfs_ul_range *range = kmalloc(sizeof(*range),
+			GFP_KERNEL);
+
+	if (!range)
+		return NULL;
+	range->kobj = (struct kobject){};
+	range->min = min;
+	range->max = max;
+
+	return range;
+}
+
+static ssize_t damon_sysfs_ul_range_min_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	struct damon_sysfs_ul_range *range = container_of(kobj,
+			struct damon_sysfs_ul_range, kobj);
+
+	return sysfs_emit(buf, "%lu\n", range->min);
+}
+
+static ssize_t damon_sysfs_ul_range_min_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	struct damon_sysfs_ul_range *range = container_of(kobj,
+			struct damon_sysfs_ul_range, kobj);
+	unsigned long min;
+	int err;
+
+	err = kstrtoul(buf, 0, &min);
+	if (err)
+		return -EINVAL;
+
+	range->min = min;
+	return count;
+}
+
+static ssize_t damon_sysfs_ul_range_max_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	struct damon_sysfs_ul_range *range = container_of(kobj,
+			struct damon_sysfs_ul_range, kobj);
+
+	return sysfs_emit(buf, "%lu\n", range->max);
+}
+
+static ssize_t damon_sysfs_ul_range_max_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	struct damon_sysfs_ul_range *range = container_of(kobj,
+			struct damon_sysfs_ul_range, kobj);
+	unsigned long max;
+	int err;
+
+	err = kstrtoul(buf, 0, &max);
+	if (err)
+		return -EINVAL;
+
+	range->max = max;
+	return count;
+}
+
+static void damon_sysfs_ul_range_release(struct kobject *kobj)
+{
+	kfree(container_of(kobj, struct damon_sysfs_ul_range, kobj));
+}
+
+static struct kobj_attribute damon_sysfs_ul_range_min_attr =
+		__ATTR(min, 0600, damon_sysfs_ul_range_min_show,
+				damon_sysfs_ul_range_min_store);
+
+static struct kobj_attribute damon_sysfs_ul_range_max_attr =
+		__ATTR(max, 0600, damon_sysfs_ul_range_max_show,
+				damon_sysfs_ul_range_max_store);
+
+static struct attribute *damon_sysfs_ul_range_attrs[] = {
+	&damon_sysfs_ul_range_min_attr.attr,
+	&damon_sysfs_ul_range_max_attr.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(damon_sysfs_ul_range);
+
+static struct kobj_type damon_sysfs_ul_range_ktype = {
+	.release = damon_sysfs_ul_range_release,
+	.sysfs_ops = &kobj_sysfs_ops,
+	.default_groups = damon_sysfs_ul_range_groups,
+};
+
+/*
+ * target directory
+ */
+
+struct damon_sysfs_target {
+	struct kobject kobj;
+	int pid;
+};
+
+static struct damon_sysfs_target *damon_sysfs_target_alloc(void)
+{
+	return kzalloc(sizeof(struct damon_sysfs_target), GFP_KERNEL);
+}
+
+static ssize_t damon_sysfs_target_pid_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	struct damon_sysfs_target *target = container_of(kobj,
+			struct damon_sysfs_target, kobj);
+
+	return sysfs_emit(buf, "%d\n", target->pid);
+}
+
+static ssize_t damon_sysfs_target_pid_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	struct damon_sysfs_target *target = container_of(kobj,
+			struct damon_sysfs_target, kobj);
+	int err = kstrtoint(buf, 0, &target->pid);
+
+	if (err)
+		return -EINVAL;
+	return count;
+}
+
+static void damon_sysfs_target_release(struct kobject *kobj)
+{
+	kfree(container_of(kobj, struct damon_sysfs_target, kobj));
+}
+
+static struct kobj_attribute damon_sysfs_target_pid_attr = __ATTR(pid, 0600,
+		damon_sysfs_target_pid_show, damon_sysfs_target_pid_store);
+
+static struct attribute *damon_sysfs_target_attrs[] = {
+	&damon_sysfs_target_pid_attr.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(damon_sysfs_target);
+
+static struct kobj_type damon_sysfs_target_ktype = {
+	.release = damon_sysfs_target_release,
+	.sysfs_ops = &kobj_sysfs_ops,
+	.default_groups = damon_sysfs_target_groups,
+};
+
+/*
+ * targets directory
+ */
+
+struct damon_sysfs_targets {
+	struct kobject kobj;
+	struct damon_sysfs_target **targets_arr;
+	int nr;
+};
+
+static struct damon_sysfs_targets *damon_sysfs_targets_alloc(void)
+{
+	return kzalloc(sizeof(struct damon_sysfs_targets), GFP_KERNEL);
+}
+
+static void damon_sysfs_targets_rm_dirs(struct damon_sysfs_targets *targets)
+{
+	struct damon_sysfs_target **targets_arr = targets->targets_arr;
+	int i;
+
+	for (i = 0; i < targets->nr; i++)
+		kobject_put(&targets_arr[i]->kobj);
+	targets->nr = 0;
+	kfree(targets_arr);
+	targets->targets_arr = NULL;
+}
+
+static int damon_sysfs_targets_add_dirs(struct damon_sysfs_targets *targets,
+		int nr_targets)
+{
+	struct damon_sysfs_target **targets_arr, *target;
+	int err, i;
+
+	damon_sysfs_targets_rm_dirs(targets);
+	if (!nr_targets)
+		return 0;
+
+	targets_arr = kmalloc_array(nr_targets, sizeof(*targets_arr),
+			GFP_KERNEL | __GFP_NOWARN);
+	if (!targets_arr)
+		return -ENOMEM;
+	targets->targets_arr = targets_arr;
+
+	for (i = 0; i < nr_targets; i++) {
+		target = damon_sysfs_target_alloc();
+		if (!target) {
+			damon_sysfs_targets_rm_dirs(targets);
+			return -ENOMEM;
+		}
+
+		err = kobject_init_and_add(&target->kobj,
+				&damon_sysfs_target_ktype, &targets->kobj,
+				"%d", i);
+		if (err)
+			goto out;
+
+		targets_arr[i] = target;
+		targets->nr++;
+	}
+	return 0;
+
+out:
+	damon_sysfs_targets_rm_dirs(targets);
+	kobject_put(&target->kobj);
+	return err;
+}
+
+static ssize_t damon_sysfs_targets_nr_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	struct damon_sysfs_targets *targets = container_of(kobj,
+			struct damon_sysfs_targets, kobj);
+
+	return sysfs_emit(buf, "%d\n", targets->nr);
+}
+
+static ssize_t damon_sysfs_targets_nr_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	struct damon_sysfs_targets *targets = container_of(kobj,
+			struct damon_sysfs_targets, kobj);
+	int nr, err = kstrtoint(buf, 0, &nr);
+
+	if (err)
+		return err;
+	if (nr < 0)
+		return -EINVAL;
+
+	if (!mutex_trylock(&damon_sysfs_lock))
+		return -EBUSY;
+	err = damon_sysfs_targets_add_dirs(targets, nr);
+	mutex_unlock(&damon_sysfs_lock);
+	if (err)
+		return err;
+
+	return count;
+}
+
+static void damon_sysfs_targets_release(struct kobject *kobj)
+{
+	kfree(container_of(kobj, struct damon_sysfs_targets, kobj));
+}
+
+static struct kobj_attribute damon_sysfs_targets_nr_attr = __ATTR(nr, 0600,
+		damon_sysfs_targets_nr_show, damon_sysfs_targets_nr_store);
+
+static struct attribute *damon_sysfs_targets_attrs[] = {
+	&damon_sysfs_targets_nr_attr.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(damon_sysfs_targets);
+
+static struct kobj_type damon_sysfs_targets_ktype = {
+	.release = damon_sysfs_targets_release,
+	.sysfs_ops = &kobj_sysfs_ops,
+	.default_groups = damon_sysfs_targets_groups,
+};
+
+/*
+ * intervals directory
+ */
+
+struct damon_sysfs_intervals {
+	struct kobject kobj;
+	unsigned long sample_us;
+	unsigned long aggr_us;
+	unsigned long update_us;
+};
+
+static struct damon_sysfs_intervals *damon_sysfs_intervals_alloc(
+		unsigned long sample_us, unsigned long aggr_us,
+		unsigned long update_us)
+{
+	struct damon_sysfs_intervals *intervals = kmalloc(sizeof(*intervals),
+			GFP_KERNEL);
+
+	if (!intervals)
+		return NULL;
+
+	intervals->kobj = (struct kobject){};
+	intervals->sample_us = sample_us;
+	intervals->aggr_us = aggr_us;
+	intervals->update_us = update_us;
+	return intervals;
+}
+
+static ssize_t damon_sysfs_intervals_sample_us_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	struct damon_sysfs_intervals *intervals = container_of(kobj,
+			struct damon_sysfs_intervals, kobj);
+
+	return sysfs_emit(buf, "%lu\n", intervals->sample_us);
+}
+
+static ssize_t damon_sysfs_intervals_sample_us_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	struct damon_sysfs_intervals *intervals = container_of(kobj,
+			struct damon_sysfs_intervals, kobj);
+	unsigned long us;
+	int err = kstrtoul(buf, 0, &us);
+
+	if (err)
+		return -EINVAL;
+
+	intervals->sample_us = us;
+	return count;
+}
+
+static ssize_t damon_sysfs_intervals_aggr_us_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	struct damon_sysfs_intervals *intervals = container_of(kobj,
+			struct damon_sysfs_intervals, kobj);
+
+	return sysfs_emit(buf, "%lu\n", intervals->aggr_us);
+}
+
+static ssize_t damon_sysfs_intervals_aggr_us_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	struct damon_sysfs_intervals *intervals = container_of(kobj,
+			struct damon_sysfs_intervals, kobj);
+	unsigned long us;
+	int err = kstrtoul(buf, 0, &us);
+
+	if (err)
+		return -EINVAL;
+
+	intervals->aggr_us = us;
+	return count;
+}
+
+static ssize_t damon_sysfs_intervals_update_us_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	struct damon_sysfs_intervals *intervals = container_of(kobj,
+			struct damon_sysfs_intervals, kobj);
+
+	return sysfs_emit(buf, "%lu\n", intervals->update_us);
+}
+
+static ssize_t damon_sysfs_intervals_update_us_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	struct damon_sysfs_intervals *intervals = container_of(kobj,
+			struct damon_sysfs_intervals, kobj);
+	unsigned long us;
+	int err = kstrtoul(buf, 0, &us);
+
+	if (err)
+		return -EINVAL;
+
+	intervals->update_us = us;
+	return count;
+}
+
+static void damon_sysfs_intervals_release(struct kobject *kobj)
+{
+	kfree(container_of(kobj, struct damon_sysfs_intervals, kobj));
+}
+
+static struct kobj_attribute damon_sysfs_intervals_sample_us_attr =
+		__ATTR(sample_us, 0600,
+				damon_sysfs_intervals_sample_us_show,
+				damon_sysfs_intervals_sample_us_store);
+
+static struct kobj_attribute damon_sysfs_intervals_aggr_us_attr =
+		__ATTR(aggr_us, 0600,
+				damon_sysfs_intervals_aggr_us_show,
+				damon_sysfs_intervals_aggr_us_store);
+
+static struct kobj_attribute damon_sysfs_intervals_update_us_attr =
+		__ATTR(update_us, 0600,
+				damon_sysfs_intervals_update_us_show,
+				damon_sysfs_intervals_update_us_store);
+
+static struct attribute *damon_sysfs_intervals_attrs[] = {
+	&damon_sysfs_intervals_sample_us_attr.attr,
+	&damon_sysfs_intervals_aggr_us_attr.attr,
+	&damon_sysfs_intervals_update_us_attr.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(damon_sysfs_intervals);
+
+static struct kobj_type damon_sysfs_intervals_ktype = {
+	.release = damon_sysfs_intervals_release,
+	.sysfs_ops = &kobj_sysfs_ops,
+	.default_groups = damon_sysfs_intervals_groups,
+};
+
+/*
+ * monitoring_attrs directory
+ */
+
+struct damon_sysfs_attrs {
+	struct kobject kobj;
+	struct damon_sysfs_intervals *intervals;
+	struct damon_sysfs_ul_range *nr_regions_range;
+};
+
+static struct damon_sysfs_attrs *damon_sysfs_attrs_alloc(void)
+{
+	struct damon_sysfs_attrs *attrs = kmalloc(sizeof(*attrs), GFP_KERNEL);
+
+	if (!attrs)
+		return NULL;
+	attrs->kobj = (struct kobject){};
+	return attrs;
+}
+
+static int damon_sysfs_attrs_add_dirs(struct damon_sysfs_attrs *attrs)
+{
+	struct damon_sysfs_intervals *intervals;
+	struct damon_sysfs_ul_range *nr_regions_range;
+	int err;
+
+	intervals = damon_sysfs_intervals_alloc(5000, 100000, 60000000);
+	if (!intervals)
+		return -ENOMEM;
+
+	err = kobject_init_and_add(&intervals->kobj,
+			&damon_sysfs_intervals_ktype, &attrs->kobj,
+			"intervals");
+	if (err)
+		goto put_intervals_out;
+	attrs->intervals = intervals;
+
+	nr_regions_range = damon_sysfs_ul_range_alloc(10, 1000);
+	if (!nr_regions_range)
+		goto put_intervals_out;
+
+	err = kobject_init_and_add(&nr_regions_range->kobj,
+			&damon_sysfs_ul_range_ktype, &attrs->kobj,
+			"nr_regions");
+	if (err)
+		goto put_nr_regions_intervals_out;
+	attrs->nr_regions_range = nr_regions_range;
+	return 0;
+
+put_nr_regions_intervals_out:
+	kobject_put(&nr_regions_range->kobj);
+	attrs->nr_regions_range = NULL;
+put_intervals_out:
+	kobject_put(&intervals->kobj);
+	attrs->intervals = NULL;
+	return err;
+}
+
+static void damon_sysfs_attrs_rm_dirs(struct damon_sysfs_attrs *attrs)
+{
+	kobject_put(&attrs->nr_regions_range->kobj);
+	kobject_put(&attrs->intervals->kobj);
+}
+
+static void damon_sysfs_attrs_release(struct kobject *kobj)
+{
+	kfree(container_of(kobj, struct damon_sysfs_attrs, kobj));
+}
+
+static struct attribute *damon_sysfs_attrs_attrs[] = {
+	NULL,
+};
+ATTRIBUTE_GROUPS(damon_sysfs_attrs);
+
+static struct kobj_type damon_sysfs_attrs_ktype = {
+	.release = damon_sysfs_attrs_release,
+	.sysfs_ops = &kobj_sysfs_ops,
+	.default_groups = damon_sysfs_attrs_groups,
+};
+
+/*
+ * context directory
+ */
+
+/* This should match with enum damon_ops_id */
+static const char * const damon_sysfs_ops_strs[] = {
+	"vaddr",
+	"paddr",
+};
+
+struct damon_sysfs_context {
+	struct kobject kobj;
+	enum damon_ops_id ops_id;
+	struct damon_sysfs_attrs *attrs;
+	struct damon_sysfs_targets *targets;
+};
+
+static struct damon_sysfs_context *damon_sysfs_context_alloc(
+		enum damon_ops_id ops_id)
+{
+	struct damon_sysfs_context *context = kmalloc(sizeof(*context),
+				GFP_KERNEL);
+
+	if (!context)
+		return NULL;
+	context->kobj = (struct kobject){};
+	context->ops_id = ops_id;
+	return context;
+}
+
+static int damon_sysfs_context_set_attrs(struct damon_sysfs_context *context)
+{
+	struct damon_sysfs_attrs *attrs = damon_sysfs_attrs_alloc();
+	int err;
+
+	if (!attrs)
+		return -ENOMEM;
+	err = kobject_init_and_add(&attrs->kobj, &damon_sysfs_attrs_ktype,
+			&context->kobj, "monitoring_attrs");
+	if (err)
+		goto out;
+	err = damon_sysfs_attrs_add_dirs(attrs);
+	if (err)
+		goto out;
+	context->attrs = attrs;
+	return 0;
+
+out:
+	kobject_put(&attrs->kobj);
+	return err;
+}
+
+static int damon_sysfs_context_set_targets(struct damon_sysfs_context *context)
+{
+	struct damon_sysfs_targets *targets = damon_sysfs_targets_alloc();
+	int err;
+
+	if (!targets)
+		return -ENOMEM;
+	err = kobject_init_and_add(&targets->kobj, &damon_sysfs_targets_ktype,
+			&context->kobj, "targets");
+	if (err) {
+		kobject_put(&targets->kobj);
+		return err;
+	}
+	context->targets = targets;
+	return 0;
+}
+
+static int damon_sysfs_context_add_dirs(struct damon_sysfs_context *context)
+{
+	int err;
+
+	err = damon_sysfs_context_set_attrs(context);
+	if (err)
+		return err;
+
+	err = damon_sysfs_context_set_targets(context);
+	if (err)
+		goto put_attrs_out;
+
+	return 0;
+
+put_attrs_out:
+	kobject_put(&context->attrs->kobj);
+	context->attrs = NULL;
+	return err;
+}
+
+static void damon_sysfs_context_rm_dirs(struct damon_sysfs_context *context)
+{
+	damon_sysfs_attrs_rm_dirs(context->attrs);
+	kobject_put(&context->attrs->kobj);
+	damon_sysfs_targets_rm_dirs(context->targets);
+	kobject_put(&context->targets->kobj);
+}
+
+static ssize_t damon_sysfs_context_operations_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	struct damon_sysfs_context *context = container_of(kobj,
+			struct damon_sysfs_context, kobj);
+
+	return sysfs_emit(buf, "%s\n", damon_sysfs_ops_strs[context->ops_id]);
+}
+
+static ssize_t damon_sysfs_context_operations_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	struct damon_sysfs_context *context = container_of(kobj,
+			struct damon_sysfs_context, kobj);
+
+	if (sysfs_streq(buf, damon_sysfs_ops_strs[DAMON_OPS_VADDR])) {
+		context->ops_id = DAMON_OPS_VADDR;
+		return count;
+	}
+	return -EINVAL;
+}
+
+static void damon_sysfs_context_release(struct kobject *kobj)
+{
+	kfree(container_of(kobj, struct damon_sysfs_context, kobj));
+}
+
+static struct kobj_attribute damon_sysfs_context_operations_attr = __ATTR(
+		operations, 0600, damon_sysfs_context_operations_show,
+		damon_sysfs_context_operations_store);
+
+static struct attribute *damon_sysfs_context_attrs[] = {
+	&damon_sysfs_context_operations_attr.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(damon_sysfs_context);
+
+static struct kobj_type damon_sysfs_context_ktype = {
+	.release = damon_sysfs_context_release,
+	.sysfs_ops = &kobj_sysfs_ops,
+	.default_groups = damon_sysfs_context_groups,
+};
+
+/*
+ * contexts directory
+ */
+
+struct damon_sysfs_contexts {
+	struct kobject kobj;
+	struct damon_sysfs_context **contexts_arr;
+	int nr;
+};
+
+static struct damon_sysfs_contexts *damon_sysfs_contexts_alloc(void)
+{
+	return kzalloc(sizeof(struct damon_sysfs_contexts), GFP_KERNEL);
+}
+
+static void damon_sysfs_contexts_rm_dirs(struct damon_sysfs_contexts *contexts)
+{
+	struct damon_sysfs_context **contexts_arr = contexts->contexts_arr;
+	int i;
+
+	for (i = 0; i < contexts->nr; i++) {
+		damon_sysfs_context_rm_dirs(contexts_arr[i]);
+		kobject_put(&contexts_arr[i]->kobj);
+	}
+	contexts->nr = 0;
+	kfree(contexts_arr);
+	contexts->contexts_arr = NULL;
+}
+
+static int damon_sysfs_contexts_add_dirs(struct damon_sysfs_contexts *contexts,
+		int nr_contexts)
+{
+	struct damon_sysfs_context **contexts_arr, *context;
+	int err, i;
+
+	damon_sysfs_contexts_rm_dirs(contexts);
+	if (!nr_contexts)
+		return 0;
+
+	contexts_arr = kmalloc_array(nr_contexts, sizeof(*contexts_arr),
+			GFP_KERNEL | __GFP_NOWARN);
+	if (!contexts_arr)
+		return -ENOMEM;
+	contexts->contexts_arr = contexts_arr;
+
+	for (i = 0; i < nr_contexts; i++) {
+		context = damon_sysfs_context_alloc(DAMON_OPS_VADDR);
+		if (!context) {
+			damon_sysfs_contexts_rm_dirs(contexts);
+			return -ENOMEM;
+		}
+
+		err = kobject_init_and_add(&context->kobj,
+				&damon_sysfs_context_ktype, &contexts->kobj,
+				"%d", i);
+		if (err)
+			goto out;
+
+		err = damon_sysfs_context_add_dirs(context);
+		if (err)
+			goto out;
+
+		contexts_arr[i] = context;
+		contexts->nr++;
+	}
+	return 0;
+
+out:
+	damon_sysfs_contexts_rm_dirs(contexts);
+	kobject_put(&context->kobj);
+	return err;
+}
+
+static ssize_t damon_sysfs_contexts_nr_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	struct damon_sysfs_contexts *contexts = container_of(kobj,
+			struct damon_sysfs_contexts, kobj);
+
+	return sysfs_emit(buf, "%d\n", contexts->nr);
+}
+
+static ssize_t damon_sysfs_contexts_nr_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	struct damon_sysfs_contexts *contexts = container_of(kobj,
+			struct damon_sysfs_contexts, kobj);
+	int nr, err;
+
+	err = kstrtoint(buf, 0, &nr);
+	if (err)
+		return err;
+	/* TODO: support multiple contexts per kdamond */
+	if (nr < 0 || 1 < nr)
+		return -EINVAL;
+
+	if (!mutex_trylock(&damon_sysfs_lock))
+		return -EBUSY;
+	err = damon_sysfs_contexts_add_dirs(contexts, nr);
+	mutex_unlock(&damon_sysfs_lock);
+	if (err)
+		return err;
+
+	return count;
+}
+
+static void damon_sysfs_contexts_release(struct kobject *kobj)
+{
+	kfree(container_of(kobj, struct damon_sysfs_contexts, kobj));
+}
+
+static struct kobj_attribute damon_sysfs_contexts_nr_attr = __ATTR(nr, 0600,
+		damon_sysfs_contexts_nr_show, damon_sysfs_contexts_nr_store);
+
+static struct attribute *damon_sysfs_contexts_attrs[] = {
+	&damon_sysfs_contexts_nr_attr.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(damon_sysfs_contexts);
+
+static struct kobj_type damon_sysfs_contexts_ktype = {
+	.release = damon_sysfs_contexts_release,
+	.sysfs_ops = &kobj_sysfs_ops,
+	.default_groups = damon_sysfs_contexts_groups,
+};
+
+/*
+ * kdamond directory
+ */
+
+struct damon_sysfs_kdamond {
+	struct kobject kobj;
+	struct damon_sysfs_contexts *contexts;
+};
+
+static struct damon_sysfs_kdamond *damon_sysfs_kdamond_alloc(void)
+{
+	return kzalloc(sizeof(struct damon_sysfs_kdamond), GFP_KERNEL);
+}
+
+static int damon_sysfs_kdamond_add_dirs(struct damon_sysfs_kdamond *kdamond)
+{
+	struct damon_sysfs_contexts *contexts;
+	int err;
+
+	contexts = damon_sysfs_contexts_alloc();
+	if (!contexts)
+		return -ENOMEM;
+
+	err = kobject_init_and_add(&contexts->kobj,
+			&damon_sysfs_contexts_ktype, &kdamond->kobj,
+			"contexts");
+	if (err) {
+		kobject_put(&contexts->kobj);
+		return err;
+	}
+	kdamond->contexts = contexts;
+
+	return err;
+}
+
+static void damon_sysfs_kdamond_rm_dirs(struct damon_sysfs_kdamond *kdamond)
+{
+	damon_sysfs_contexts_rm_dirs(kdamond->contexts);
+	kobject_put(&kdamond->contexts->kobj);
+}
+
+static ssize_t damon_sysfs_kdamond_state_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	return sysfs_emit(buf, "off\n");
+}
+
+static ssize_t damon_sysfs_kdamond_state_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	return count;
+}
+
+static ssize_t damon_sysfs_kdamond_pid_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	return sysfs_emit(buf, "-1\n");
+}
+
+static void damon_sysfs_kdamond_release(struct kobject *kobj)
+{
+	kfree(container_of(kobj, struct damon_sysfs_kdamond, kobj));
+}
+
+static struct kobj_attribute damon_sysfs_kdamond_state_attr =
+	__ATTR(state, 0600, damon_sysfs_kdamond_state_show,
+		damon_sysfs_kdamond_state_store);
+
+static struct kobj_attribute damon_sysfs_kdamond_pid_attr = __ATTR(pid, 0400,
+		damon_sysfs_kdamond_pid_show, NULL);
+
+static struct attribute *damon_sysfs_kdamond_attrs[] = {
+	&damon_sysfs_kdamond_state_attr.attr,
+	&damon_sysfs_kdamond_pid_attr.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(damon_sysfs_kdamond);
+
+static struct kobj_type damon_sysfs_kdamond_ktype = {
+	.release = damon_sysfs_kdamond_release,
+	.sysfs_ops = &kobj_sysfs_ops,
+	.default_groups = damon_sysfs_kdamond_groups,
+};
+
+/*
+ * kdamonds directory
+ */
+
+struct damon_sysfs_kdamonds {
+	struct kobject kobj;
+	struct damon_sysfs_kdamond **kdamonds_arr;
+	int nr;
+};
+
+static struct damon_sysfs_kdamonds *damon_sysfs_kdamonds_alloc(void)
+{
+	return kzalloc(sizeof(struct damon_sysfs_kdamonds), GFP_KERNEL);
+}
+
+static void damon_sysfs_kdamonds_rm_dirs(struct damon_sysfs_kdamonds *kdamonds)
+{
+	struct damon_sysfs_kdamond **kdamonds_arr = kdamonds->kdamonds_arr;
+	int i;
+
+	for (i = 0; i < kdamonds->nr; i++) {
+		damon_sysfs_kdamond_rm_dirs(kdamonds_arr[i]);
+		kobject_put(&kdamonds_arr[i]->kobj);
+	}
+	kdamonds->nr = 0;
+	kfree(kdamonds_arr);
+	kdamonds->kdamonds_arr = NULL;
+}
+
+static int damon_sysfs_kdamonds_add_dirs(struct damon_sysfs_kdamonds *kdamonds,
+		int nr_kdamonds)
+{
+	struct damon_sysfs_kdamond **kdamonds_arr, *kdamond;
+	int err, i;
+
+	damon_sysfs_kdamonds_rm_dirs(kdamonds);
+	if (!nr_kdamonds)
+		return 0;
+
+	kdamonds_arr = kmalloc_array(nr_kdamonds, sizeof(*kdamonds_arr),
+			GFP_KERNEL | __GFP_NOWARN);
+	if (!kdamonds_arr)
+		return -ENOMEM;
+	kdamonds->kdamonds_arr = kdamonds_arr;
+
+	for (i = 0; i < nr_kdamonds; i++) {
+		kdamond = damon_sysfs_kdamond_alloc();
+		if (!kdamond) {
+			damon_sysfs_kdamonds_rm_dirs(kdamonds);
+			return -ENOMEM;
+		}
+
+		err = kobject_init_and_add(&kdamond->kobj,
+				&damon_sysfs_kdamond_ktype, &kdamonds->kobj,
+				"%d", i);
+		if (err)
+			goto out;
+
+		err = damon_sysfs_kdamond_add_dirs(kdamond);
+		if (err)
+			goto out;
+
+		kdamonds_arr[i] = kdamond;
+		kdamonds->nr++;
+	}
+	return 0;
+
+out:
+	damon_sysfs_kdamonds_rm_dirs(kdamonds);
+	kobject_put(&kdamond->kobj);
+	return err;
+}
+
+static ssize_t damon_sysfs_kdamonds_nr_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	struct damon_sysfs_kdamonds *kdamonds = container_of(kobj,
+			struct damon_sysfs_kdamonds, kobj);
+
+	return sysfs_emit(buf, "%d\n", kdamonds->nr);
+}
+
+static ssize_t damon_sysfs_kdamonds_nr_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	struct damon_sysfs_kdamonds *kdamonds = container_of(kobj,
+			struct damon_sysfs_kdamonds, kobj);
+	int nr, err;
+
+	err = kstrtoint(buf, 0, &nr);
+	if (err)
+		return err;
+	if (nr < 0)
+		return -EINVAL;
+
+	if (!mutex_trylock(&damon_sysfs_lock))
+		return -EBUSY;
+	err = damon_sysfs_kdamonds_add_dirs(kdamonds, nr);
+	mutex_unlock(&damon_sysfs_lock);
+	if (err)
+		return err;
+
+	return count;
+}
+
+static void damon_sysfs_kdamonds_release(struct kobject *kobj)
+{
+	kfree(container_of(kobj, struct damon_sysfs_kdamonds, kobj));
+}
+
+static struct kobj_attribute damon_sysfs_kdamonds_nr_attr = __ATTR(nr, 0600,
+		damon_sysfs_kdamonds_nr_show, damon_sysfs_kdamonds_nr_store);
+
+static struct attribute *damon_sysfs_kdamonds_attrs[] = {
+	&damon_sysfs_kdamonds_nr_attr.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(damon_sysfs_kdamonds);
+
+static struct kobj_type damon_sysfs_kdamonds_ktype = {
+	.release = damon_sysfs_kdamonds_release,
+	.sysfs_ops = &kobj_sysfs_ops,
+	.default_groups = damon_sysfs_kdamonds_groups,
+};
+
+/*
+ * damon user interface directory
+ */
+
+struct damon_sysfs_ui_dir {
+	struct kobject kobj;
+	struct damon_sysfs_kdamonds *kdamonds;
+};
+
+static struct damon_sysfs_ui_dir *damon_sysfs_ui_dir_alloc(void)
+{
+	return kzalloc(sizeof(struct damon_sysfs_ui_dir), GFP_KERNEL);
+}
+
+static int damon_sysfs_ui_dir_add_dirs(struct damon_sysfs_ui_dir *ui_dir)
+{
+	struct damon_sysfs_kdamonds *kdamonds;
+	int err;
+
+	kdamonds = damon_sysfs_kdamonds_alloc();
+	if (!kdamonds)
+		return -ENOMEM;
+
+	err = kobject_init_and_add(&kdamonds->kobj,
+			&damon_sysfs_kdamonds_ktype, &ui_dir->kobj,
+			"kdamonds");
+	if (err) {
+		kobject_put(&kdamonds->kobj);
+		return err;
+	}
+	ui_dir->kdamonds = kdamonds;
+	return err;
+}
+
+static void damon_sysfs_ui_dir_release(struct kobject *kobj)
+{
+	kfree(container_of(kobj, struct damon_sysfs_ui_dir, kobj));
+}
+
+static struct attribute *damon_sysfs_ui_dir_attrs[] = {
+	NULL,
+};
+ATTRIBUTE_GROUPS(damon_sysfs_ui_dir);
+
+static struct kobj_type damon_sysfs_ui_dir_ktype = {
+	.release = damon_sysfs_ui_dir_release,
+	.sysfs_ops = &kobj_sysfs_ops,
+	.default_groups = damon_sysfs_ui_dir_groups,
+};
+
+static int __init damon_sysfs_init(void)
+{
+	struct kobject *damon_sysfs_root;
+	struct damon_sysfs_ui_dir *admin;
+	int err;
+
+	damon_sysfs_root = kobject_create_and_add("damon", mm_kobj);
+	if (!damon_sysfs_root)
+		return -ENOMEM;
+
+	admin = damon_sysfs_ui_dir_alloc();
+	if (!admin) {
+		kobject_put(damon_sysfs_root);
+		return -ENOMEM;
+	}
+	err = kobject_init_and_add(&admin->kobj, &damon_sysfs_ui_dir_ktype,
+			damon_sysfs_root, "admin");
+	if (err)
+		goto out;
+	err = damon_sysfs_ui_dir_add_dirs(admin);
+	if (err)
+		goto out;
+	return 0;
+
+out:
+	kobject_put(&admin->kobj);
+	kobject_put(damon_sysfs_root);
+	return err;
+}
+subsys_initcall(damon_sysfs_init);
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH 04/12] mm/damon/sysfs: Link DAMON for virtual address spaces monitoring
  2022-02-23 15:20 [PATCH 00/12] Introduce DAMON sysfs interface SeongJae Park
                   ` (2 preceding siblings ...)
  2022-02-23 15:20 ` [PATCH 03/12] mm/damon: Implement a minimal stub for sysfs-based DAMON interface SeongJae Park
@ 2022-02-23 15:20 ` SeongJae Park
  2022-02-23 15:20 ` [PATCH 05/12] mm/damon/sysfs: Support physical address space monitoring SeongJae Park
                   ` (9 subsequent siblings)
  13 siblings, 0 replies; 24+ messages in thread
From: SeongJae Park @ 2022-02-23 15:20 UTC (permalink / raw)
  To: akpm
  Cc: corbet, skhan, rientjes, xhao, linux-damon, linux-mm, linux-doc,
	linux-kselftest, linux-kernel, SeongJae Park

This commit links the DAMON sysfs interface to DAMON so that users can
control DAMON via the interface.  In detail, this commit makes writing
'on' to 'state' file constructs DAMON contexts based on values that
users have written to relevant sysfs files and start the context.  It
supports only virtual address spaces monitoring at the moment, though.

The files hierarchy of DAMON sysfs interface after this commit is shown
below.  In the below figure, parents-children relations are represented
with indentations, each directory is having ``/`` suffix, and files in
each directory are separated by comma (",").

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

The usage is straightforward.  Writing a number ('N') to each 'nr' file
makes directories named '0' to 'N-1'.  Users can construct DAMON
contexts by writing proper values to the files in the straightforward
manner and start each kdamond by writing 'on' to 'kdamonds/<N>/state'.

Signed-off-by: SeongJae Park <sj@kernel.org>
---
 mm/damon/sysfs.c | 220 ++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 217 insertions(+), 3 deletions(-)

diff --git a/mm/damon/sysfs.c b/mm/damon/sysfs.c
index e8087ffd924c..be0bc5e745cf 100644
--- a/mm/damon/sysfs.c
+++ b/mm/damon/sysfs.c
@@ -775,6 +775,7 @@ static struct kobj_type damon_sysfs_contexts_ktype = {
 struct damon_sysfs_kdamond {
 	struct kobject kobj;
 	struct damon_sysfs_contexts *contexts;
+	struct damon_ctx *damon_ctx;
 };
 
 static struct damon_sysfs_kdamond *damon_sysfs_kdamond_alloc(void)
@@ -809,26 +810,217 @@ 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)
 {
-	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 int damon_sysfs_set_attrs(struct damon_ctx *ctx,
+		struct damon_sysfs_attrs *sys_attrs)
+{
+	struct damon_sysfs_intervals *sys_intervals = sys_attrs->intervals;
+	struct damon_sysfs_ul_range *sys_nr_regions =
+		sys_attrs->nr_regions_range;
+
+	return damon_set_attrs(ctx, sys_intervals->sample_us,
+			sys_intervals->aggr_us, sys_intervals->update_us,
+			sys_nr_regions->min, sys_nr_regions->max);
+}
+
+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_targets *sysfs_targets)
+{
+	int i;
+
+	for (i = 0; i < sysfs_targets->nr; i++) {
+		struct damon_sysfs_target *sys_target =
+			sysfs_targets->targets_arr[i];
+		struct damon_target *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(sys_target->pid);
+			if (!t->pid) {
+				damon_sysfs_destroy_targets(ctx);
+				return -EINVAL;
+			}
+		}
+		damon_add_target(ctx, t);
+	}
+	return 0;
+}
+
+static void damon_sysfs_before_terminate(struct damon_ctx *ctx)
+{
+	struct damon_target *t, *next;
+
+	if (ctx->ops.id != DAMON_OPS_VADDR)
+		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();
+	int err;
+
+	if (!ctx)
+		return ERR_PTR(-ENOMEM);
+
+	err = damon_select_ops(ctx, sys_ctx->ops_id);
+	if (err)
+		goto out;
+	err = damon_sysfs_set_attrs(ctx, sys_ctx->attrs);
+	if (err)
+		goto out;
+	err = damon_sysfs_set_targets(ctx, sys_ctx->targets);
+	if (err)
+		goto out;
+
+	ctx->callback.before_terminate = damon_sysfs_before_terminate;
+	return ctx;
+
+out:
+	damon_destroy_ctx(ctx);
+	return ERR_PTR(err);
+}
+
+static int damon_sysfs_turn_damon_on(struct damon_sysfs_kdamond *kdamond)
+{
+	struct damon_ctx *ctx;
+	int err;
+
+	if (kdamond->damon_ctx &&
+			damon_sysfs_ctx_running(kdamond->damon_ctx))
+		return -EBUSY;
+	/* TODO: support multiple contexts per kdamond */
+	if (kdamond->contexts->nr != 1)
+		return -EINVAL;
+
+	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))
+		return PTR_ERR(ctx);
+	err = damon_start(&ctx, 1, false);
+	if (err) {
+		damon_destroy_ctx(ctx);
+		return err;
+	}
+	kdamond->damon_ctx = ctx;
+	return err;
+}
+
+static int damon_sysfs_turn_damon_off(struct damon_sysfs_kdamond *kdamond)
+{
+	if (!kdamond->damon_ctx)
+		return -EINVAL;
+	return damon_stop(&kdamond->damon_ctx, 1);
+	/*
+	 * To allow users show final monitoring results of already turned-off
+	 * DAMON, we free kdamond->damon_ctx in next
+	 * damon_sysfs_turn_damon_on(), or kdamonds_nr_store()
+	 */
 }
 
 static ssize_t damon_sysfs_kdamond_state_store(struct kobject *kobj,
 		struct kobj_attribute *attr, const char *buf, size_t count)
 {
-	return count;
+	struct damon_sysfs_kdamond *kdamond = container_of(kobj,
+			struct damon_sysfs_kdamond, kobj);
+	ssize_t ret;
+
+	if (!mutex_trylock(&damon_sysfs_lock))
+		return -EBUSY;
+	if (sysfs_streq(buf, "on"))
+		ret = damon_sysfs_turn_damon_on(kdamond);
+	else if (sysfs_streq(buf, "off"))
+		ret = damon_sysfs_turn_damon_off(kdamond);
+	else
+		ret = -EINVAL;
+	mutex_unlock(&damon_sysfs_lock);
+	if (!ret)
+		ret = count;
+	return ret;
 }
 
 static ssize_t damon_sysfs_kdamond_pid_show(struct kobject *kobj,
 		struct kobj_attribute *attr, char *buf)
 {
-	return sysfs_emit(buf, "-1\n");
+	struct damon_sysfs_kdamond *kdamond = container_of(kobj,
+			struct damon_sysfs_kdamond, kobj);
+	struct damon_ctx *ctx;
+	int pid;
+
+	if (!mutex_trylock(&damon_sysfs_lock))
+		return -EBUSY;
+	ctx = kdamond->damon_ctx;
+	if (!ctx) {
+		pid = -1;
+		goto out;
+	}
+	mutex_lock(&ctx->kdamond_lock);
+	if (!ctx->kdamond)
+		pid = -1;
+	else
+		pid = ctx->kdamond->pid;
+	mutex_unlock(&ctx->kdamond_lock);
+out:
+	mutex_unlock(&damon_sysfs_lock);
+	return sysfs_emit(buf, "%d\n", pid);
 }
 
 static void damon_sysfs_kdamond_release(struct kobject *kobj)
 {
+	struct damon_sysfs_kdamond *kdamond = container_of(kobj,
+			struct damon_sysfs_kdamond, kobj);
+
+	if (kdamond->damon_ctx)
+		damon_destroy_ctx(kdamond->damon_ctx);
 	kfree(container_of(kobj, struct damon_sysfs_kdamond, kobj));
 }
 
@@ -881,12 +1073,34 @@ static void damon_sysfs_kdamonds_rm_dirs(struct damon_sysfs_kdamonds *kdamonds)
 	kdamonds->kdamonds_arr = NULL;
 }
 
+static int damon_sysfs_nr_running_ctxs(struct damon_sysfs_kdamond **kdamonds,
+		int nr_kdamonds)
+{
+	int nr_running_ctxs = 0;
+	int i;
+
+	for (i = 0; i < nr_kdamonds; i++) {
+		struct damon_ctx *ctx = kdamonds[i]->damon_ctx;
+
+		if (!ctx)
+			continue;
+		mutex_lock(&ctx->kdamond_lock);
+		if (ctx->kdamond)
+			nr_running_ctxs++;
+		mutex_unlock(&ctx->kdamond_lock);
+	}
+	return nr_running_ctxs;
+}
+
 static int damon_sysfs_kdamonds_add_dirs(struct damon_sysfs_kdamonds *kdamonds,
 		int nr_kdamonds)
 {
 	struct damon_sysfs_kdamond **kdamonds_arr, *kdamond;
 	int err, i;
 
+	if (damon_sysfs_nr_running_ctxs(kdamonds->kdamonds_arr, kdamonds->nr))
+		return -EBUSY;
+
 	damon_sysfs_kdamonds_rm_dirs(kdamonds);
 	if (!nr_kdamonds)
 		return 0;
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH 05/12] mm/damon/sysfs: Support physical address space monitoring
  2022-02-23 15:20 [PATCH 00/12] Introduce DAMON sysfs interface SeongJae Park
                   ` (3 preceding siblings ...)
  2022-02-23 15:20 ` [PATCH 04/12] mm/damon/sysfs: Link DAMON for virtual address spaces monitoring SeongJae Park
@ 2022-02-23 15:20 ` SeongJae Park
  2022-02-23 15:20 ` [PATCH 06/12] mm/damon/sysfs: Support DAMON-based Operation Schemes SeongJae Park
                   ` (8 subsequent siblings)
  13 siblings, 0 replies; 24+ messages in thread
From: SeongJae Park @ 2022-02-23 15:20 UTC (permalink / raw)
  To: akpm
  Cc: corbet, skhan, rientjes, xhao, linux-damon, linux-mm, linux-doc,
	linux-kselftest, linux-kernel, SeongJae Park

This commit makes DAMON sysfs interface supports the physical address
space monitoring.  Specifically, this commit adds support of the initial
monitoring regions set feature by adding 'regions' directory under each
target directory and makes context operations file to receive 'paddr' in
addition to 'vaddr'.

As a result, the files hierarchy becomes as below:

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

Signed-off-by: SeongJae Park <sj@kernel.org>
---
 mm/damon/sysfs.c | 284 ++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 279 insertions(+), 5 deletions(-)

diff --git a/mm/damon/sysfs.c b/mm/damon/sysfs.c
index be0bc5e745cf..c9ad3808077e 100644
--- a/mm/damon/sysfs.c
+++ b/mm/damon/sysfs.c
@@ -115,12 +115,222 @@ static struct kobj_type damon_sysfs_ul_range_ktype = {
 	.default_groups = damon_sysfs_ul_range_groups,
 };
 
+/*
+ * init region directory
+ */
+
+struct damon_sysfs_region {
+	struct kobject kobj;
+	unsigned long start;
+	unsigned long end;
+};
+
+static struct damon_sysfs_region *damon_sysfs_region_alloc(
+		unsigned long start,
+		unsigned long end)
+{
+	struct damon_sysfs_region *region = kmalloc(sizeof(*region),
+			GFP_KERNEL);
+
+	if (!region)
+		return NULL;
+	region->kobj = (struct kobject){};
+	region->start = start;
+	region->end = end;
+	return region;
+}
+
+static ssize_t damon_sysfs_region_start_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	struct damon_sysfs_region *region = container_of(kobj,
+			struct damon_sysfs_region, kobj);
+
+	return sysfs_emit(buf, "%lu\n", region->start);
+}
+
+static ssize_t damon_sysfs_region_start_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	struct damon_sysfs_region *region = container_of(kobj,
+			struct damon_sysfs_region, kobj);
+	int err = kstrtoul(buf, 0, &region->start);
+
+	if (err)
+		return -EINVAL;
+	return count;
+}
+
+static ssize_t damon_sysfs_region_end_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	struct damon_sysfs_region *region = container_of(kobj,
+			struct damon_sysfs_region, kobj);
+
+	return sysfs_emit(buf, "%lu\n", region->end);
+}
+
+static ssize_t damon_sysfs_region_end_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	struct damon_sysfs_region *region = container_of(kobj,
+			struct damon_sysfs_region, kobj);
+	int err = kstrtoul(buf, 0, &region->end);
+
+	if (err)
+		return -EINVAL;
+	return count;
+}
+
+static void damon_sysfs_region_release(struct kobject *kobj)
+{
+	kfree(container_of(kobj, struct damon_sysfs_region, kobj));
+}
+
+static struct kobj_attribute damon_sysfs_region_start_attr =
+		__ATTR(start, 0600, damon_sysfs_region_start_show,
+				damon_sysfs_region_start_store);
+
+static struct kobj_attribute damon_sysfs_region_end_attr =
+		__ATTR(end, 0600, damon_sysfs_region_end_show,
+				damon_sysfs_region_end_store);
+
+static struct attribute *damon_sysfs_region_attrs[] = {
+	&damon_sysfs_region_start_attr.attr,
+	&damon_sysfs_region_end_attr.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(damon_sysfs_region);
+
+static struct kobj_type damon_sysfs_region_ktype = {
+	.release = damon_sysfs_region_release,
+	.sysfs_ops = &kobj_sysfs_ops,
+	.default_groups = damon_sysfs_region_groups,
+};
+
+/*
+ * init_regions directory
+ */
+
+struct damon_sysfs_regions {
+	struct kobject kobj;
+	struct damon_sysfs_region **regions_arr;
+	int nr;
+};
+
+static struct damon_sysfs_regions *damon_sysfs_regions_alloc(void)
+{
+	return kzalloc(sizeof(struct damon_sysfs_regions), GFP_KERNEL);
+}
+
+static void damon_sysfs_regions_rm_dirs(struct damon_sysfs_regions *regions)
+{
+	struct damon_sysfs_region **regions_arr = regions->regions_arr;
+	int i;
+
+	for (i = 0; i < regions->nr; i++)
+		kobject_put(&regions_arr[i]->kobj);
+	regions->nr = 0;
+	kfree(regions_arr);
+	regions->regions_arr = NULL;
+}
+
+static int damon_sysfs_regions_add_dirs(struct damon_sysfs_regions *regions,
+		int nr_regions)
+{
+	struct damon_sysfs_region **regions_arr, *region;
+	int err, i;
+
+	damon_sysfs_regions_rm_dirs(regions);
+	if (!nr_regions)
+		return 0;
+
+	regions_arr = kmalloc_array(nr_regions, sizeof(*regions_arr),
+			GFP_KERNEL | __GFP_NOWARN);
+	if (!regions_arr)
+		return -ENOMEM;
+	regions->regions_arr = regions_arr;
+
+	for (i = 0; i < nr_regions; i++) {
+		region = damon_sysfs_region_alloc(0, 0);
+		if (!region) {
+			damon_sysfs_regions_rm_dirs(regions);
+			return -ENOMEM;
+		}
+
+		err = kobject_init_and_add(&region->kobj,
+				&damon_sysfs_region_ktype, &regions->kobj,
+				"%d", i);
+		if (err) {
+			kobject_put(&region->kobj);
+			damon_sysfs_regions_rm_dirs(regions);
+			return err;
+		}
+
+		regions_arr[i] = region;
+		regions->nr++;
+	}
+	return 0;
+}
+
+static ssize_t damon_sysfs_regions_nr_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	struct damon_sysfs_regions *regions = container_of(kobj,
+			struct damon_sysfs_regions, kobj);
+
+	return sysfs_emit(buf, "%d\n", regions->nr);
+}
+
+static ssize_t damon_sysfs_regions_nr_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	struct damon_sysfs_regions *regions = container_of(kobj,
+			struct damon_sysfs_regions, kobj);
+	int nr, err = kstrtoint(buf, 0, &nr);
+
+	if (err)
+		return err;
+	if (nr < 0)
+		return -EINVAL;
+
+	if (!mutex_trylock(&damon_sysfs_lock))
+		return -EBUSY;
+	err = damon_sysfs_regions_add_dirs(regions, nr);
+	mutex_unlock(&damon_sysfs_lock);
+	if (err)
+		return err;
+
+	return count;
+}
+
+static void damon_sysfs_regions_release(struct kobject *kobj)
+{
+	kfree(container_of(kobj, struct damon_sysfs_regions, kobj));
+}
+
+static struct kobj_attribute damon_sysfs_regions_nr_attr = __ATTR(nr, 0600,
+		damon_sysfs_regions_nr_show, damon_sysfs_regions_nr_store);
+
+static struct attribute *damon_sysfs_regions_attrs[] = {
+	&damon_sysfs_regions_nr_attr.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(damon_sysfs_regions);
+
+static struct kobj_type damon_sysfs_regions_ktype = {
+	.release = damon_sysfs_regions_release,
+	.sysfs_ops = &kobj_sysfs_ops,
+	.default_groups = damon_sysfs_regions_groups,
+};
+
 /*
  * target directory
  */
 
 struct damon_sysfs_target {
 	struct kobject kobj;
+	struct damon_sysfs_regions *regions;
 	int pid;
 };
 
@@ -129,6 +339,29 @@ static struct damon_sysfs_target *damon_sysfs_target_alloc(void)
 	return kzalloc(sizeof(struct damon_sysfs_target), GFP_KERNEL);
 }
 
+static int damon_sysfs_target_add_dirs(struct damon_sysfs_target *target)
+{
+	struct damon_sysfs_regions *regions = damon_sysfs_regions_alloc();
+	int err;
+
+	if (!regions)
+		return -ENOMEM;
+
+	err = kobject_init_and_add(&regions->kobj, &damon_sysfs_regions_ktype,
+			&target->kobj, "regions");
+	if (err)
+		kobject_put(&regions->kobj);
+	else
+		target->regions = regions;
+	return err;
+}
+
+static void damon_sysfs_target_rm_dirs(struct damon_sysfs_target *target)
+{
+	damon_sysfs_regions_rm_dirs(target->regions);
+	kobject_put(&target->regions->kobj);
+}
+
 static ssize_t damon_sysfs_target_pid_show(struct kobject *kobj,
 		struct kobj_attribute *attr, char *buf)
 {
@@ -190,8 +423,10 @@ static void damon_sysfs_targets_rm_dirs(struct damon_sysfs_targets *targets)
 	struct damon_sysfs_target **targets_arr = targets->targets_arr;
 	int i;
 
-	for (i = 0; i < targets->nr; i++)
+	for (i = 0; i < targets->nr; i++) {
+		damon_sysfs_target_rm_dirs(targets_arr[i]);
 		kobject_put(&targets_arr[i]->kobj);
+	}
 	targets->nr = 0;
 	kfree(targets_arr);
 	targets->targets_arr = NULL;
@@ -226,6 +461,10 @@ static int damon_sysfs_targets_add_dirs(struct damon_sysfs_targets *targets,
 		if (err)
 			goto out;
 
+		err = damon_sysfs_target_add_dirs(target);
+		if (err)
+			goto out;
+
 		targets_arr[i] = target;
 		targets->nr++;
 	}
@@ -613,10 +852,13 @@ static ssize_t damon_sysfs_context_operations_store(struct kobject *kobj,
 {
 	struct damon_sysfs_context *context = container_of(kobj,
 			struct damon_sysfs_context, kobj);
+	enum damon_ops_id id;
 
-	if (sysfs_streq(buf, damon_sysfs_ops_strs[DAMON_OPS_VADDR])) {
-		context->ops_id = DAMON_OPS_VADDR;
-		return count;
+	for (id = 0; id < NR_DAMON_OPS; id++) {
+		if (sysfs_streq(buf, damon_sysfs_ops_strs[id])) {
+			context->ops_id = id;
+			return count;
+		}
 	}
 	return -EINVAL;
 }
@@ -859,10 +1101,37 @@ static void damon_sysfs_destroy_targets(struct damon_ctx *ctx)
 	}
 }
 
+static int damon_sysfs_set_regions(struct damon_target *t,
+		struct damon_sysfs_regions *sysfs_regions)
+{
+	int i;
+
+	for (i = 0; i < sysfs_regions->nr; i++) {
+		struct damon_sysfs_region *sys_region =
+			sysfs_regions->regions_arr[i];
+		struct damon_region *prev, *r;
+
+		if (sys_region->start > sys_region->end)
+			return -EINVAL;
+		r = damon_new_region(sys_region->start, sys_region->end);
+		if (!r)
+			return -ENOMEM;
+		damon_add_region(r, t);
+		if (damon_nr_regions(t) > 1) {
+			prev = damon_prev_region(r);
+			if (prev->ar.end > r->ar.start) {
+				damon_destroy_region(r, t);
+				return -EINVAL;
+			}
+		}
+	}
+	return 0;
+}
+
 static int damon_sysfs_set_targets(struct damon_ctx *ctx,
 		struct damon_sysfs_targets *sysfs_targets)
 {
-	int i;
+	int i, err;
 
 	for (i = 0; i < sysfs_targets->nr; i++) {
 		struct damon_sysfs_target *sys_target =
@@ -881,6 +1150,11 @@ static int damon_sysfs_set_targets(struct damon_ctx *ctx,
 			}
 		}
 		damon_add_target(ctx, t);
+		err = damon_sysfs_set_regions(t, sys_target->regions);
+		if (err) {
+			damon_sysfs_destroy_targets(ctx);
+			return err;
+		}
 	}
 	return 0;
 }
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH 06/12] mm/damon/sysfs: Support DAMON-based Operation Schemes
  2022-02-23 15:20 [PATCH 00/12] Introduce DAMON sysfs interface SeongJae Park
                   ` (4 preceding siblings ...)
  2022-02-23 15:20 ` [PATCH 05/12] mm/damon/sysfs: Support physical address space monitoring SeongJae Park
@ 2022-02-23 15:20 ` SeongJae Park
  2022-02-23 15:20 ` [PATCH 07/12] mm/damon/sysfs: Support DAMOS quotas SeongJae Park
                   ` (7 subsequent siblings)
  13 siblings, 0 replies; 24+ messages in thread
From: SeongJae Park @ 2022-02-23 15:20 UTC (permalink / raw)
  To: akpm
  Cc: corbet, skhan, rientjes, xhao, linux-damon, linux-mm, linux-doc,
	linux-kselftest, linux-kernel, SeongJae Park

This commit makes DAMON sysfs interface supports the DAMON-based
operation schemes (DAMOS) feature.  Specifically, this commit adds
'schemes' directory under each context direcotry, and makes kdamond
'state' file writing respects the contents in the directory.

Note that this commit doesn't support all features of DAMOS but only the
target access pattern and action feature.  Supports for quotas,
prioritization, watermarks will follow.

As a result, the files hierarchy becomes as below:

    /sys/kernel/mm/damon/admin
    │ kdamonds/nr
    │ │ 0/state,pid
    │ │ │ contexts/nr
    │ │ │ │ 0/operations
    │ │ │ │ │ monitoring_attrs/intervals/sample_us,aggr_us,update_us
    │ │ │ │ │ │ nr_regions/min,max
    │ │ │ │ │ targets/nr
    │ │ │ │ │ │ 0/pid
    │ │ │ │ │ │ │ regions/nr
    │ │ │ │ │ │ │ │ 0/start,end
    │ │ │ │ │ │ │ │ ...
    │ │ │ │ │ │ ...
    │ │ │ │ │ schemes/nr    <- NEW DIRECTORY
    │ │ │ │ │ │ 0/action
    │ │ │ │ │ │ │ access_pattern/
    │ │ │ │ │ │ │ │ sz/min,max
    │ │ │ │ │ │ │ │ nr_accesses/min,max
    │ │ │ │ │ │ │ │ age/min,max
    │ │ │ │ │ │ ...
    │ │ │ │ ...
    │ │ ...

Signed-off-by: SeongJae Park <sj@kernel.org>
---
 mm/damon/sysfs.c | 418 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 418 insertions(+)

diff --git a/mm/damon/sysfs.c b/mm/damon/sysfs.c
index c9ad3808077e..2350a637e662 100644
--- a/mm/damon/sysfs.c
+++ b/mm/damon/sysfs.c
@@ -115,6 +115,349 @@ static struct kobj_type damon_sysfs_ul_range_ktype = {
 	.default_groups = damon_sysfs_ul_range_groups,
 };
 
+/*
+ * access_pattern directory
+ */
+
+struct damon_sysfs_access_pattern {
+	struct kobject kobj;
+	struct damon_sysfs_ul_range *sz;
+	struct damon_sysfs_ul_range *nr_accesses;
+	struct damon_sysfs_ul_range *age;
+};
+
+static
+struct damon_sysfs_access_pattern *damon_sysfs_access_pattern_alloc(void)
+{
+	struct damon_sysfs_access_pattern *access_pattern =
+		kmalloc(sizeof(*access_pattern), GFP_KERNEL);
+
+	if (!access_pattern)
+		return NULL;
+	access_pattern->kobj = (struct kobject){};
+	return access_pattern;
+}
+
+static int damon_sysfs_access_pattern_add_range_dir(
+		struct damon_sysfs_access_pattern *access_pattern,
+		struct damon_sysfs_ul_range **range_dir_ptr,
+		char *name)
+{
+	struct damon_sysfs_ul_range *range = damon_sysfs_ul_range_alloc(0, 0);
+	int err;
+
+	if (!range)
+		return -ENOMEM;
+	err = kobject_init_and_add(&range->kobj, &damon_sysfs_ul_range_ktype,
+			&access_pattern->kobj, name);
+	if (err)
+		kobject_put(&range->kobj);
+	else
+		*range_dir_ptr = range;
+	return err;
+}
+
+static int damon_sysfs_access_pattern_add_dirs(
+		struct damon_sysfs_access_pattern *access_pattern)
+{
+	int err;
+
+	err = damon_sysfs_access_pattern_add_range_dir(access_pattern,
+			&access_pattern->sz, "sz");
+	if (err)
+		goto put_sz_out;
+
+	err = damon_sysfs_access_pattern_add_range_dir(access_pattern,
+			&access_pattern->nr_accesses, "nr_accesses");
+	if (err)
+		goto put_nr_accesses_sz_out;
+
+	err = damon_sysfs_access_pattern_add_range_dir(access_pattern,
+			&access_pattern->age, "age");
+	if (err)
+		goto put_age_nr_accesses_sz_out;
+	return 0;
+
+put_age_nr_accesses_sz_out:
+	kobject_put(&access_pattern->age->kobj);
+	access_pattern->age = NULL;
+put_nr_accesses_sz_out:
+	kobject_put(&access_pattern->nr_accesses->kobj);
+	access_pattern->nr_accesses = NULL;
+put_sz_out:
+	kobject_put(&access_pattern->sz->kobj);
+	access_pattern->sz = NULL;
+	return err;
+}
+
+static void damon_sysfs_access_pattern_rm_dirs(
+		struct damon_sysfs_access_pattern *access_pattern)
+{
+	kobject_put(&access_pattern->sz->kobj);
+	kobject_put(&access_pattern->nr_accesses->kobj);
+	kobject_put(&access_pattern->age->kobj);
+}
+
+static void damon_sysfs_access_pattern_release(struct kobject *kobj)
+{
+	kfree(container_of(kobj, struct damon_sysfs_access_pattern, kobj));
+}
+
+static struct attribute *damon_sysfs_access_pattern_attrs[] = {
+	NULL,
+};
+ATTRIBUTE_GROUPS(damon_sysfs_access_pattern);
+
+static struct kobj_type damon_sysfs_access_pattern_ktype = {
+	.release = damon_sysfs_access_pattern_release,
+	.sysfs_ops = &kobj_sysfs_ops,
+	.default_groups = damon_sysfs_access_pattern_groups,
+};
+
+/*
+ * scheme directory
+ */
+
+struct damon_sysfs_scheme {
+	struct kobject kobj;
+	enum damos_action action;
+	struct damon_sysfs_access_pattern *access_pattern;
+};
+
+/* This should match with enum damos_action */
+static const char * const damon_sysfs_damos_action_strs[] = {
+	"willneed",
+	"cold",
+	"pageout",
+	"hugepage",
+	"nohugepage",
+	"stat",
+};
+
+static struct damon_sysfs_scheme *damon_sysfs_scheme_alloc(
+		enum damos_action action)
+{
+	struct damon_sysfs_scheme *scheme = kmalloc(sizeof(*scheme),
+				GFP_KERNEL);
+
+	if (!scheme)
+		return NULL;
+	scheme->kobj = (struct kobject){};
+	scheme->action = action;
+	return scheme;
+}
+
+static int damon_sysfs_scheme_set_access_pattern(
+		struct damon_sysfs_scheme *scheme)
+{
+	struct damon_sysfs_access_pattern *access_pattern;
+	int err;
+
+	access_pattern = damon_sysfs_access_pattern_alloc();
+	if (!access_pattern)
+		return -ENOMEM;
+	err = kobject_init_and_add(&access_pattern->kobj,
+			&damon_sysfs_access_pattern_ktype, &scheme->kobj,
+			"access_pattern");
+	if (err)
+		goto out;
+	err = damon_sysfs_access_pattern_add_dirs(access_pattern);
+	if (err)
+		goto out;
+	scheme->access_pattern = access_pattern;
+	return 0;
+
+out:
+	kobject_put(&access_pattern->kobj);
+	return err;
+}
+
+static int damon_sysfs_scheme_add_dirs(struct damon_sysfs_scheme *scheme)
+{
+	int err;
+
+	err = damon_sysfs_scheme_set_access_pattern(scheme);
+	if (err)
+		return err;
+	return 0;
+}
+
+static void damon_sysfs_scheme_rm_dirs(struct damon_sysfs_scheme *scheme)
+{
+	damon_sysfs_access_pattern_rm_dirs(scheme->access_pattern);
+	kobject_put(&scheme->access_pattern->kobj);
+}
+
+static ssize_t damon_sysfs_scheme_action_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	struct damon_sysfs_scheme *scheme = container_of(kobj,
+			struct damon_sysfs_scheme, kobj);
+
+	return sysfs_emit(buf, "%s\n",
+			damon_sysfs_damos_action_strs[scheme->action]);
+}
+
+static ssize_t damon_sysfs_scheme_action_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	struct damon_sysfs_scheme *scheme = container_of(kobj,
+			struct damon_sysfs_scheme, kobj);
+	enum damos_action action;
+
+	for (action = 0; action < NR_DAMOS_ACTIONS; action++) {
+		if (sysfs_streq(buf, damon_sysfs_damos_action_strs[action])) {
+			scheme->action = action;
+			return count;
+		}
+	}
+	return -EINVAL;
+}
+
+static void damon_sysfs_scheme_release(struct kobject *kobj)
+{
+	kfree(container_of(kobj, struct damon_sysfs_scheme, kobj));
+}
+
+static struct kobj_attribute damon_sysfs_scheme_action_attr = __ATTR(
+		action, 0600, damon_sysfs_scheme_action_show,
+		damon_sysfs_scheme_action_store);
+
+
+static struct attribute *damon_sysfs_scheme_attrs[] = {
+	&damon_sysfs_scheme_action_attr.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(damon_sysfs_scheme);
+
+static struct kobj_type damon_sysfs_scheme_ktype = {
+	.release = damon_sysfs_scheme_release,
+	.sysfs_ops = &kobj_sysfs_ops,
+	.default_groups = damon_sysfs_scheme_groups,
+};
+
+/*
+ * schemes directory
+ */
+
+struct damon_sysfs_schemes {
+	struct kobject kobj;
+	struct damon_sysfs_scheme **schemes_arr;
+	int nr;
+};
+
+static struct damon_sysfs_schemes *damon_sysfs_schemes_alloc(void)
+{
+	return kzalloc(sizeof(struct damon_sysfs_schemes), GFP_KERNEL);
+}
+
+static void damon_sysfs_schemes_rm_dirs(struct damon_sysfs_schemes *schemes)
+{
+	struct damon_sysfs_scheme **schemes_arr = schemes->schemes_arr;
+	int i;
+
+	for (i = 0; i < schemes->nr; i++) {
+		damon_sysfs_scheme_rm_dirs(schemes_arr[i]);
+		kobject_put(&schemes_arr[i]->kobj);
+	}
+	schemes->nr = 0;
+	kfree(schemes_arr);
+	schemes->schemes_arr = NULL;
+}
+
+static int damon_sysfs_schemes_add_dirs(struct damon_sysfs_schemes *schemes,
+		int nr_schemes)
+{
+	struct damon_sysfs_scheme **schemes_arr, *scheme;
+	int err, i;
+
+	damon_sysfs_schemes_rm_dirs(schemes);
+	if (!nr_schemes)
+		return 0;
+
+	schemes_arr = kmalloc_array(nr_schemes, sizeof(*schemes_arr),
+			GFP_KERNEL | __GFP_NOWARN);
+	if (!schemes_arr)
+		return -ENOMEM;
+	schemes->schemes_arr = schemes_arr;
+
+	for (i = 0; i < nr_schemes; i++) {
+		scheme = damon_sysfs_scheme_alloc(DAMOS_STAT);
+		if (!scheme) {
+			damon_sysfs_schemes_rm_dirs(schemes);
+			return -ENOMEM;
+		}
+
+		err = kobject_init_and_add(&scheme->kobj,
+				&damon_sysfs_scheme_ktype, &schemes->kobj,
+				"%d", i);
+		if (err)
+			goto out;
+		err = damon_sysfs_scheme_add_dirs(scheme);
+		if (err)
+			goto out;
+
+		schemes_arr[i] = scheme;
+		schemes->nr++;
+	}
+	return 0;
+
+out:
+	damon_sysfs_schemes_rm_dirs(schemes);
+	kobject_put(&scheme->kobj);
+	return err;
+}
+
+static ssize_t damon_sysfs_schemes_nr_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	struct damon_sysfs_schemes *schemes = container_of(kobj,
+			struct damon_sysfs_schemes, kobj);
+
+	return sysfs_emit(buf, "%d\n", schemes->nr);
+}
+
+static ssize_t damon_sysfs_schemes_nr_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	struct damon_sysfs_schemes *schemes = container_of(kobj,
+			struct damon_sysfs_schemes, kobj);
+	int nr, err = kstrtoint(buf, 0, &nr);
+
+	if (err)
+		return err;
+	if (nr < 0)
+		return -EINVAL;
+
+	if (!mutex_trylock(&damon_sysfs_lock))
+		return -EBUSY;
+	err = damon_sysfs_schemes_add_dirs(schemes, nr);
+	mutex_unlock(&damon_sysfs_lock);
+	if (err)
+		return err;
+	return count;
+}
+
+static void damon_sysfs_schemes_release(struct kobject *kobj)
+{
+	kfree(container_of(kobj, struct damon_sysfs_schemes, kobj));
+}
+
+static struct kobj_attribute damon_sysfs_schemes_nr_attr = __ATTR(nr, 0600,
+		damon_sysfs_schemes_nr_show, damon_sysfs_schemes_nr_store);
+
+static struct attribute *damon_sysfs_schemes_attrs[] = {
+	&damon_sysfs_schemes_nr_attr.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(damon_sysfs_schemes);
+
+static struct kobj_type damon_sysfs_schemes_ktype = {
+	.release = damon_sysfs_schemes_release,
+	.sysfs_ops = &kobj_sysfs_ops,
+	.default_groups = damon_sysfs_schemes_groups,
+};
+
 /*
  * init region directory
  */
@@ -756,6 +1099,7 @@ struct damon_sysfs_context {
 	enum damon_ops_id ops_id;
 	struct damon_sysfs_attrs *attrs;
 	struct damon_sysfs_targets *targets;
+	struct damon_sysfs_schemes *schemes;
 };
 
 static struct damon_sysfs_context *damon_sysfs_context_alloc(
@@ -810,6 +1154,23 @@ static int damon_sysfs_context_set_targets(struct damon_sysfs_context *context)
 	return 0;
 }
 
+static int damon_sysfs_context_set_schemes(struct damon_sysfs_context *context)
+{
+	struct damon_sysfs_schemes *schemes = damon_sysfs_schemes_alloc();
+	int err;
+
+	if (!schemes)
+		return -ENOMEM;
+	err = kobject_init_and_add(&schemes->kobj, &damon_sysfs_schemes_ktype,
+			&context->kobj, "schemes");
+	if (err) {
+		kobject_put(&schemes->kobj);
+		return err;
+	}
+	context->schemes = schemes;
+	return 0;
+}
+
 static int damon_sysfs_context_add_dirs(struct damon_sysfs_context *context)
 {
 	int err;
@@ -822,8 +1183,14 @@ static int damon_sysfs_context_add_dirs(struct damon_sysfs_context *context)
 	if (err)
 		goto put_attrs_out;
 
+	err = damon_sysfs_context_set_schemes(context);
+	if (err)
+		goto put_targets_attrs_out;
 	return 0;
 
+put_targets_attrs_out:
+	kobject_put(&context->targets->kobj);
+	context->targets = NULL;
 put_attrs_out:
 	kobject_put(&context->attrs->kobj);
 	context->attrs = NULL;
@@ -836,6 +1203,8 @@ static void damon_sysfs_context_rm_dirs(struct damon_sysfs_context *context)
 	kobject_put(&context->attrs->kobj);
 	damon_sysfs_targets_rm_dirs(context->targets);
 	kobject_put(&context->targets->kobj);
+	damon_sysfs_schemes_rm_dirs(context->schemes);
+	kobject_put(&context->schemes->kobj);
 }
 
 static ssize_t damon_sysfs_context_operations_show(struct kobject *kobj,
@@ -1159,6 +1528,52 @@ static int damon_sysfs_set_targets(struct damon_ctx *ctx,
 	return 0;
 }
 
+static struct damos *damon_sysfs_mk_scheme(
+		struct damon_sysfs_scheme *sysfs_scheme)
+{
+	struct damon_sysfs_access_pattern *pattern =
+		sysfs_scheme->access_pattern;
+	struct damos_quota quota = {
+		.ms = 0,
+		.sz = 0,
+		.reset_interval = 0,
+		.weight_sz = 1000,
+		.weight_nr_accesses = 1000,
+		.weight_age = 1000,
+	};
+	struct damos_watermarks wmarks = {
+		.metric = DAMOS_WMARK_NONE,
+		.interval = 0,
+		.high = 0,
+		.mid = 0,
+		.low = 0,
+	};
+
+	return damon_new_scheme(pattern->sz->min, pattern->sz->max,
+			pattern->nr_accesses->min, pattern->nr_accesses->max,
+			pattern->age->min, pattern->age->max,
+			sysfs_scheme->action, &quota, &wmarks);
+}
+
+static int damon_sysfs_set_schemes(struct damon_ctx *ctx,
+		struct damon_sysfs_schemes *sysfs_schemes)
+{
+	int i;
+
+	for (i = 0; i < sysfs_schemes->nr; i++) {
+		struct damos *scheme, *next;
+
+		scheme = damon_sysfs_mk_scheme(sysfs_schemes->schemes_arr[i]);
+		if (!scheme) {
+			damon_for_each_scheme_safe(scheme, next, ctx)
+				damon_destroy_scheme(scheme);
+			return -ENOMEM;
+		}
+		damon_add_scheme(ctx, scheme);
+	}
+	return 0;
+}
+
 static void damon_sysfs_before_terminate(struct damon_ctx *ctx)
 {
 	struct damon_target *t, *next;
@@ -1190,6 +1605,9 @@ static struct damon_ctx *damon_sysfs_build_ctx(
 	if (err)
 		goto out;
 	err = damon_sysfs_set_targets(ctx, sys_ctx->targets);
+	if (err)
+		goto out;
+	err = damon_sysfs_set_schemes(ctx, sys_ctx->schemes);
 	if (err)
 		goto out;
 
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH 07/12] mm/damon/sysfs: Support DAMOS quotas
  2022-02-23 15:20 [PATCH 00/12] Introduce DAMON sysfs interface SeongJae Park
                   ` (5 preceding siblings ...)
  2022-02-23 15:20 ` [PATCH 06/12] mm/damon/sysfs: Support DAMON-based Operation Schemes SeongJae Park
@ 2022-02-23 15:20 ` SeongJae Park
  2022-02-23 15:20 ` [PATCH 08/12] mm/damon/sysfs: Support schemes prioritization weights SeongJae Park
                   ` (6 subsequent siblings)
  13 siblings, 0 replies; 24+ messages in thread
From: SeongJae Park @ 2022-02-23 15:20 UTC (permalink / raw)
  To: akpm
  Cc: corbet, skhan, rientjes, xhao, linux-damon, linux-mm, linux-doc,
	linux-kselftest, linux-kernel, SeongJae Park

This commit makes DAMON sysfs interface supports the DAMOS quotas
feature.  Specifically, this commit adds 'quotas' directory under each
scheme directory and makes kdamond 'state' file writing respects the
contents in the directory.

As a result, the files hierarchy becomes as below:

    /sys/kernel/mm/damon/admin
    │ kdamonds/nr
    │ │ 0/state,pid
    │ │ │ contexts/nr
    │ │ │ │ 0/operations
    │ │ │ │ │ monitoring_attrs/intervals/sample_us,aggr_us,update_us
    │ │ │ │ │ │ nr_regions/min,max
    │ │ │ │ │ targets/nr
    │ │ │ │ │ │ 0/pid
    │ │ │ │ │ │ │ regions/nr
    │ │ │ │ │ │ │ │ 0/start,end
    │ │ │ │ │ │ │ │ ...
    │ │ │ │ │ │ ...
    │ │ │ │ │ schemes/nr
    │ │ │ │ │ │ 0/action
    │ │ │ │ │ │ │ access_pattern/
    │ │ │ │ │ │ │ │ sz/min,max
    │ │ │ │ │ │ │ │ nr_accesses/min,max
    │ │ │ │ │ │ │ │ age/min,max
    │ │ │ │ │ │ │ quotas/ms,sz,reset_interval_ms    <- NEW DIRECTORY
    │ │ │ │ │ │ ...
    │ │ │ │ ...
    │ │ ...

Signed-off-by: SeongJae Park <sj@kernel.org>
---
 mm/damon/sysfs.c | 143 ++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 140 insertions(+), 3 deletions(-)

diff --git a/mm/damon/sysfs.c b/mm/damon/sysfs.c
index 2350a637e662..a14caeb5b6a1 100644
--- a/mm/damon/sysfs.c
+++ b/mm/damon/sysfs.c
@@ -115,6 +115,115 @@ static struct kobj_type damon_sysfs_ul_range_ktype = {
 	.default_groups = damon_sysfs_ul_range_groups,
 };
 
+/*
+ * quotas directory
+ */
+
+struct damon_sysfs_quotas {
+	struct kobject kobj;
+	unsigned long ms;
+	unsigned long sz;
+	unsigned long reset_interval_ms;
+};
+
+static struct damon_sysfs_quotas *damon_sysfs_quotas_alloc(void)
+{
+	return kzalloc(sizeof(struct damon_sysfs_quotas), GFP_KERNEL);
+}
+
+static ssize_t damon_sysfs_quotas_ms_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	struct damon_sysfs_quotas *quotas = container_of(kobj,
+			struct damon_sysfs_quotas, kobj);
+
+	return sysfs_emit(buf, "%lu\n", quotas->ms);
+}
+
+static ssize_t damon_sysfs_quotas_ms_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	struct damon_sysfs_quotas *quotas = container_of(kobj,
+			struct damon_sysfs_quotas, kobj);
+	int err = kstrtoul(buf, 0, &quotas->ms);
+
+	if (err)
+		return -EINVAL;
+	return count;
+}
+
+static ssize_t damon_sysfs_quotas_sz_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	struct damon_sysfs_quotas *quotas = container_of(kobj,
+			struct damon_sysfs_quotas, kobj);
+
+	return sysfs_emit(buf, "%lu\n", quotas->sz);
+}
+
+static ssize_t damon_sysfs_quotas_sz_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	struct damon_sysfs_quotas *quotas = container_of(kobj,
+			struct damon_sysfs_quotas, kobj);
+	int err = kstrtoul(buf, 0, &quotas->sz);
+
+	if (err)
+		return -EINVAL;
+	return count;
+}
+
+static ssize_t damon_sysfs_quotas_reset_interval_ms_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	struct damon_sysfs_quotas *quotas = container_of(kobj,
+			struct damon_sysfs_quotas, kobj);
+
+	return sysfs_emit(buf, "%lu\n", quotas->reset_interval_ms);
+}
+
+static ssize_t damon_sysfs_quotas_reset_interval_ms_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	struct damon_sysfs_quotas *quotas = container_of(kobj,
+			struct damon_sysfs_quotas, kobj);
+	int err = kstrtoul(buf, 0, &quotas->reset_interval_ms);
+
+	if (err)
+		return -EINVAL;
+	return count;
+}
+
+static void damon_sysfs_quotas_release(struct kobject *kobj)
+{
+	kfree(container_of(kobj, struct damon_sysfs_quotas, kobj));
+}
+
+static struct kobj_attribute damon_sysfs_quotas_ms_attr = __ATTR(ms, 0600,
+		damon_sysfs_quotas_ms_show, damon_sysfs_quotas_ms_store);
+
+static struct kobj_attribute damon_sysfs_quotas_sz_attr = __ATTR(sz, 0600,
+		damon_sysfs_quotas_sz_show, damon_sysfs_quotas_sz_store);
+
+static struct kobj_attribute damon_sysfs_quotas_reset_interval_ms_attr =
+		__ATTR(reset_interval_ms, 0600,
+			damon_sysfs_quotas_reset_interval_ms_show,
+			damon_sysfs_quotas_reset_interval_ms_store);
+
+static struct attribute *damon_sysfs_quotas_attrs[] = {
+	&damon_sysfs_quotas_ms_attr.attr,
+	&damon_sysfs_quotas_sz_attr.attr,
+	&damon_sysfs_quotas_reset_interval_ms_attr.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(damon_sysfs_quotas);
+
+static struct kobj_type damon_sysfs_quotas_ktype = {
+	.release = damon_sysfs_quotas_release,
+	.sysfs_ops = &kobj_sysfs_ops,
+	.default_groups = damon_sysfs_quotas_groups,
+};
+
 /*
  * access_pattern directory
  */
@@ -222,6 +331,7 @@ struct damon_sysfs_scheme {
 	struct kobject kobj;
 	enum damos_action action;
 	struct damon_sysfs_access_pattern *access_pattern;
+	struct damon_sysfs_quotas *quotas;
 };
 
 /* This should match with enum damos_action */
@@ -272,6 +382,24 @@ static int damon_sysfs_scheme_set_access_pattern(
 	return err;
 }
 
+static int damon_sysfs_scheme_set_quotas(struct damon_sysfs_scheme *scheme)
+{
+	struct damon_sysfs_quotas *quotas = damon_sysfs_quotas_alloc();
+	int err;
+
+	if (!quotas)
+		return -ENOMEM;
+	err = kobject_init_and_add(&quotas->kobj, &damon_sysfs_quotas_ktype,
+			&scheme->kobj, "quotas");
+	if (err)
+		goto out;
+	return 0;
+
+out:
+	kobject_put(&quotas->kobj);
+	return err;
+}
+
 static int damon_sysfs_scheme_add_dirs(struct damon_sysfs_scheme *scheme)
 {
 	int err;
@@ -279,13 +407,22 @@ static int damon_sysfs_scheme_add_dirs(struct damon_sysfs_scheme *scheme)
 	err = damon_sysfs_scheme_set_access_pattern(scheme);
 	if (err)
 		return err;
+	err = damon_sysfs_scheme_set_quotas(scheme);
+	if (err)
+		goto put_access_pattern_out;
 	return 0;
+
+put_access_pattern_out:
+	kobject_put(&scheme->access_pattern->kobj);
+	scheme->access_pattern = NULL;
+	return err;
 }
 
 static void damon_sysfs_scheme_rm_dirs(struct damon_sysfs_scheme *scheme)
 {
 	damon_sysfs_access_pattern_rm_dirs(scheme->access_pattern);
 	kobject_put(&scheme->access_pattern->kobj);
+	kobject_put(&scheme->quotas->kobj);
 }
 
 static ssize_t damon_sysfs_scheme_action_show(struct kobject *kobj,
@@ -1533,10 +1670,10 @@ static struct damos *damon_sysfs_mk_scheme(
 {
 	struct damon_sysfs_access_pattern *pattern =
 		sysfs_scheme->access_pattern;
+	struct damon_sysfs_quotas *sysfs_quotas = sysfs_scheme->quotas;
 	struct damos_quota quota = {
-		.ms = 0,
-		.sz = 0,
-		.reset_interval = 0,
+		.ms = sysfs_quotas->ms,
+		.sz = sysfs_quotas->sz,
 		.weight_sz = 1000,
 		.weight_nr_accesses = 1000,
 		.weight_age = 1000,
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH 08/12] mm/damon/sysfs: Support schemes prioritization weights
  2022-02-23 15:20 [PATCH 00/12] Introduce DAMON sysfs interface SeongJae Park
                   ` (6 preceding siblings ...)
  2022-02-23 15:20 ` [PATCH 07/12] mm/damon/sysfs: Support DAMOS quotas SeongJae Park
@ 2022-02-23 15:20 ` SeongJae Park
  2022-02-23 15:20 ` [PATCH 09/12] mm/damon/sysfs: Support DAMOS watermarks SeongJae Park
                   ` (5 subsequent siblings)
  13 siblings, 0 replies; 24+ messages in thread
From: SeongJae Park @ 2022-02-23 15:20 UTC (permalink / raw)
  To: akpm
  Cc: corbet, skhan, rientjes, xhao, linux-damon, linux-mm, linux-doc,
	linux-kselftest, linux-kernel, SeongJae Park

This commit makes DAMON sysfs interface supports the DAMOS' regions
prioritization weights feature under quotas limitation.  Specifically,
this commit adds 'weights' directory under each scheme directory and
makes kdamond 'state' file writing respects the contents in the
directory.

    /sys/kernel/mm/damon/admin
    │ kdamonds/nr
    │ │ 0/state,pid
    │ │ │ contexts/nr
    │ │ │ │ 0/operations
    │ │ │ │ │ monitoring_attrs/intervals/sample_us,aggr_us,update_us
    │ │ │ │ │ │ nr_regions/min,max
    │ │ │ │ │ targets/nr
    │ │ │ │ │ │ 0/pid
    │ │ │ │ │ │ │ regions/nr
    │ │ │ │ │ │ │ │ 0/start,end
    │ │ │ │ │ │ │ │ ...
    │ │ │ │ │ │ ...
    │ │ │ │ │ schemes/nr
    │ │ │ │ │ │ 0/action
    │ │ │ │ │ │ │ access_pattern/
    │ │ │ │ │ │ │ │ sz/min,max
    │ │ │ │ │ │ │ │ nr_accesses/min,max
    │ │ │ │ │ │ │ │ age/min,max
    │ │ │ │ │ │ │ quotas/ms,sz,reset_interval_ms
    │ │ │ │ │ │ │ │ weights/sz,nr_accesses,age    <- NEW DIRECTORY
    │ │ │ │ │ │ ...
    │ │ │ │ ...
    │ │ ...

Signed-off-by: SeongJae Park <sj@kernel.org>
---
 mm/damon/sysfs.c | 157 ++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 154 insertions(+), 3 deletions(-)

diff --git a/mm/damon/sysfs.c b/mm/damon/sysfs.c
index a14caeb5b6a1..b8596908be7b 100644
--- a/mm/damon/sysfs.c
+++ b/mm/damon/sysfs.c
@@ -115,12 +115,133 @@ static struct kobj_type damon_sysfs_ul_range_ktype = {
 	.default_groups = damon_sysfs_ul_range_groups,
 };
 
+/*
+ * scheme/weights directory
+ */
+
+struct damon_sysfs_weights {
+	struct kobject kobj;
+	unsigned int sz;
+	unsigned int nr_accesses;
+	unsigned int age;
+};
+
+static struct damon_sysfs_weights *damon_sysfs_weights_alloc(unsigned int sz,
+		unsigned int nr_accesses, unsigned int age)
+{
+	struct damon_sysfs_weights *weights = kmalloc(sizeof(*weights),
+			GFP_KERNEL);
+
+	if (!weights)
+		return NULL;
+	weights->kobj = (struct kobject){};
+	weights->sz = sz;
+	weights->nr_accesses = nr_accesses;
+	weights->age = age;
+	return weights;
+}
+
+static ssize_t damon_sysfs_weights_sz_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	struct damon_sysfs_weights *weights = container_of(kobj,
+			struct damon_sysfs_weights, kobj);
+
+	return sysfs_emit(buf, "%u\n", weights->sz);
+}
+
+static ssize_t damon_sysfs_weights_sz_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	struct damon_sysfs_weights *weights = container_of(kobj,
+			struct damon_sysfs_weights, kobj);
+	int err = kstrtouint(buf, 0, &weights->sz);
+
+	if (err)
+		return -EINVAL;
+	return count;
+}
+
+static ssize_t damon_sysfs_weights_nr_accesses_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	struct damon_sysfs_weights *weights = container_of(kobj,
+			struct damon_sysfs_weights, kobj);
+
+	return sysfs_emit(buf, "%u\n", weights->nr_accesses);
+}
+
+static ssize_t damon_sysfs_weights_nr_accesses_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	struct damon_sysfs_weights *weights = container_of(kobj,
+			struct damon_sysfs_weights, kobj);
+	int err = kstrtouint(buf, 0, &weights->nr_accesses);
+
+	if (err)
+		return -EINVAL;
+	return count;
+}
+
+static ssize_t damon_sysfs_weights_age_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	struct damon_sysfs_weights *weights = container_of(kobj,
+			struct damon_sysfs_weights, kobj);
+
+	return sysfs_emit(buf, "%u\n", weights->age);
+}
+
+static ssize_t damon_sysfs_weights_age_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	struct damon_sysfs_weights *weights = container_of(kobj,
+			struct damon_sysfs_weights, kobj);
+	int err = kstrtouint(buf, 0, &weights->age);
+
+	if (err)
+		return -EINVAL;
+	return count;
+}
+
+static void damon_sysfs_weights_release(struct kobject *kobj)
+{
+	kfree(container_of(kobj, struct damon_sysfs_weights, kobj));
+}
+
+static struct kobj_attribute damon_sysfs_weights_sz_attr =
+		__ATTR(sz, 0600, damon_sysfs_weights_sz_show,
+				damon_sysfs_weights_sz_store);
+
+static struct kobj_attribute damon_sysfs_weights_nr_accesses_attr =
+		__ATTR(nr_accesses, 0600, damon_sysfs_weights_nr_accesses_show,
+				damon_sysfs_weights_nr_accesses_store);
+
+static struct kobj_attribute damon_sysfs_weights_age_attr =
+		__ATTR(age, 0600, damon_sysfs_weights_age_show,
+				damon_sysfs_weights_age_store);
+
+static struct attribute *damon_sysfs_weights_attrs[] = {
+	&damon_sysfs_weights_sz_attr.attr,
+	&damon_sysfs_weights_nr_accesses_attr.attr,
+	&damon_sysfs_weights_age_attr.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(damon_sysfs_weights);
+
+static struct kobj_type damon_sysfs_weights_ktype = {
+	.release = damon_sysfs_weights_release,
+	.sysfs_ops = &kobj_sysfs_ops,
+	.default_groups = damon_sysfs_weights_groups,
+};
+
 /*
  * quotas directory
  */
 
 struct damon_sysfs_quotas {
 	struct kobject kobj;
+	struct damon_sysfs_weights *weights;
 	unsigned long ms;
 	unsigned long sz;
 	unsigned long reset_interval_ms;
@@ -131,6 +252,29 @@ static struct damon_sysfs_quotas *damon_sysfs_quotas_alloc(void)
 	return kzalloc(sizeof(struct damon_sysfs_quotas), GFP_KERNEL);
 }
 
+static int damon_sysfs_quotas_add_dirs(struct damon_sysfs_quotas *quotas)
+{
+	struct damon_sysfs_weights *weights;
+	int err;
+
+	weights = damon_sysfs_weights_alloc(0, 0, 0);
+	if (!weights)
+		return -ENOMEM;
+
+	err = kobject_init_and_add(&weights->kobj, &damon_sysfs_weights_ktype,
+			&quotas->kobj, "weights");
+	if (err)
+		kobject_put(&weights->kobj);
+	else
+		quotas->weights = weights;
+	return err;
+}
+
+static void damon_sysfs_quotas_rm_dirs(struct damon_sysfs_quotas *quotas)
+{
+	kobject_put(&quotas->weights->kobj);
+}
+
 static ssize_t damon_sysfs_quotas_ms_show(struct kobject *kobj,
 		struct kobj_attribute *attr, char *buf)
 {
@@ -393,6 +537,10 @@ static int damon_sysfs_scheme_set_quotas(struct damon_sysfs_scheme *scheme)
 			&scheme->kobj, "quotas");
 	if (err)
 		goto out;
+	err = damon_sysfs_quotas_add_dirs(quotas);
+	if (err)
+		goto out;
+	scheme->quotas = quotas;
 	return 0;
 
 out:
@@ -422,6 +570,7 @@ static void damon_sysfs_scheme_rm_dirs(struct damon_sysfs_scheme *scheme)
 {
 	damon_sysfs_access_pattern_rm_dirs(scheme->access_pattern);
 	kobject_put(&scheme->access_pattern->kobj);
+	damon_sysfs_quotas_rm_dirs(scheme->quotas);
 	kobject_put(&scheme->quotas->kobj);
 }
 
@@ -1671,12 +1820,14 @@ static struct damos *damon_sysfs_mk_scheme(
 	struct damon_sysfs_access_pattern *pattern =
 		sysfs_scheme->access_pattern;
 	struct damon_sysfs_quotas *sysfs_quotas = sysfs_scheme->quotas;
+	struct damon_sysfs_weights *sysfs_weights = sysfs_quotas->weights;
 	struct damos_quota quota = {
 		.ms = sysfs_quotas->ms,
 		.sz = sysfs_quotas->sz,
-		.weight_sz = 1000,
-		.weight_nr_accesses = 1000,
-		.weight_age = 1000,
+		.reset_interval = sysfs_quotas->reset_interval_ms,
+		.weight_sz = sysfs_weights->sz,
+		.weight_nr_accesses = sysfs_weights->nr_accesses,
+		.weight_age = sysfs_weights->age,
 	};
 	struct damos_watermarks wmarks = {
 		.metric = DAMOS_WMARK_NONE,
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH 09/12] mm/damon/sysfs: Support DAMOS watermarks
  2022-02-23 15:20 [PATCH 00/12] Introduce DAMON sysfs interface SeongJae Park
                   ` (7 preceding siblings ...)
  2022-02-23 15:20 ` [PATCH 08/12] mm/damon/sysfs: Support schemes prioritization weights SeongJae Park
@ 2022-02-23 15:20 ` SeongJae Park
  2022-02-23 15:20 ` [PATCH 10/12] mm/damon/sysfs: Support DAMOS stats SeongJae Park
                   ` (4 subsequent siblings)
  13 siblings, 0 replies; 24+ messages in thread
From: SeongJae Park @ 2022-02-23 15:20 UTC (permalink / raw)
  To: akpm
  Cc: corbet, skhan, rientjes, xhao, linux-damon, linux-mm, linux-doc,
	linux-kselftest, linux-kernel, SeongJae Park

This commit makes DAMON sysfs interface supports the DAMOS watermarks
feature.  Specifically, this commit adds 'watermarks' directory under
each scheme directory and makes kdamond 'state' file writing respects
the contents in the directory.

As a result, the files hierarchy becomes as below:

    /sys/kernel/mm/damon/admin
    │ kdamonds/nr
    │ │ 0/state,pid
    │ │ │ contexts/nr
    │ │ │ │ 0/operations
    │ │ │ │ │ monitoring_attrs/intervals/sample_us,aggr_us,update_us
    │ │ │ │ │ │ nr_regions/min,max
    │ │ │ │ │ targets/nr
    │ │ │ │ │ │ 0/pid
    │ │ │ │ │ │ │ regions/nr
    │ │ │ │ │ │ │ │ 0/start,end
    │ │ │ │ │ │ │ │ ...
    │ │ │ │ │ │ ...
    │ │ │ │ │ schemes/nr
    │ │ │ │ │ │ 0/action
    │ │ │ │ │ │ │ access_pattern/
    │ │ │ │ │ │ │ │ sz/min,max
    │ │ │ │ │ │ │ │ nr_accesses/min,max
    │ │ │ │ │ │ │ │ age/min,max
    │ │ │ │ │ │ │ quotas/ms,sz,reset_interval_ms
    │ │ │ │ │ │ │ │ weights/sz,nr_accesses,age
    │ │ │ │ │ │ │ watermarks/    <- NEW DIRECTORY
    │ │ │ │ │ │ │ │ metric,interval_us,high,mid,lo
    │ │ │ │ │ │ ...
    │ │ │ │ ...
    │ │ ...

Signed-off-by: SeongJae Park <sj@kernel.org>
---
 mm/damon/sysfs.c | 226 +++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 221 insertions(+), 5 deletions(-)

diff --git a/mm/damon/sysfs.c b/mm/damon/sysfs.c
index b8596908be7b..9e54ed7316c6 100644
--- a/mm/damon/sysfs.c
+++ b/mm/damon/sysfs.c
@@ -115,6 +115,195 @@ static struct kobj_type damon_sysfs_ul_range_ktype = {
 	.default_groups = damon_sysfs_ul_range_groups,
 };
 
+/*
+ * watermarks directory
+ */
+
+struct damon_sysfs_watermarks {
+	struct kobject kobj;
+	enum damos_wmark_metric metric;
+	unsigned long interval_us;
+	unsigned long high;
+	unsigned long mid;
+	unsigned long low;
+};
+
+static struct damon_sysfs_watermarks *damon_sysfs_watermarks_alloc(
+		enum damos_wmark_metric metric, unsigned long interval_us,
+		unsigned long high, unsigned long mid, unsigned long low)
+{
+	struct damon_sysfs_watermarks *watermarks = kmalloc(
+			sizeof(*watermarks), GFP_KERNEL);
+
+	if (!watermarks)
+		return NULL;
+	watermarks->kobj = (struct kobject){};
+	watermarks->metric = metric;
+	watermarks->interval_us = interval_us;
+	watermarks->high = high;
+	watermarks->mid = mid;
+	watermarks->low = low;
+	return watermarks;
+}
+
+/* Should match with enum damos_wmark_metric */
+static const char * const damon_sysfs_wmark_metric_strs[] = {
+	"none",
+	"free_mem_rate",
+};
+
+static ssize_t damon_sysfs_watermarks_metric_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	struct damon_sysfs_watermarks *watermarks = container_of(kobj,
+			struct damon_sysfs_watermarks, kobj);
+
+	return sysfs_emit(buf, "%s\n",
+			damon_sysfs_wmark_metric_strs[watermarks->metric]);
+}
+
+static ssize_t damon_sysfs_watermarks_metric_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	struct damon_sysfs_watermarks *watermarks = container_of(kobj,
+			struct damon_sysfs_watermarks, kobj);
+	enum damos_wmark_metric metric;
+
+	for (metric = 0; metric <= NR_DAMOS_WMARK_METRICS; metric++) {
+		if (sysfs_streq(buf, damon_sysfs_wmark_metric_strs[metric])) {
+			watermarks->metric = metric;
+			return count;
+		}
+	}
+	return -EINVAL;
+}
+
+static ssize_t damon_sysfs_watermarks_interval_us_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	struct damon_sysfs_watermarks *watermarks = container_of(kobj,
+			struct damon_sysfs_watermarks, kobj);
+
+	return sysfs_emit(buf, "%lu\n", watermarks->interval_us);
+}
+
+static ssize_t damon_sysfs_watermarks_interval_us_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	struct damon_sysfs_watermarks *watermarks = container_of(kobj,
+			struct damon_sysfs_watermarks, kobj);
+	int err = kstrtoul(buf, 0, &watermarks->interval_us);
+
+	if (err)
+		return -EINVAL;
+	return count;
+}
+
+static ssize_t damon_sysfs_watermarks_high_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	struct damon_sysfs_watermarks *watermarks = container_of(kobj,
+			struct damon_sysfs_watermarks, kobj);
+
+	return sysfs_emit(buf, "%lu\n", watermarks->high);
+}
+
+static ssize_t damon_sysfs_watermarks_high_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	struct damon_sysfs_watermarks *watermarks = container_of(kobj,
+			struct damon_sysfs_watermarks, kobj);
+	int err = kstrtoul(buf, 0, &watermarks->high);
+
+	if (err)
+		return -EINVAL;
+	return count;
+}
+
+static ssize_t damon_sysfs_watermarks_mid_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	struct damon_sysfs_watermarks *watermarks = container_of(kobj,
+			struct damon_sysfs_watermarks, kobj);
+
+	return sysfs_emit(buf, "%lu\n", watermarks->mid);
+}
+
+static ssize_t damon_sysfs_watermarks_mid_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	struct damon_sysfs_watermarks *watermarks = container_of(kobj,
+			struct damon_sysfs_watermarks, kobj);
+	int err = kstrtoul(buf, 0, &watermarks->mid);
+
+	if (err)
+		return -EINVAL;
+	return count;
+}
+
+static ssize_t damon_sysfs_watermarks_low_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	struct damon_sysfs_watermarks *watermarks = container_of(kobj,
+			struct damon_sysfs_watermarks, kobj);
+
+	return sysfs_emit(buf, "%lu\n", watermarks->low);
+}
+
+static ssize_t damon_sysfs_watermarks_low_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	struct damon_sysfs_watermarks *watermarks = container_of(kobj,
+			struct damon_sysfs_watermarks, kobj);
+	int err = kstrtoul(buf, 0, &watermarks->low);
+
+	if (err)
+		return -EINVAL;
+	return count;
+}
+
+static void damon_sysfs_watermarks_release(struct kobject *kobj)
+{
+	kfree(container_of(kobj, struct damon_sysfs_watermarks, kobj));
+}
+
+static struct kobj_attribute damon_sysfs_watermarks_metric_attr =
+		__ATTR(metric, 0600, damon_sysfs_watermarks_metric_show,
+				damon_sysfs_watermarks_metric_store);
+
+static struct kobj_attribute damon_sysfs_watermarks_interval_us_attr =
+		__ATTR(interval_us, 0600,
+				damon_sysfs_watermarks_interval_us_show,
+				damon_sysfs_watermarks_interval_us_store);
+
+static struct kobj_attribute damon_sysfs_watermarks_high_attr =
+		__ATTR(high, 0600, damon_sysfs_watermarks_high_show,
+				damon_sysfs_watermarks_high_store);
+
+static struct kobj_attribute damon_sysfs_watermarks_mid_attr =
+		__ATTR(mid, 0600, damon_sysfs_watermarks_mid_show,
+				damon_sysfs_watermarks_mid_store);
+
+static struct kobj_attribute damon_sysfs_watermarks_low_attr =
+		__ATTR(low, 0600, damon_sysfs_watermarks_low_show,
+				damon_sysfs_watermarks_low_store);
+
+static struct attribute *damon_sysfs_watermarks_attrs[] = {
+	&damon_sysfs_watermarks_metric_attr.attr,
+	&damon_sysfs_watermarks_interval_us_attr.attr,
+	&damon_sysfs_watermarks_high_attr.attr,
+	&damon_sysfs_watermarks_mid_attr.attr,
+	&damon_sysfs_watermarks_low_attr.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(damon_sysfs_watermarks);
+
+static struct kobj_type damon_sysfs_watermarks_ktype = {
+	.release = damon_sysfs_watermarks_release,
+	.sysfs_ops = &kobj_sysfs_ops,
+	.default_groups = damon_sysfs_watermarks_groups,
+};
+
 /*
  * scheme/weights directory
  */
@@ -476,6 +665,7 @@ struct damon_sysfs_scheme {
 	enum damos_action action;
 	struct damon_sysfs_access_pattern *access_pattern;
 	struct damon_sysfs_quotas *quotas;
+	struct damon_sysfs_watermarks *watermarks;
 };
 
 /* This should match with enum damos_action */
@@ -548,6 +738,24 @@ static int damon_sysfs_scheme_set_quotas(struct damon_sysfs_scheme *scheme)
 	return err;
 }
 
+static int damon_sysfs_scheme_set_watermarks(struct damon_sysfs_scheme *scheme)
+{
+	struct damon_sysfs_watermarks *watermarks =
+		damon_sysfs_watermarks_alloc(DAMOS_WMARK_NONE, 0, 0, 0, 0);
+	int err;
+
+	if (!watermarks)
+		return -ENOMEM;
+	err = kobject_init_and_add(&watermarks->kobj,
+			&damon_sysfs_watermarks_ktype, &scheme->kobj,
+			"watermarks");
+	if (err)
+		kobject_put(&watermarks->kobj);
+	else
+		scheme->watermarks = watermarks;
+	return err;
+}
+
 static int damon_sysfs_scheme_add_dirs(struct damon_sysfs_scheme *scheme)
 {
 	int err;
@@ -558,8 +766,14 @@ static int damon_sysfs_scheme_add_dirs(struct damon_sysfs_scheme *scheme)
 	err = damon_sysfs_scheme_set_quotas(scheme);
 	if (err)
 		goto put_access_pattern_out;
+	err = damon_sysfs_scheme_set_watermarks(scheme);
+	if (err)
+		goto put_quotas_access_pattern_out;
 	return 0;
 
+put_quotas_access_pattern_out:
+	kobject_put(&scheme->quotas->kobj);
+	scheme->quotas = NULL;
 put_access_pattern_out:
 	kobject_put(&scheme->access_pattern->kobj);
 	scheme->access_pattern = NULL;
@@ -572,6 +786,7 @@ static void damon_sysfs_scheme_rm_dirs(struct damon_sysfs_scheme *scheme)
 	kobject_put(&scheme->access_pattern->kobj);
 	damon_sysfs_quotas_rm_dirs(scheme->quotas);
 	kobject_put(&scheme->quotas->kobj);
+	kobject_put(&scheme->watermarks->kobj);
 }
 
 static ssize_t damon_sysfs_scheme_action_show(struct kobject *kobj,
@@ -1821,6 +2036,7 @@ static struct damos *damon_sysfs_mk_scheme(
 		sysfs_scheme->access_pattern;
 	struct damon_sysfs_quotas *sysfs_quotas = sysfs_scheme->quotas;
 	struct damon_sysfs_weights *sysfs_weights = sysfs_quotas->weights;
+	struct damon_sysfs_watermarks *sysfs_wmarks = sysfs_scheme->watermarks;
 	struct damos_quota quota = {
 		.ms = sysfs_quotas->ms,
 		.sz = sysfs_quotas->sz,
@@ -1830,11 +2046,11 @@ static struct damos *damon_sysfs_mk_scheme(
 		.weight_age = sysfs_weights->age,
 	};
 	struct damos_watermarks wmarks = {
-		.metric = DAMOS_WMARK_NONE,
-		.interval = 0,
-		.high = 0,
-		.mid = 0,
-		.low = 0,
+		.metric = sysfs_wmarks->metric,
+		.interval = sysfs_wmarks->interval_us,
+		.high = sysfs_wmarks->high,
+		.mid = sysfs_wmarks->mid,
+		.low = sysfs_wmarks->low,
 	};
 
 	return damon_new_scheme(pattern->sz->min, pattern->sz->max,
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH 10/12] mm/damon/sysfs: Support DAMOS stats
  2022-02-23 15:20 [PATCH 00/12] Introduce DAMON sysfs interface SeongJae Park
                   ` (8 preceding siblings ...)
  2022-02-23 15:20 ` [PATCH 09/12] mm/damon/sysfs: Support DAMOS watermarks SeongJae Park
@ 2022-02-23 15:20 ` SeongJae Park
  2022-02-23 15:20 ` [PATCH 11/12] selftests/damon: Add a test for DAMON sysfs interface SeongJae Park
                   ` (3 subsequent siblings)
  13 siblings, 0 replies; 24+ messages in thread
From: SeongJae Park @ 2022-02-23 15:20 UTC (permalink / raw)
  To: akpm
  Cc: corbet, skhan, rientjes, xhao, linux-damon, linux-mm, linux-doc,
	linux-kselftest, linux-kernel, SeongJae Park

This commit makes DAMON sysfs interface supports the DAMOS stats
feature.  Specifically, this commit adds 'stats' directory under each
scheme directory, and makes kdamond 'state' file writing
('update_schemes_stats') to update the contents of the files under the
'stats' directory.

As a result, the files hierarchy becomes as below:

    /sys/kernel/mm/damon/admin
    │ kdamonds/nr
    │ │ 0/state,pid
    │ │ │ contexts/nr
    │ │ │ │ 0/operations
    │ │ │ │ │ monitoring_attrs/intervals/sample_us,aggr_us,update_us
    │ │ │ │ │ │ nr_regions/min,max
    │ │ │ │ │ targets/nr
    │ │ │ │ │ │ 0/pid
    │ │ │ │ │ │ │ regions/nr
    │ │ │ │ │ │ │ │ 0/start,end
    │ │ │ │ │ │ │ │ ...
    │ │ │ │ │ │ ...
    │ │ │ │ │ schemes/nr
    │ │ │ │ │ │ 0/action
    │ │ │ │ │ │ │ access_pattern/
    │ │ │ │ │ │ │ │ sz/min,max
    │ │ │ │ │ │ │ │ nr_accesses/min,max
    │ │ │ │ │ │ │ │ age/min,max
    │ │ │ │ │ │ │ quotas/ms,sz,reset_interval_ms
    │ │ │ │ │ │ │ │ weights/sz,nr_accesses,age
    │ │ │ │ │ │ │ watermarks/metric,interval_us,high,mid,low
    │ │ │ │ │ │ │ stats/    <- NEW DIRECTORY
    │ │ │ │ │ │ │ │ nr_tried,sz_tried,nr_applied,sz_applied,qt_exceeds
    │ │ │ │ │ │ ...
    │ │ │ │ ...
    │ │ ...

Signed-off-by: SeongJae Park <sj@kernel.org>
---
 mm/damon/sysfs.c | 215 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 215 insertions(+)

diff --git a/mm/damon/sysfs.c b/mm/damon/sysfs.c
index 9e54ed7316c6..a4ad6b473b0a 100644
--- a/mm/damon/sysfs.c
+++ b/mm/damon/sysfs.c
@@ -115,6 +115,170 @@ static struct kobj_type damon_sysfs_ul_range_ktype = {
 	.default_groups = damon_sysfs_ul_range_groups,
 };
 
+/*
+ * schemes/stats directory
+ */
+
+struct damon_sysfs_stats {
+	struct kobject kobj;
+	unsigned long nr_tried;
+	unsigned long sz_tried;
+	unsigned long nr_applied;
+	unsigned long sz_applied;
+	unsigned long qt_exceeds;
+};
+
+static struct damon_sysfs_stats *damon_sysfs_stats_alloc(void)
+{
+	return kzalloc(sizeof(struct damon_sysfs_stats), GFP_KERNEL);
+}
+
+static ssize_t damon_sysfs_stats_nr_tried_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	struct damon_sysfs_stats *stats = container_of(kobj,
+			struct damon_sysfs_stats, kobj);
+
+	return sysfs_emit(buf, "%lu\n", stats->nr_tried);
+}
+
+static ssize_t damon_sysfs_stats_nr_tried_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	struct damon_sysfs_stats *stats = container_of(kobj,
+			struct damon_sysfs_stats, kobj);
+	int err = kstrtoul(buf, 0, &stats->nr_tried);
+
+	if (err)
+		return -EINVAL;
+	return count;
+}
+
+static ssize_t damon_sysfs_stats_sz_tried_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	struct damon_sysfs_stats *stats = container_of(kobj,
+			struct damon_sysfs_stats, kobj);
+
+	return sysfs_emit(buf, "%lu\n", stats->sz_tried);
+}
+
+static ssize_t damon_sysfs_stats_sz_tried_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	struct damon_sysfs_stats *stats = container_of(kobj,
+			struct damon_sysfs_stats, kobj);
+	int err = kstrtoul(buf, 0, &stats->sz_tried);
+
+	if (err)
+		return -EINVAL;
+	return count;
+}
+
+static ssize_t damon_sysfs_stats_nr_applied_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	struct damon_sysfs_stats *stats = container_of(kobj,
+			struct damon_sysfs_stats, kobj);
+
+	return sysfs_emit(buf, "%lu\n", stats->nr_applied);
+}
+
+static ssize_t damon_sysfs_stats_nr_applied_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	struct damon_sysfs_stats *stats = container_of(kobj,
+			struct damon_sysfs_stats, kobj);
+	int err = kstrtoul(buf, 0, &stats->nr_applied);
+
+	if (err)
+		return -EINVAL;
+	return count;
+}
+
+static ssize_t damon_sysfs_stats_sz_applied_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	struct damon_sysfs_stats *stats = container_of(kobj,
+			struct damon_sysfs_stats, kobj);
+
+	return sysfs_emit(buf, "%lu\n", stats->sz_applied);
+}
+
+static ssize_t damon_sysfs_stats_sz_applied_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	struct damon_sysfs_stats *stats = container_of(kobj,
+			struct damon_sysfs_stats, kobj);
+	int err = kstrtoul(buf, 0, &stats->sz_applied);
+
+	if (err)
+		return -EINVAL;
+	return count;
+}
+
+static ssize_t damon_sysfs_stats_qt_exceeds_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	struct damon_sysfs_stats *stats = container_of(kobj,
+			struct damon_sysfs_stats, kobj);
+
+	return sysfs_emit(buf, "%lu\n", stats->qt_exceeds);
+}
+
+static ssize_t damon_sysfs_stats_qt_exceeds_store(struct kobject *kobj,
+		struct kobj_attribute *attr, const char *buf, size_t count)
+{
+	struct damon_sysfs_stats *stats = container_of(kobj,
+			struct damon_sysfs_stats, kobj);
+	int err = kstrtoul(buf, 0, &stats->qt_exceeds);
+
+	if (err)
+		return -EINVAL;
+	return count;
+}
+
+static void damon_sysfs_stats_release(struct kobject *kobj)
+{
+	kfree(container_of(kobj, struct damon_sysfs_stats, kobj));
+}
+
+static struct kobj_attribute damon_sysfs_stats_nr_tried_attr =
+		__ATTR(nr_tried, 0400, damon_sysfs_stats_nr_tried_show,
+				damon_sysfs_stats_nr_tried_store);
+
+static struct kobj_attribute damon_sysfs_stats_sz_tried_attr =
+		__ATTR(sz_tried, 0400, damon_sysfs_stats_sz_tried_show,
+				damon_sysfs_stats_sz_tried_store);
+
+static struct kobj_attribute damon_sysfs_stats_nr_applied_attr =
+		__ATTR(nr_applied, 0400, damon_sysfs_stats_nr_applied_show,
+				damon_sysfs_stats_nr_applied_store);
+
+static struct kobj_attribute damon_sysfs_stats_sz_applied_attr =
+		__ATTR(sz_applied, 0400, damon_sysfs_stats_sz_applied_show,
+				damon_sysfs_stats_sz_applied_store);
+
+static struct kobj_attribute damon_sysfs_stats_qt_exceeds_attr =
+		__ATTR(qt_exceeds, 0400, damon_sysfs_stats_qt_exceeds_show,
+				damon_sysfs_stats_qt_exceeds_store);
+
+static struct attribute *damon_sysfs_stats_attrs[] = {
+	&damon_sysfs_stats_nr_tried_attr.attr,
+	&damon_sysfs_stats_sz_tried_attr.attr,
+	&damon_sysfs_stats_nr_applied_attr.attr,
+	&damon_sysfs_stats_sz_applied_attr.attr,
+	&damon_sysfs_stats_qt_exceeds_attr.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(damon_sysfs_stats);
+
+static struct kobj_type damon_sysfs_stats_ktype = {
+	.release = damon_sysfs_stats_release,
+	.sysfs_ops = &kobj_sysfs_ops,
+	.default_groups = damon_sysfs_stats_groups,
+};
+
 /*
  * watermarks directory
  */
@@ -666,6 +830,7 @@ struct damon_sysfs_scheme {
 	struct damon_sysfs_access_pattern *access_pattern;
 	struct damon_sysfs_quotas *quotas;
 	struct damon_sysfs_watermarks *watermarks;
+	struct damon_sysfs_stats *stats;
 };
 
 /* This should match with enum damos_action */
@@ -756,6 +921,22 @@ static int damon_sysfs_scheme_set_watermarks(struct damon_sysfs_scheme *scheme)
 	return err;
 }
 
+static int damon_sysfs_scheme_set_stats(struct damon_sysfs_scheme *scheme)
+{
+	struct damon_sysfs_stats *stats = damon_sysfs_stats_alloc();
+	int err;
+
+	if (!stats)
+		return -ENOMEM;
+	err = kobject_init_and_add(&stats->kobj, &damon_sysfs_stats_ktype,
+			&scheme->kobj, "stats");
+	if (err)
+		kobject_put(&stats->kobj);
+	else
+		scheme->stats = stats;
+	return err;
+}
+
 static int damon_sysfs_scheme_add_dirs(struct damon_sysfs_scheme *scheme)
 {
 	int err;
@@ -769,8 +950,14 @@ static int damon_sysfs_scheme_add_dirs(struct damon_sysfs_scheme *scheme)
 	err = damon_sysfs_scheme_set_watermarks(scheme);
 	if (err)
 		goto put_quotas_access_pattern_out;
+	err = damon_sysfs_scheme_set_stats(scheme);
+	if (err)
+		goto put_watermarks_quotas_access_pattern_out;
 	return 0;
 
+put_watermarks_quotas_access_pattern_out:
+	kobject_put(&scheme->watermarks->kobj);
+	scheme->watermarks = NULL;
 put_quotas_access_pattern_out:
 	kobject_put(&scheme->quotas->kobj);
 	scheme->quotas = NULL;
@@ -787,6 +974,7 @@ static void damon_sysfs_scheme_rm_dirs(struct damon_sysfs_scheme *scheme)
 	damon_sysfs_quotas_rm_dirs(scheme->quotas);
 	kobject_put(&scheme->quotas->kobj);
 	kobject_put(&scheme->watermarks->kobj);
+	kobject_put(&scheme->stats->kobj);
 }
 
 static ssize_t damon_sysfs_scheme_action_show(struct kobject *kobj,
@@ -2163,6 +2351,31 @@ static int damon_sysfs_turn_damon_off(struct damon_sysfs_kdamond *kdamond)
 	 */
 }
 
+static int damon_sysfs_update_schemes_stats(struct damon_sysfs_kdamond *kdamond)
+{
+	struct damon_ctx *ctx = kdamond->damon_ctx;
+	struct damos *scheme;
+	int schemes_idx = 0;
+
+	if (!ctx)
+		return -EINVAL;
+	mutex_lock(&ctx->kdamond_lock);
+	damon_for_each_scheme(scheme, ctx) {
+		struct damon_sysfs_schemes *sysfs_schemes;
+		struct damon_sysfs_stats *sysfs_stats;
+
+		sysfs_schemes = kdamond->contexts->contexts_arr[0]->schemes;
+		sysfs_stats = sysfs_schemes->schemes_arr[schemes_idx++]->stats;
+		sysfs_stats->nr_tried = scheme->stat.nr_tried;
+		sysfs_stats->sz_tried = scheme->stat.sz_tried;
+		sysfs_stats->nr_applied = scheme->stat.nr_applied;
+		sysfs_stats->sz_applied = scheme->stat.sz_applied;
+		sysfs_stats->qt_exceeds = scheme->stat.qt_exceeds;
+	}
+	mutex_unlock(&ctx->kdamond_lock);
+	return 0;
+}
+
 static ssize_t damon_sysfs_kdamond_state_store(struct kobject *kobj,
 		struct kobj_attribute *attr, const char *buf, size_t count)
 {
@@ -2176,6 +2389,8 @@ static ssize_t damon_sysfs_kdamond_state_store(struct kobject *kobj,
 		ret = damon_sysfs_turn_damon_on(kdamond);
 	else if (sysfs_streq(buf, "off"))
 		ret = damon_sysfs_turn_damon_off(kdamond);
+	else if (sysfs_streq(buf, "update_schemes_stats"))
+		ret = damon_sysfs_update_schemes_stats(kdamond);
 	else
 		ret = -EINVAL;
 	mutex_unlock(&damon_sysfs_lock);
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH 11/12] selftests/damon: Add a test for DAMON sysfs interface
  2022-02-23 15:20 [PATCH 00/12] Introduce DAMON sysfs interface SeongJae Park
                   ` (9 preceding siblings ...)
  2022-02-23 15:20 ` [PATCH 10/12] mm/damon/sysfs: Support DAMOS stats SeongJae Park
@ 2022-02-23 15:20 ` SeongJae Park
  2022-02-23 15:20 ` [PATCH 12/12] Docs/admin-guide/mm/damon/usage: Document " SeongJae Park
                   ` (2 subsequent siblings)
  13 siblings, 0 replies; 24+ messages in thread
From: SeongJae Park @ 2022-02-23 15:20 UTC (permalink / raw)
  To: akpm
  Cc: corbet, skhan, rientjes, xhao, linux-damon, linux-mm, linux-doc,
	linux-kselftest, linux-kernel, SeongJae Park

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

Signed-off-by: SeongJae Park <sj@kernel.org>
---
 tools/testing/selftests/damon/Makefile |   1 +
 tools/testing/selftests/damon/sysfs.sh | 306 +++++++++++++++++++++++++
 2 files changed, 307 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..38a06a2845b5
--- /dev/null
+++ b/tools/testing/selftests/damon/sysfs.sh
@@ -0,0 +1,306 @@
+#!/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_stats()
+{
+	stats_dir=$1
+	ensure_dir "$stats_dir" "exist"
+	for f in nr_tried sz_tried nr_applied sz_applied qt_exceeds
+	do
+		ensure_file "$stats_dir/$f" "exist" "400"
+	done
+}
+
+test_watermarks()
+{
+	watermarks_dir=$1
+	ensure_dir "$watermarks_dir" "exist"
+	ensure_file "$watermarks_dir/metric" "exist" "600"
+	ensure_file "$watermarks_dir/interval_us" "exist" "600"
+	ensure_file "$watermarks_dir/high" "exist" "600"
+	ensure_file "$watermarks_dir/mid" "exist" "600"
+	ensure_file "$watermarks_dir/low" "exist" "600"
+}
+
+test_weights()
+{
+	weights_dir=$1
+	ensure_dir "$weights_dir" "exist"
+	ensure_file "$weights_dir/sz" "exist" "600"
+	ensure_file "$weights_dir/nr_accesses" "exist" "600"
+	ensure_file "$weights_dir/age" "exist" "600"
+}
+
+test_quotas()
+{
+	quotas_dir=$1
+	ensure_dir "$quotas_dir" "exist"
+	ensure_file "$quotas_dir/ms" "exist" 600
+	ensure_file "$quotas_dir/sz" "exist" 600
+	ensure_file "$quotas_dir/reset_interval_ms" "exist" 600
+	test_weights "$quotas_dir/weights"
+}
+
+test_access_pattern()
+{
+	access_pattern_dir=$1
+	ensure_dir "$access_pattern_dir" "exist"
+	test_range "$access_pattern_dir/age"
+	test_range "$access_pattern_dir/nr_accesses"
+	test_range "$access_pattern_dir/sz"
+}
+
+test_scheme()
+{
+	scheme_dir=$1
+	ensure_dir "$scheme_dir" "exist"
+	ensure_file "$scheme_dir/action" "exist" "600"
+	test_access_pattern "$scheme_dir/access_pattern"
+	test_quotas "$scheme_dir/quotas"
+	test_watermarks "$scheme_dir/watermarks"
+	test_stats "$scheme_dir/stats"
+}
+
+test_schemes()
+{
+	schemes_dir=$1
+	ensure_dir "$schemes_dir" "exist"
+	ensure_file "$schemes_dir/nr" "exist" 600
+
+	ensure_write_succ  "$schemes_dir/nr" "1" "valid input"
+	test_scheme "$schemes_dir/0"
+
+	ensure_write_succ  "$schemes_dir/nr" "2" "valid input"
+	test_scheme "$schemes_dir/0"
+	test_scheme "$schemes_dir/1"
+
+	ensure_write_succ "$schemes_dir/nr" "0" "valid input"
+	ensure_dir "$schemes_dir/0" "not_exist"
+	ensure_dir "$schemes_dir/1" "not_exist"
+}
+
+test_region()
+{
+	region_dir=$1
+	ensure_dir "$region_dir" "exist"
+	ensure_file "$region_dir/start" "exist" 600
+	ensure_file "$region_dir/end" "exist" 600
+}
+
+test_regions()
+{
+	regions_dir=$1
+	ensure_dir "$regions_dir" "exist"
+	ensure_file "$regions_dir/nr" "exist" 600
+
+	ensure_write_succ  "$regions_dir/nr" "1" "valid input"
+	test_region "$regions_dir/0"
+
+	ensure_write_succ  "$regions_dir/nr" "2" "valid input"
+	test_region "$regions_dir/0"
+	test_region "$regions_dir/1"
+
+	ensure_write_succ "$regions_dir/nr" "0" "valid input"
+	ensure_dir "$regions_dir/0" "not_exist"
+	ensure_dir "$regions_dir/1" "not_exist"
+}
+
+test_target()
+{
+	target_dir=$1
+	ensure_dir "$target_dir" "exist"
+	ensure_file "$target_dir/pid" "exist" "600"
+	test_regions "$target_dir/regions"
+}
+
+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_schemes "$context_dir/schemes"
+}
+
+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


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH 12/12] Docs/admin-guide/mm/damon/usage: Document DAMON sysfs interface
  2022-02-23 15:20 [PATCH 00/12] Introduce DAMON sysfs interface SeongJae Park
                   ` (10 preceding siblings ...)
  2022-02-23 15:20 ` [PATCH 11/12] selftests/damon: Add a test for DAMON sysfs interface SeongJae Park
@ 2022-02-23 15:20 ` SeongJae Park
  2022-02-23 16:07 ` [PATCH 00/12] Introduce " Greg KH
  2022-02-25  7:32 ` xhao
  13 siblings, 0 replies; 24+ messages in thread
From: SeongJae Park @ 2022-02-23 15:20 UTC (permalink / raw)
  To: akpm
  Cc: corbet, skhan, rientjes, xhao, linux-damon, linux-mm, linux-doc,
	linux-kselftest, linux-kernel, SeongJae Park

This commit adds detailed usage of DAMON sysfs interface in the
admin-guide document for DAMON.

Signed-off-by: SeongJae Park <sj@kernel.org>
---
 Documentation/admin-guide/mm/damon/usage.rst | 349 ++++++++++++++++++-
 1 file changed, 343 insertions(+), 6 deletions(-)

diff --git a/Documentation/admin-guide/mm/damon/usage.rst b/Documentation/admin-guide/mm/damon/usage.rst
index b6ec650873b2..51cd3d531404 100644
--- a/Documentation/admin-guide/mm/damon/usage.rst
+++ b/Documentation/admin-guide/mm/damon/usage.rst
@@ -4,7 +4,7 @@
 Detailed Usages
 ===============
 
-DAMON provides below three interfaces for different users.
+DAMON provides below interfaces for different users.
 
 - *DAMON user space tool.*
   `This <https://github.com/awslabs/damo>`_ is for privileged people such as
@@ -14,17 +14,21 @@ DAMON provides below three interfaces for different users.
   virtual and physical address spaces monitoring.  For more detail, please
   refer to its `usage document
   <https://github.com/awslabs/damo/blob/next/USAGE.md>`_.
-- *debugfs interface.*
-  :ref:`This <debugfs_interface>` is for privileged user space programmers who
+- *sysfs interface.*
+  :ref:`This <sysfs_interface>` is for privileged user space programmers who
   want more optimized use of DAMON.  Using this, users can use DAMON’s major
-  features by reading from and writing to special debugfs files.  Therefore,
-  you can write and use your personalized DAMON debugfs wrapper programs that
-  reads/writes the debugfs files instead of you.  The `DAMON user space tool
+  features by reading from and writing to special sysfs files.  Therefore,
+  you can write and use your personalized DAMON sysfs wrapper programs that
+  reads/writes the sysfs files instead of you.  The `DAMON user space tool
   <https://github.com/awslabs/damo>`_ is one example of such programs.  It
   supports both virtual and physical address spaces monitoring.  Note that this
   interface provides only simple :ref:`statistics <damos_stats>` for the
   monitoring results.  For detailed monitoring results, DAMON provides a
   :ref:`tracepoint <tracepoint>`.
+- *debugfs interface.*
+  :ref:`This <debugfs_interface>` is almost identical to :ref:`sysfs interface
+  <sysfs_interface>`.  This will be removed after next LTS kernel is released,
+  so users should move to the :ref:`sysfs interface <sysfs_interface>`.
 - *Kernel Space Programming Interface.*
   :doc:`This </vm/damon/api>` is for kernel space programmers.  Using this,
   users can utilize every feature of DAMON most flexibly and efficiently by
@@ -32,6 +36,339 @@ DAMON provides below three interfaces for different users.
   DAMON for various address spaces.  For detail, please refer to the interface
   :doc:`document </vm/damon/api>`.
 
+.. _sysfs_interface:
+
+sysfs Interface
+===============
+
+DAMON sysfs interface is built when ``CONFIG_DAMON_SYSFS`` is defined.  It
+creates multiple directories and files under its sysfs directory,
+``<sysfs>/kernel/mm/damon/``.  You can control DAMON by writing to and reading
+from the files under the directory.
+
+For a short example, users can monitor the virtual address space of a given
+workload as below. ::
+
+    # cd /sys/kernel/mm/damon/admin/
+    # echo 1 > kdamonds/nr && echo 1 > kdamonds/0/contexts/nr
+    # echo vaddr > kdamonds/0/contexts/0/operations
+    # echo 1 > kdamonds/0/contexts/0/targets/nr
+    # echo $(pidof <workload>) > kdamonds/0/contexts/0/targets/0/pid
+    # echo on > kdamonds/0/state
+
+Files Hierarchy
+---------------
+
+The files hierarchy of DAMON sysfs interface is shown below.  In the below
+figure, parents-children relations are represented with indentations, each
+directory is having ``/`` suffix, and files in each directory are separated by
+comma (","). ::
+
+    /sys/kernel/mm/damon/admin
+    │ kdamonds/nr
+    │ │ 0/state,pid
+    │ │ │ contexts/nr
+    │ │ │ │ 0/operations
+    │ │ │ │ │ monitoring_attrs/
+    │ │ │ │ │ │ intervals/sample_us,aggr_us,update_us
+    │ │ │ │ │ │ nr_regions/min,max
+    │ │ │ │ │ targets/nr
+    │ │ │ │ │ │ 0/pid
+    │ │ │ │ │ │ │ regions/nr
+    │ │ │ │ │ │ │ │ 0/start,end
+    │ │ │ │ │ │ │ │ ...
+    │ │ │ │ │ │ ...
+    │ │ │ │ │ schemes/nr
+    │ │ │ │ │ │ 0/action
+    │ │ │ │ │ │ │ access_pattern/
+    │ │ │ │ │ │ │ │ sz/min,max
+    │ │ │ │ │ │ │ │ nr_accesses/min,max
+    │ │ │ │ │ │ │ │ age/min,max
+    │ │ │ │ │ │ │ quotas/ms,sz,reset_interval_ms
+    │ │ │ │ │ │ │ │ weights/sz,nr_accesses,age
+    │ │ │ │ │ │ │ watermarks/metric,interval_us,high,mid,low
+    │ │ │ │ │ │ │ stats/nr_tried,sz_tried,nr_applied,sz_applied,qt_exceeds
+    │ │ │ │ │ │ ...
+    │ │ │ │ ...
+    │ │ ...
+
+Root
+----
+
+The root of the DAMON sysfs interface is ``<sysfs>/kernel/mm/damon/``, and it
+has one directory named ``admin``.  The directory contains the files for
+privileged user space programs' control of DAMON.  User space tools or deamons
+having the root permission could use this directory.
+
+kdamonds/
+---------
+
+The monitoring-related information including request specifications and results
+are called DAMON context.  DAMON executes each context with a kernel thread
+called kdamond, and multiple kdamonds could run in parallel.
+
+Under the ``admin`` directory, one directory, ``kdamonds``, which has files for
+controlling the kdamonds exist.  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``) exist.
+
+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.  Writing ``update_schemes_stats`` to ``state`` file updates the
+contents of stats files for each DAMON-based operation scheme of the kdamond.
+For details of the stats, please refer to :ref:`stats section
+<sysfs_schemes_stats>`.
+
+If the state is ``on``, reading ``pid`` shows the pid of the kdamond thread.
+
+``contexts`` directory 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 three directories
+(``monitoring_attrs``, ``targets``, and ``schemes``) exist.
+
+DAMON supports multiple types of monitoring operations, including those for
+virtual address space and the physical address space.  You can set and get what
+type of monitoring operations DAMON will use for the context by writing one of
+below keywords to, and reading from the file.
+
+ - vaddr: Monitor virtual address spaces of specific processes
+ - paddr: Monitor the physical address space of the system
+
+contexts/<N>/monitoring_attrs/
+------------------------------
+
+Files for specifying attributes of the monitoring including required quality
+and efficiency of the monitoring are in ``monitoring_attrs`` directory.
+Specifically, two directories, ``intervals`` and ``nr_regions`` exist in this
+directory.
+
+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 in micro-seconds 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, exist.  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 refer
+to the Design document (:doc:`/vm/damon/design`).
+
+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``) and one directory (``regions``)
+exist.
+
+If you wrote ``vaddr`` to the ``contexts/<N>/operations``, each target should
+be a process.  You can specify the process to DAMON by writing the pid of the
+process to the ``pid`` file.
+
+targets/<N>/regions
+-------------------
+
+When ``vaddr`` monitoring operations set is being used (``vaddr`` is written to
+the ``contexts/<N>/operations`` file), DAMON automatically sets and updates the
+monitoring target regions so that entire memory mappings of target processes
+can be covered.  However, users could want to set the initial monitoring region
+to specific address ranges.
+
+In contrast, DAMON do not automatically sets and updates the monitoring target
+regions when ``paddr`` monitoring operations set is being used (``paddr`` is
+written to the ``contexts/<N>/operations``).  Therefore, users should set the
+monitoring target regions by themselves in the case.
+
+For such cases, users can explicitly set the initial monitoring target regions
+as they want, by writing proper values to the files under this directory.
+
+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 initial monitoring target region.
+
+regions/<N>/
+------------
+
+In each region directory, you will find two files (``start`` and ``end``).  You
+can set and get the start and end addresses of the initial monitoring target
+region by writing to and reading from the files, respectively.
+
+contexts/<N>/schemes/
+---------------------
+
+For usual DAMON-based data access aware memory management optimizations, users
+would normally want the system to apply a memory management action to a memory
+region of a specific access pattern.  DAMON receives such formalized operation
+schemes from the user and applies those to the target memory regions.  Users
+can get and set the schemes by reading from and writing to files under this
+directory.
+
+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 DAMON-based operation scheme.
+
+schemes/<N>/
+------------
+
+In each scheme directory, four directories (``access_pattern``, ``quotas``,
+``watermarks``, and ``stats``) and one file (``action``) exist.
+
+The ``action`` file is for setting and getting what action you want to apply to
+memory regions having specific access pattern of the interest.  The keywords
+that can be written to and read from the file and their meaning are as below.
+
+ - willneed: Call ``madvise()`` for the region with ``MADV_WILLNEED``
+ - cold: Call ``madvise()`` for the region with ``MADV_COLD``
+ - pageout: Call ``madvise()`` for the region with ``MADV_PAGEOUT``
+ - hugepage: Call ``madvise()`` for the region with ``MADV_HUGEPAGE``
+ - nohugepage: Call ``madvise()`` for the region with ``MADV_NOHUGEPAGE``
+ - stat: Do nothing but count the statistics
+
+schemes/<N>/access_pattern/
+---------------------------
+
+The target access pattern of each DAMON-based operation scheme is constructed
+with three ranges including the size of the region in bytes, number of
+monitored accesses per aggregate interval, and number of aggregated intervals
+for the age of the region.
+
+Under the ``access_pattern`` directory, three directories (``sz``,
+``nr_accesses``, and ``age``) each having two files (``min`` and ``max``)
+exist.  You can set and get the access pattern for the given scheme by writing
+to and reading from the ``min`` and ``max`` files under ``sz``,
+``nr_accesses``, and ``age`` directories, respectively.
+
+schemes/<N>/quotas/
+-------------------
+
+Optimal ``target access pattern`` for each ``action`` is workload dependent, so
+not easy to find.  Worse yet, setting a scheme of some action too aggressive
+can cause severe overhead.  To avoid such overhead, users can limit time and
+size quota for each scheme.  In detail, users can ask DAMON to try to use only
+up to specific time (``time quota``) for applying the action, and to apply the
+action to only up to specific amount (``size quota``) of memory regions having
+the target access pattern within a given time interval (``reset interval``).
+
+When the quota limit is expected to be exceeded, DAMON prioritizes found memory
+regions of the ``target access pattern`` based on their size, access frequency,
+and age.  For personalized prioritization, users can set the weights for the
+three properties.
+
+Under ``quotas`` directory, three files (``ms``, ``sz``, ``reset_interval_ms``)
+and one directory (``weights``) having three files (``sz``, ``nr_accesses``,
+and ``age``) in it exist.
+
+You can set the ``time quota`` in milliseconds, ``size quota`` in bytes, and
+``reset interval`` in milliseconds by writing the values to the three files,
+respectively.  You can also set the prioritization weights for size, access
+frequency, and age in per-thousand unit by writing the values to the three
+files under the ``weights`` directory.
+
+schemes/<N>/watermarks/
+-----------------------
+
+To allow easy activation and deactivation of each scheme based on system
+status, DAMON provides a feature called watermarks.  The feature receives five
+values called ``metric``, ``interval``, ``high``, ``mid``, and ``low``.  The
+``metric`` is the system metric such as free memory ratio that can be measured.
+If the metric value of the system is higher than the value in ``high`` or lower
+than ``low`` at the memoent, the scheme is deactivated.  If the value is lower
+than ``mid``, the scheme is activated.
+
+Under the watermarks directory, five files (``metric``, ``interval_us``,
+``high``, ``mid``, and ``low``) for setting each value exist.  You can set and
+get the five values by writing to the files, respectively.
+
+Keywords and meanings of those that can be written to the ``metric`` file are
+as below.
+
+ - none: Ignore the watermarks
+ - free_mem_rate: System's free memory rate (per thousand)
+
+The ``interval`` should written in microseconds unit.
+
+.. _sysfs_schemes_stats:
+
+schemes/<N>/stats/
+------------------
+
+DAMON counts the total number and bytes of regions that each scheme is tried to
+be applied, the two numbers for the regions that each scheme is successfully
+applied, and the total number of the quota limit exceeds.  This statistics can
+be used for online analysis or tuning of the schemes.
+
+The statistics can be retrieved by reading the files under ``stats`` directory
+(``nr_tried``, ``sz_tried``, ``nr_applied``, ``sz_applied``, and
+``qt_exceeds``), respectively.  The files are not updated in real time, so you
+should ask DAMON sysfs interface to updte the content of the files for the
+stats by writing a special keyword, ``update_schemes_stats`` to the relevant
+``kdamonds/<N>/state`` file.
+
+Example
+~~~~~~~
+
+Below commands applies a scheme saying "If a memory region of size in [4KiB,
+8KiB] is showing accesses per aggregate interval in [0, 5] for aggregate
+interval in [10, 20], page out the region.  For the paging out, use only up to
+10ms per second, and also don't page out more than 1GiB per second.  Under the
+limitation, page out memory regions having longer age first.  Also, check the
+free memory rate of the system every 5 seconds, start the monitoring and paging
+out when the free memory rate becomes lower than 50%, but stop it if the free
+memory rate becomes larger than 60%, or lower than 30%". ::
+
+    # cd <sysfs>/kernel/mm/damon/admin
+    # # populate directories
+    # echo 1 > kdamond/nr; echo 1 > kdamonds/0/contexts/nr;
+    # echo 1 > kdamonds/0/contexts/0/schemes/nr
+    # cd kdamonds/0/contexts/0/schemes/0
+    # # set the basic access pattern and the action
+    # echo 4096 > access_patterns/sz/min
+    # echo 8192 > access_patterns/sz/max
+    # echo 0 > access_patterns/nr_accesses/min
+    # echo 5 > access_patterns/nr_accesses/max
+    # echo 10 > access_patterns/age/min
+    # echo 20 > access_patterns/age/max
+    # echo pageout > action
+    # # set quotas
+    # echo 10 > quotas/ms
+    # echo $((1024*1024*1024)) > quotas/sz
+    # echo 1000 > quotas/reset_interval_ms
+    # # set watermark
+    # echo free_mem_rate > watermarks/metric
+    # echo 5000000 > watermarks/interval_us
+    # echo 600 > watermarks/high
+    # echo 500 > watermarks/mid
+    # echo 300 > watermarks/low
+
+Please note that it's highly recommended to use user space tools like `damo
+<https://github.com/awslabs/damo>`_ rather than manually reading and writing
+the files as above.  Above is only for an example.
 
 .. _debugfs_interface:
 
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* Re: [PATCH 00/12] Introduce DAMON sysfs interface
  2022-02-23 15:20 [PATCH 00/12] Introduce DAMON sysfs interface SeongJae Park
                   ` (11 preceding siblings ...)
  2022-02-23 15:20 ` [PATCH 12/12] Docs/admin-guide/mm/damon/usage: Document " SeongJae Park
@ 2022-02-23 16:07 ` Greg KH
  2022-02-23 16:44   ` SeongJae Park
  2022-02-25  7:32 ` xhao
  13 siblings, 1 reply; 24+ messages in thread
From: Greg KH @ 2022-02-23 16:07 UTC (permalink / raw)
  To: SeongJae Park
  Cc: akpm, corbet, skhan, rientjes, xhao, linux-damon, linux-mm,
	linux-doc, linux-kselftest, linux-kernel

On Wed, Feb 23, 2022 at 03:20:39PM +0000, SeongJae Park wrote:
> Chages from Previous Version (RFC)
> ==================================
> 
> Compared to the RFC version of this patchset
> (https://lore.kernel.org/linux-mm/20220217161938.8874-1-sj@kernel.org/), this
> version contains below changes.
> 
> - Implement all DAMON debugfs interface providing features
> - Writeup documents
> - Add more selftests
> 
> Introduction
> ============
> 
> DAMON's debugfs-based user interface (DAMON_DBGFS) 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
> inefficient, hard to be used, and 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 DAMON_DBGFS 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
> having one dedicated file for each 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/operations
>     # echo 1 > kdamonds/0/contexts/0/targets/nr
>     # echo $(pidof <workload>) > kdamonds/0/contexts/0/targets/0/pid
>     # echo on > kdamonds/0/state
> 
> A brief representation of the files hierarchy of DAMON sysfs interface is as
> below.  Childs are represented with indentation, directories are having '/'
> suffix, and files in each directory are separated by comma.
> 
>     /sys/kernel/mm/damon/admin
>     │ kdamonds/nr
>     │ │ 0/state,pid
>     │ │ │ contexts/nr
>     │ │ │ │ 0/operations
>     │ │ │ │ │ monitoring_attrs/
>     │ │ │ │ │ │ intervals/sample_us,aggr_us,update_us
>     │ │ │ │ │ │ nr_regions/min,max
>     │ │ │ │ │ targets/nr
>     │ │ │ │ │ │ 0/pid
>     │ │ │ │ │ │ │ regions/nr
>     │ │ │ │ │ │ │ │ 0/start,end
>     │ │ │ │ │ │ │ │ ...
>     │ │ │ │ │ │ ...
>     │ │ │ │ │ schemes/nr
>     │ │ │ │ │ 0/action
>     │ │ │ │ │ │ access_pattern/
>     │ │ │ │ │ │ │ sz/min,max
>     │ │ │ │ │ │ │ nr_accesses/min,max
>     │ │ │ │ │ │ │ age/min,max
>     │ │ │ │ │ │ quotas/ms,sz,reset_interval_ms
>     │ │ │ │ │ │ │ weights/sz,nr_accesses,age
>     │ │ │ │ │ │ watermarks/metric,interval_us,high,mid,low
>     │ │ │ │ │ │ stats/nr_tried,sz_tried,nr_applied,sz_applied,qt_exceeds
>     │ │ │ │ │ ...
>     │ │ ...
> 
> Detailed usage of the files will be described in the final Documentation patch
> of this patchset.
> 
> Main Difference Between DAMON_DBGFS and DAMON_SYSFS
> ---------------------------------------------------
> 
> At the moment, DAMON_DBGFS and DAMON_SYSFS provides same features.  One
> important difference between them is their exclusiveness.  DAMON_DBGFS works in
> an exclusive manner, so that no DAMON worker thread (kdamond) in the system can
> run concurrently and interfere somehow.  For the reason, DAMON_DBGFS asks users
> to construct all monitoring contexts and start them at once.  It's not a big
> problem but makes the operation a little bit complex and unflexible.
> 
> For more flexible usage, DAMON_SYSFS moves the responsibility of preventing any
> possible interference to the admins and work in a non-exclusive manner.  That
> is, users can configure and start contexts one by one.  Note that DAMON
> respects both exclusive groups and non-exclusive groups of contexts, in a
> manner similar to that of reader-writer locks.  That is, if any exclusive
> monitoring contexts (e.g., contexts that started via DAMON_DBGFS) are running,
> DAMON_SYSFS does not start new contexts, and vice versa.
> 
> Future Plan of DAMON_DBGFS Deprecation
> ======================================
> 
> Once this patchset is merged, DAMON_DBGFS development will be frozen.  That is,
> we will maintain it to work as is now so that no users will be break.  But, it
> will not be extended to provide any new feature of DAMON.  The support will be
> continued only until next LTS release.  After that, we will drop DAMON_DBGFS.
> 
> User-space Tooling Compatibility
> --------------------------------
> 
> As DAMON_SYSFS provides all features of DAMON_DBGFS, all user space tooling can
> move to DAMON_SYSFS.  As we will continue supporting DAMON_DBGFS until next LTS
> kernel release, user space tools would have enough time to move to DAMON_SYSFS.
> 
> The official user space tool, damo[1], is already supporting both DAMON_SYSFS
> and DAMON_DBGFS.  Both correctness tests[2] and performance tests[3] of DAMON
> using DAMON_SYSFS also passed.
> 
> [1] https://github.com/awslabs/damo
> [2] https://github.com/awslabs/damon-tests/tree/master/corr
> [3] https://github.com/awslabs/damon-tests/tree/master/perf
> 
> Complete Git Tree
> =================
> 
> You can get the complete git tree from
> https://git.kernel.org/sj/h/damon/sysfs/patches/v1.
> 
> Sequence of Patches
> ===================
> 
> First two patches (patches 1-2) make core changes for DAMON_SYSFS.  The first
> one (patch 1) allows non-exclusive DAMON contexts so that DAMON_SYSFS can work
> in non-exclusive mode, while the second one (patch 2) adds size of DAMON enum
> types so that DAMON API users can safely iterate the enums.
> 
> Third patch (patch 3) implements basic sysfs stub for virtual address spaces
> monitoring.  Note that this implements only sysfs files and DAMON is not
> linked.  Fourth patch (patch 4) links the DAMON_SYSFS to DAMON so that users
> can control DAMON using the sysfs files.
> 
> Following six patches (patches 5-10) implements other DAMON features that
> DAMON_DBGFS supports one by one (physical address space monitoring, DAMON-based
> operation schemes, schemes quotas, schemes prioritization weights, schemes
> watermarks, and schemes stats).
> 
> Following patch (patch 11) adds a simple selftest for DAMON_SYSFS, and the
> final one (patch 12) documents DAMON_SYSFS.
> 
> SeongJae Park (12):
>   mm/damon/core: Allow non-exclusive DAMON start/stop
>   mm/damon/core: Add number of each enum type values
>   mm/damon: Implement a minimal stub for sysfs-based DAMON interface
>   mm/damon/sysfs: Link DAMON for virtual address spaces monitoring
>   mm/damon/sysfs: Support physical address space monitoring
>   mm/damon/sysfs: Support DAMON-based Operation Schemes
>   mm/damon/sysfs: Support DAMOS quotas
>   mm/damon/sysfs: Support schemes prioritization weights
>   mm/damon/sysfs: Support DAMOS watermarks
>   mm/damon/sysfs: Support DAMOS stats
>   selftests/damon: Add a test for DAMON sysfs interface
>   Docs/admin-guide/mm/damon/usage: Document DAMON sysfs interface
> 
>  Documentation/admin-guide/mm/damon/usage.rst |  349 ++-
>  include/linux/damon.h                        |    6 +-
>  mm/damon/Kconfig                             |    7 +
>  mm/damon/Makefile                            |    1 +
>  mm/damon/core.c                              |   23 +-
>  mm/damon/dbgfs.c                             |    2 +-
>  mm/damon/reclaim.c                           |    2 +-
>  mm/damon/sysfs.c                             | 2684 ++++++++++++++++++
>  tools/testing/selftests/damon/Makefile       |    1 +
>  tools/testing/selftests/damon/sysfs.sh       |  306 ++
>  10 files changed, 3364 insertions(+), 17 deletions(-)
>  create mode 100644 mm/damon/sysfs.c
>  create mode 100755 tools/testing/selftests/damon/sysfs.sh

sysfs files need to be documented in Documentation/ABI/ which is where
our tools look for them (have you run scripts/get_abi.pl with your code
in the kernel?)

Please fix this up to use that format so we can then verify that you are
using sysfs correctly.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 03/12] mm/damon: Implement a minimal stub for sysfs-based DAMON interface
  2022-02-23 15:20 ` [PATCH 03/12] mm/damon: Implement a minimal stub for sysfs-based DAMON interface SeongJae Park
@ 2022-02-23 16:09   ` Greg KH
  2022-02-23 16:45     ` SeongJae Park
  2022-02-25  7:21   ` xhao
  1 sibling, 1 reply; 24+ messages in thread
From: Greg KH @ 2022-02-23 16:09 UTC (permalink / raw)
  To: SeongJae Park
  Cc: akpm, corbet, skhan, rientjes, xhao, linux-damon, linux-mm,
	linux-doc, linux-kselftest, linux-kernel

On Wed, Feb 23, 2022 at 03:20:42PM +0000, SeongJae Park wrote:
> +static struct kobj_attribute damon_sysfs_ul_range_min_attr =
> +		__ATTR(min, 0600, damon_sysfs_ul_range_min_show,
> +				damon_sysfs_ul_range_min_store);
> +
> +static struct kobj_attribute damon_sysfs_ul_range_max_attr =
> +		__ATTR(max, 0600, damon_sysfs_ul_range_max_show,
> +				damon_sysfs_ul_range_max_store);

Can you use __ATTR_RW_MODE() instead here and elsewhere?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 00/12] Introduce DAMON sysfs interface
  2022-02-23 16:07 ` [PATCH 00/12] Introduce " Greg KH
@ 2022-02-23 16:44   ` SeongJae Park
  0 siblings, 0 replies; 24+ messages in thread
From: SeongJae Park @ 2022-02-23 16:44 UTC (permalink / raw)
  To: Greg KH
  Cc: SeongJae Park, akpm, corbet, skhan, rientjes, xhao, linux-damon,
	linux-mm, linux-doc, linux-kselftest, linux-kernel

On Wed, 23 Feb 2022 17:07:20 +0100 Greg KH <gregkh@linuxfoundation.org> wrote:

> On Wed, Feb 23, 2022 at 03:20:39PM +0000, SeongJae Park wrote:
> > Chages from Previous Version (RFC)
> > ==================================
> > 
> > Compared to the RFC version of this patchset
> > (https://lore.kernel.org/linux-mm/20220217161938.8874-1-sj@kernel.org/), this
> > version contains below changes.
> > 
> > - Implement all DAMON debugfs interface providing features
> > - Writeup documents
> > - Add more selftests
> > 
> > Introduction
> > ============
> > 
> > DAMON's debugfs-based user interface (DAMON_DBGFS) 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
> > inefficient, hard to be used, and 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 DAMON_DBGFS in long term.
[...]
> > 
> > SeongJae Park (12):
> >   mm/damon/core: Allow non-exclusive DAMON start/stop
> >   mm/damon/core: Add number of each enum type values
> >   mm/damon: Implement a minimal stub for sysfs-based DAMON interface
> >   mm/damon/sysfs: Link DAMON for virtual address spaces monitoring
> >   mm/damon/sysfs: Support physical address space monitoring
> >   mm/damon/sysfs: Support DAMON-based Operation Schemes
> >   mm/damon/sysfs: Support DAMOS quotas
> >   mm/damon/sysfs: Support schemes prioritization weights
> >   mm/damon/sysfs: Support DAMOS watermarks
> >   mm/damon/sysfs: Support DAMOS stats
> >   selftests/damon: Add a test for DAMON sysfs interface
> >   Docs/admin-guide/mm/damon/usage: Document DAMON sysfs interface
> > 
> >  Documentation/admin-guide/mm/damon/usage.rst |  349 ++-
> >  include/linux/damon.h                        |    6 +-
> >  mm/damon/Kconfig                             |    7 +
> >  mm/damon/Makefile                            |    1 +
> >  mm/damon/core.c                              |   23 +-
> >  mm/damon/dbgfs.c                             |    2 +-
> >  mm/damon/reclaim.c                           |    2 +-
> >  mm/damon/sysfs.c                             | 2684 ++++++++++++++++++
> >  tools/testing/selftests/damon/Makefile       |    1 +
> >  tools/testing/selftests/damon/sysfs.sh       |  306 ++
> >  10 files changed, 3364 insertions(+), 17 deletions(-)
> >  create mode 100644 mm/damon/sysfs.c
> >  create mode 100755 tools/testing/selftests/damon/sysfs.sh
> 
> sysfs files need to be documented in Documentation/ABI/ which is where
> our tools look for them (have you run scripts/get_abi.pl with your code
> in the kernel?)
> 
> Please fix this up to use that format so we can then verify that you are
> using sysfs correctly.

Sure, I will do so in the next version.


Thanks,
SJ

> 
> thanks,
> 
> greg k-h

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 03/12] mm/damon: Implement a minimal stub for sysfs-based DAMON interface
  2022-02-23 16:09   ` Greg KH
@ 2022-02-23 16:45     ` SeongJae Park
  2022-02-23 17:13       ` SeongJae Park
  0 siblings, 1 reply; 24+ messages in thread
From: SeongJae Park @ 2022-02-23 16:45 UTC (permalink / raw)
  To: Greg KH
  Cc: SeongJae Park, akpm, corbet, skhan, rientjes, xhao, linux-damon,
	linux-mm, linux-doc, linux-kselftest, linux-kernel

On Wed, 23 Feb 2022 17:09:38 +0100 Greg KH <gregkh@linuxfoundation.org> wrote:

> On Wed, Feb 23, 2022 at 03:20:42PM +0000, SeongJae Park wrote:
> > +static struct kobj_attribute damon_sysfs_ul_range_min_attr =
> > +		__ATTR(min, 0600, damon_sysfs_ul_range_min_show,
> > +				damon_sysfs_ul_range_min_store);
> > +
> > +static struct kobj_attribute damon_sysfs_ul_range_max_attr =
> > +		__ATTR(max, 0600, damon_sysfs_ul_range_max_show,
> > +				damon_sysfs_ul_range_max_store);
> 
> Can you use __ATTR_RW_MODE() instead here and elsewhere?

Sure, I will, in the next revision.


Thanks,
SJ

> 
> thanks,
> 
> greg k-h
> 

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 03/12] mm/damon: Implement a minimal stub for sysfs-based DAMON interface
  2022-02-23 16:45     ` SeongJae Park
@ 2022-02-23 17:13       ` SeongJae Park
  2022-02-23 18:33         ` Greg KH
  0 siblings, 1 reply; 24+ messages in thread
From: SeongJae Park @ 2022-02-23 17:13 UTC (permalink / raw)
  To: SeongJae Park
  Cc: Greg KH, akpm, corbet, skhan, rientjes, xhao, linux-damon,
	linux-mm, linux-doc, linux-kselftest, linux-kernel

On Wed, 23 Feb 2022 16:45:13 +0000 SeongJae Park <sj@kernel.org> wrote:

> On Wed, 23 Feb 2022 17:09:38 +0100 Greg KH <gregkh@linuxfoundation.org> wrote:
> 
> > On Wed, Feb 23, 2022 at 03:20:42PM +0000, SeongJae Park wrote:
> > > +static struct kobj_attribute damon_sysfs_ul_range_min_attr =
> > > +		__ATTR(min, 0600, damon_sysfs_ul_range_min_show,
> > > +				damon_sysfs_ul_range_min_store);
> > > +
> > > +static struct kobj_attribute damon_sysfs_ul_range_max_attr =
> > > +		__ATTR(max, 0600, damon_sysfs_ul_range_max_show,
> > > +				damon_sysfs_ul_range_max_store);
> > 
> > Can you use __ATTR_RW_MODE() instead here and elsewhere?
> 
> Sure, I will, in the next revision.

After thinking once more, I realized that it might not so simple.  First of
all, there are two files having same name in different directories
(kdamonds/<N>/pid and targets/<N>/pid).  The files work differently, so I need
to use different _show/_store callbacks for them but __ATTR_RW_MODE() wouldn't
support the case.

Secondly, I'd like to keep the file names short because the meaning of the
files can easily inferred from the hierarchy, but want to keep the _show/_store
callback names to have prefixes that allows us easily know their meaning and
usage even though it makes the name a little bit longer because I don't want to
have too much source files for DAMON sysfs interface.

Am I missing some of your point?


Thanks,
SJ

[...]

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 03/12] mm/damon: Implement a minimal stub for sysfs-based DAMON interface
  2022-02-23 17:13       ` SeongJae Park
@ 2022-02-23 18:33         ` Greg KH
  2022-02-23 19:03           ` SeongJae Park
  0 siblings, 1 reply; 24+ messages in thread
From: Greg KH @ 2022-02-23 18:33 UTC (permalink / raw)
  To: SeongJae Park
  Cc: akpm, corbet, skhan, rientjes, xhao, linux-damon, linux-mm,
	linux-doc, linux-kselftest, linux-kernel

On Wed, Feb 23, 2022 at 05:13:41PM +0000, SeongJae Park wrote:
> On Wed, 23 Feb 2022 16:45:13 +0000 SeongJae Park <sj@kernel.org> wrote:
> 
> > On Wed, 23 Feb 2022 17:09:38 +0100 Greg KH <gregkh@linuxfoundation.org> wrote:
> > 
> > > On Wed, Feb 23, 2022 at 03:20:42PM +0000, SeongJae Park wrote:
> > > > +static struct kobj_attribute damon_sysfs_ul_range_min_attr =
> > > > +		__ATTR(min, 0600, damon_sysfs_ul_range_min_show,
> > > > +				damon_sysfs_ul_range_min_store);
> > > > +
> > > > +static struct kobj_attribute damon_sysfs_ul_range_max_attr =
> > > > +		__ATTR(max, 0600, damon_sysfs_ul_range_max_show,
> > > > +				damon_sysfs_ul_range_max_store);
> > > 
> > > Can you use __ATTR_RW_MODE() instead here and elsewhere?
> > 
> > Sure, I will, in the next revision.
> 
> After thinking once more, I realized that it might not so simple.  First of
> all, there are two files having same name in different directories
> (kdamonds/<N>/pid and targets/<N>/pid).  The files work differently, so I need
> to use different _show/_store callbacks for them but __ATTR_RW_MODE() wouldn't
> support the case.

The reason I recommend using these macros is to prevent you from having
sysfs files with the same name, yet doing different things in different
places in the sysfs tree :)

> Secondly, I'd like to keep the file names short because the meaning of the
> files can easily inferred from the hierarchy, but want to keep the _show/_store
> callback names to have prefixes that allows us easily know their meaning and
> usage even though it makes the name a little bit longer because I don't want to
> have too much source files for DAMON sysfs interface.
> 
> Am I missing some of your point?

How about renaming one of the files?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 03/12] mm/damon: Implement a minimal stub for sysfs-based DAMON interface
  2022-02-23 18:33         ` Greg KH
@ 2022-02-23 19:03           ` SeongJae Park
  0 siblings, 0 replies; 24+ messages in thread
From: SeongJae Park @ 2022-02-23 19:03 UTC (permalink / raw)
  To: Greg KH
  Cc: SeongJae Park, akpm, corbet, skhan, rientjes, xhao, linux-damon,
	linux-mm, linux-doc, linux-kselftest, linux-kernel

On Wed, 23 Feb 2022 19:33:31 +0100 Greg KH <gregkh@linuxfoundation.org> wrote:

> On Wed, Feb 23, 2022 at 05:13:41PM +0000, SeongJae Park wrote:
> > On Wed, 23 Feb 2022 16:45:13 +0000 SeongJae Park <sj@kernel.org> wrote:
> > 
> > > On Wed, 23 Feb 2022 17:09:38 +0100 Greg KH <gregkh@linuxfoundation.org> wrote:
> > > 
> > > > On Wed, Feb 23, 2022 at 03:20:42PM +0000, SeongJae Park wrote:
> > > > > +static struct kobj_attribute damon_sysfs_ul_range_min_attr =
> > > > > +		__ATTR(min, 0600, damon_sysfs_ul_range_min_show,
> > > > > +				damon_sysfs_ul_range_min_store);
> > > > > +
> > > > > +static struct kobj_attribute damon_sysfs_ul_range_max_attr =
> > > > > +		__ATTR(max, 0600, damon_sysfs_ul_range_max_show,
> > > > > +				damon_sysfs_ul_range_max_store);
> > > > 
> > > > Can you use __ATTR_RW_MODE() instead here and elsewhere?
> > > 
> > > Sure, I will, in the next revision.
> > 
> > After thinking once more, I realized that it might not so simple.  First of
> > all, there are two files having same name in different directories
> > (kdamonds/<N>/pid and targets/<N>/pid).  The files work differently, so I need
> > to use different _show/_store callbacks for them but __ATTR_RW_MODE() wouldn't
> > support the case.
> 
> The reason I recommend using these macros is to prevent you from having
> sysfs files with the same name, yet doing different things in different
> places in the sysfs tree :)

Thank you for clarifying!  Maybe I was making the hierarchy unnecessarily deep
and thus naming files too short and/or common, which could confuses relative
paths users.

> 
> > Secondly, I'd like to keep the file names short because the meaning of the
> > files can easily inferred from the hierarchy, but want to keep the _show/_store
> > callback names to have prefixes that allows us easily know their meaning and
> > usage even though it makes the name a little bit longer because I don't want to
> > have too much source files for DAMON sysfs interface.
> > 
> > Am I missing some of your point?
> 
> How about renaming one of the files?

Thank you for the suggestion, will do so.


Thanks,
SJ

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 03/12] mm/damon: Implement a minimal stub for sysfs-based DAMON interface
  2022-02-23 15:20 ` [PATCH 03/12] mm/damon: Implement a minimal stub for sysfs-based DAMON interface SeongJae Park
  2022-02-23 16:09   ` Greg KH
@ 2022-02-25  7:21   ` xhao
  2022-02-25  8:10     ` SeongJae Park
  1 sibling, 1 reply; 24+ messages in thread
From: xhao @ 2022-02-25  7:21 UTC (permalink / raw)
  To: SeongJae Park, akpm
  Cc: corbet, skhan, rientjes, linux-damon, linux-mm, linux-doc,
	linux-kselftest, linux-kernel

Hi, SeongJae:

On 2/23/22 11:20 PM, SeongJae Park wrote:
> 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 separated by
> white spaces.  As a result, it is ineffient, hard to be used, and
> 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.
>
> To this end, this commit implements a stub of a part of the new user
> interface of DAMON using sysfs.  Specifically, this commit implements
> the sysfs control parts for virtual address space monitoring.
>
> More specifically, the idea of the new interface is, using directory
> hierarchies and making one file for one value.  The hierarchy that this
> commit is introducing is as below.  In the below figure,
> parents-children relations are represented with indentations, each
> directory is having ``/`` suffix, and files in each directory are
> separated by comma (",").
>
>      /sys/kernel/mm/damon/admin
>      │ kdamonds/nr
>      │ │ 0/state,pid
>      │ │ │ contexts/nr
>      │ │ │ │ 0/operations
>      │ │ │ │ │ monitoring_attrs/
>      │ │ │ │ │ │ intervals/sample_us,aggr_us,update_us
>      │ │ │ │ │ │ nr_regions/min,max
>      │ │ │ │ │ targets/nr
>      │ │ │ │ │ │ 0/pid
>      │ │ │ │ │ │ ...
>      │ │ │ │ ...
>      │ │ ...

>
> Writing a number <N> to each 'nr' file makes directories of name <0> to
> <N-1> in the directory of the 'nr' file.  That's all this commit does.
> Writing proper values to relevant files will construct the DAMON
> contexts, and writing a special keyword, 'on', to 'state' files for each
> kdamond will ask DAMON to start the constructed contexts.
>
> For a short example, using below commands for
> monitoring virtual address spaces of a given workload is imaginable:
>
>      # 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

I do some test  about the sys interface, like this:

[root@rt2k03395 0]# tree
.
├── contexts
│   ├── 0
│   │   ├── monitoring_attrs
│   │   │   ├── intervals
│   │   │   │   ├── aggr_us
│   │   │   │   ├── sample_us
│   │   │   │   └── update_us
│   │   │   └── nr_regions
│   │   │       ├── max
│   │   │       └── min
│   │   ├── operations
│   │   ├── schemes
│   │   │   └── nr
│   │   └── targets
│   │       ├── 0
│   │       │   ├── pid
│   │       │   └── regions
│   │       │       ├── 0
│   │       │       │   ├── end
│   │       │       │   └── start
│   │       │       ├── 1
│   │       │       │   ├── end
│   │       │       │   └── start
│   │       │       ├── 10
│   │       │       │   ├── end
│   │       │       │   └── start
│   │       │       ├── 11
│   │       │       │   ├── end
│   │       │       │   └── start
│   │       │       ├── 12

cd regions/
[root@rt2k03395 regions]# ls
0  10  12  14  16  18  2   21  23  25  27  29  30  32  34  36  38 4   
41  43  45  47  49  6  8  nr
1  11  13  15  17  19  20  22  24  26  28  3   31  33  35  37  39 40  
42  44  46  48  5   7  9
[root@rt2k03395 regions]# cd 44/cat *

[root@rt2k03395 regions/44]# cat *
0  0

I'm skeptical about the number regions ? And after manually setting the 
number of nr, the processing of

"start" and "end" will be very troublesome,I guess you might want to do 
some special region addresses,

such as hot or cold region, Is that true ?But I think you need to think 
about how do you deal with too many

uncontacted reigons that need to be done.

> Please note that this commit is implementing only the sysfs part stub as
> abovely mentioned.  This commit doesn't implement the special keywords
> for 'state' files.  Following commits will do that.
>
> Signed-off-by: SeongJae Park <sj@kernel.org>
> ---
>   mm/damon/Kconfig  |    7 +
>   mm/damon/Makefile |    1 +
>   mm/damon/sysfs.c  | 1059 +++++++++++++++++++++++++++++++++++++++++++++
>   3 files changed, 1067 insertions(+)
>   create mode 100644 mm/damon/sysfs.c
>
> diff --git a/mm/damon/Kconfig b/mm/damon/Kconfig
> index 01bad77ad7ae..9b559c76d6dd 100644
> --- a/mm/damon/Kconfig
> +++ b/mm/damon/Kconfig
> @@ -52,6 +52,13 @@ config DAMON_VADDR_KUNIT_TEST
>   
>   	  If unsure, say N.
>   
> +config DAMON_SYSFS
> +	bool "DAMON sysfs interface"
> +	depends on DAMON && SYSFS
> +	help
> +	  This builds the sysfs interface for DAMON.  The user space can use
> +	  the interface for arbitrary data access monitoring.
> +
>   config DAMON_DBGFS
>   	bool "DAMON debugfs interface"
>   	depends on DAMON_VADDR && DAMON_PADDR && DEBUG_FS
> diff --git a/mm/damon/Makefile b/mm/damon/Makefile
> index aebbf6c14c51..dbf7190b4144 100644
> --- a/mm/damon/Makefile
> +++ b/mm/damon/Makefile
> @@ -3,5 +3,6 @@
>   obj-y				:= core.o
>   obj-$(CONFIG_DAMON_VADDR)	+= ops-common.o vaddr.o
>   obj-$(CONFIG_DAMON_PADDR)	+= ops-common.o paddr.o
> +obj-$(CONFIG_DAMON_SYSFS)	+= sysfs.o
>   obj-$(CONFIG_DAMON_DBGFS)	+= dbgfs.o
>   obj-$(CONFIG_DAMON_RECLAIM)	+= reclaim.o
> diff --git a/mm/damon/sysfs.c b/mm/damon/sysfs.c
> new file mode 100644
> index 000000000000..e8087ffd924c
> --- /dev/null
> +++ b/mm/damon/sysfs.c
> @@ -0,0 +1,1059 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * DAMON sysfs Interface
> + *
> + * Copyright (c) 2022 SeongJae Park <sj@kernel.org>
> + */
> +
> +#include <linux/damon.h>
> +#include <linux/kobject.h>
> +#include <linux/pid.h>
> +#include <linux/sched.h>
> +#include <linux/slab.h>
> +
> +static DEFINE_MUTEX(damon_sysfs_lock);
> +
> +/*
> + * unsigned long range directory
> + */
> +
> +struct damon_sysfs_ul_range {
> +	struct kobject kobj;
> +	unsigned long min;
> +	unsigned long max;
> +};
> +
> +static struct damon_sysfs_ul_range *damon_sysfs_ul_range_alloc(
> +		unsigned long min,
> +		unsigned long max)
> +{
> +	struct damon_sysfs_ul_range *range = kmalloc(sizeof(*range),
> +			GFP_KERNEL);
> +
> +	if (!range)
> +		return NULL;
> +	range->kobj = (struct kobject){};
> +	range->min = min;
> +	range->max = max;
> +
> +	return range;
> +}
> +
> +static ssize_t damon_sysfs_ul_range_min_show(struct kobject *kobj,
> +		struct kobj_attribute *attr, char *buf)
> +{
> +	struct damon_sysfs_ul_range *range = container_of(kobj,
> +			struct damon_sysfs_ul_range, kobj);
> +
> +	return sysfs_emit(buf, "%lu\n", range->min);
> +}
> +
> +static ssize_t damon_sysfs_ul_range_min_store(struct kobject *kobj,
> +		struct kobj_attribute *attr, const char *buf, size_t count)
> +{
> +	struct damon_sysfs_ul_range *range = container_of(kobj,
> +			struct damon_sysfs_ul_range, kobj);
> +	unsigned long min;
> +	int err;
> +
> +	err = kstrtoul(buf, 0, &min);
> +	if (err)
> +		return -EINVAL;
> +
> +	range->min = min;
> +	return count;
> +}
> +
> +static ssize_t damon_sysfs_ul_range_max_show(struct kobject *kobj,
> +		struct kobj_attribute *attr, char *buf)
> +{
> +	struct damon_sysfs_ul_range *range = container_of(kobj,
> +			struct damon_sysfs_ul_range, kobj);
> +
> +	return sysfs_emit(buf, "%lu\n", range->max);
> +}
> +
> +static ssize_t damon_sysfs_ul_range_max_store(struct kobject *kobj,
> +		struct kobj_attribute *attr, const char *buf, size_t count)
> +{
> +	struct damon_sysfs_ul_range *range = container_of(kobj,
> +			struct damon_sysfs_ul_range, kobj);
> +	unsigned long max;
> +	int err;
> +
> +	err = kstrtoul(buf, 0, &max);
> +	if (err)
> +		return -EINVAL;
> +
> +	range->max = max;
> +	return count;
> +}
> +
> +static void damon_sysfs_ul_range_release(struct kobject *kobj)
> +{
> +	kfree(container_of(kobj, struct damon_sysfs_ul_range, kobj));
> +}
> +
> +static struct kobj_attribute damon_sysfs_ul_range_min_attr =
> +		__ATTR(min, 0600, damon_sysfs_ul_range_min_show,
> +				damon_sysfs_ul_range_min_store);
> +
> +static struct kobj_attribute damon_sysfs_ul_range_max_attr =
> +		__ATTR(max, 0600, damon_sysfs_ul_range_max_show,
> +				damon_sysfs_ul_range_max_store);
> +
> +static struct attribute *damon_sysfs_ul_range_attrs[] = {
> +	&damon_sysfs_ul_range_min_attr.attr,
> +	&damon_sysfs_ul_range_max_attr.attr,
> +	NULL,
> +};
> +ATTRIBUTE_GROUPS(damon_sysfs_ul_range);
> +
> +static struct kobj_type damon_sysfs_ul_range_ktype = {
> +	.release = damon_sysfs_ul_range_release,
> +	.sysfs_ops = &kobj_sysfs_ops,
> +	.default_groups = damon_sysfs_ul_range_groups,
> +};
> +
> +/*
> + * target directory
> + */
> +
> +struct damon_sysfs_target {
> +	struct kobject kobj;
> +	int pid;
> +};
> +
> +static struct damon_sysfs_target *damon_sysfs_target_alloc(void)
> +{
> +	return kzalloc(sizeof(struct damon_sysfs_target), GFP_KERNEL);
> +}
> +
> +static ssize_t damon_sysfs_target_pid_show(struct kobject *kobj,
> +		struct kobj_attribute *attr, char *buf)
> +{
> +	struct damon_sysfs_target *target = container_of(kobj,
> +			struct damon_sysfs_target, kobj);
> +
> +	return sysfs_emit(buf, "%d\n", target->pid);
> +}
> +
> +static ssize_t damon_sysfs_target_pid_store(struct kobject *kobj,
> +		struct kobj_attribute *attr, const char *buf, size_t count)
> +{
> +	struct damon_sysfs_target *target = container_of(kobj,
> +			struct damon_sysfs_target, kobj);
> +	int err = kstrtoint(buf, 0, &target->pid);
> +
> +	if (err)
> +		return -EINVAL;
> +	return count;
> +}
> +
> +static void damon_sysfs_target_release(struct kobject *kobj)
> +{
> +	kfree(container_of(kobj, struct damon_sysfs_target, kobj));
> +}
> +
> +static struct kobj_attribute damon_sysfs_target_pid_attr = __ATTR(pid, 0600,
> +		damon_sysfs_target_pid_show, damon_sysfs_target_pid_store);
> +
> +static struct attribute *damon_sysfs_target_attrs[] = {
> +	&damon_sysfs_target_pid_attr.attr,
> +	NULL,
> +};
> +ATTRIBUTE_GROUPS(damon_sysfs_target);
> +
> +static struct kobj_type damon_sysfs_target_ktype = {
> +	.release = damon_sysfs_target_release,
> +	.sysfs_ops = &kobj_sysfs_ops,
> +	.default_groups = damon_sysfs_target_groups,
> +};
> +
> +/*
> + * targets directory
> + */
> +
> +struct damon_sysfs_targets {
> +	struct kobject kobj;
> +	struct damon_sysfs_target **targets_arr;
> +	int nr;
> +};
> +
> +static struct damon_sysfs_targets *damon_sysfs_targets_alloc(void)
> +{
> +	return kzalloc(sizeof(struct damon_sysfs_targets), GFP_KERNEL);
> +}
> +
> +static void damon_sysfs_targets_rm_dirs(struct damon_sysfs_targets *targets)
> +{
> +	struct damon_sysfs_target **targets_arr = targets->targets_arr;
> +	int i;
> +
> +	for (i = 0; i < targets->nr; i++)
> +		kobject_put(&targets_arr[i]->kobj);
> +	targets->nr = 0;
> +	kfree(targets_arr);
> +	targets->targets_arr = NULL;
> +}
> +
> +static int damon_sysfs_targets_add_dirs(struct damon_sysfs_targets *targets,
> +		int nr_targets)
> +{
> +	struct damon_sysfs_target **targets_arr, *target;
> +	int err, i;
> +
> +	damon_sysfs_targets_rm_dirs(targets);
> +	if (!nr_targets)
> +		return 0;
> +
> +	targets_arr = kmalloc_array(nr_targets, sizeof(*targets_arr),
> +			GFP_KERNEL | __GFP_NOWARN);
> +	if (!targets_arr)
> +		return -ENOMEM;
> +	targets->targets_arr = targets_arr;
> +
> +	for (i = 0; i < nr_targets; i++) {
> +		target = damon_sysfs_target_alloc();
> +		if (!target) {
> +			damon_sysfs_targets_rm_dirs(targets);
> +			return -ENOMEM;
> +		}
> +
> +		err = kobject_init_and_add(&target->kobj,
> +				&damon_sysfs_target_ktype, &targets->kobj,
> +				"%d", i);
> +		if (err)
> +			goto out;
> +
> +		targets_arr[i] = target;
> +		targets->nr++;
> +	}
> +	return 0;
> +
> +out:
> +	damon_sysfs_targets_rm_dirs(targets);
> +	kobject_put(&target->kobj);
> +	return err;
> +}
> +
> +static ssize_t damon_sysfs_targets_nr_show(struct kobject *kobj,
> +		struct kobj_attribute *attr, char *buf)
> +{
> +	struct damon_sysfs_targets *targets = container_of(kobj,
> +			struct damon_sysfs_targets, kobj);
> +
> +	return sysfs_emit(buf, "%d\n", targets->nr);
> +}
> +
> +static ssize_t damon_sysfs_targets_nr_store(struct kobject *kobj,
> +		struct kobj_attribute *attr, const char *buf, size_t count)
> +{
> +	struct damon_sysfs_targets *targets = container_of(kobj,
> +			struct damon_sysfs_targets, kobj);
> +	int nr, err = kstrtoint(buf, 0, &nr);
> +
> +	if (err)
> +		return err;
> +	if (nr < 0)
> +		return -EINVAL;
> +
> +	if (!mutex_trylock(&damon_sysfs_lock))
> +		return -EBUSY;
> +	err = damon_sysfs_targets_add_dirs(targets, nr);
> +	mutex_unlock(&damon_sysfs_lock);
> +	if (err)
> +		return err;
> +
> +	return count;
> +}
> +
> +static void damon_sysfs_targets_release(struct kobject *kobj)
> +{
> +	kfree(container_of(kobj, struct damon_sysfs_targets, kobj));
> +}
> +
> +static struct kobj_attribute damon_sysfs_targets_nr_attr = __ATTR(nr, 0600,
> +		damon_sysfs_targets_nr_show, damon_sysfs_targets_nr_store);
> +
> +static struct attribute *damon_sysfs_targets_attrs[] = {
> +	&damon_sysfs_targets_nr_attr.attr,
> +	NULL,
> +};
> +ATTRIBUTE_GROUPS(damon_sysfs_targets);
> +
> +static struct kobj_type damon_sysfs_targets_ktype = {
> +	.release = damon_sysfs_targets_release,
> +	.sysfs_ops = &kobj_sysfs_ops,
> +	.default_groups = damon_sysfs_targets_groups,
> +};
> +
> +/*
> + * intervals directory
> + */
> +
> +struct damon_sysfs_intervals {
> +	struct kobject kobj;
> +	unsigned long sample_us;
> +	unsigned long aggr_us;
> +	unsigned long update_us;
> +};
> +
> +static struct damon_sysfs_intervals *damon_sysfs_intervals_alloc(
> +		unsigned long sample_us, unsigned long aggr_us,
> +		unsigned long update_us)
> +{
> +	struct damon_sysfs_intervals *intervals = kmalloc(sizeof(*intervals),
> +			GFP_KERNEL);
> +
> +	if (!intervals)
> +		return NULL;
> +
> +	intervals->kobj = (struct kobject){};
> +	intervals->sample_us = sample_us;
> +	intervals->aggr_us = aggr_us;
> +	intervals->update_us = update_us;
> +	return intervals;
> +}
> +
> +static ssize_t damon_sysfs_intervals_sample_us_show(struct kobject *kobj,
> +		struct kobj_attribute *attr, char *buf)
> +{
> +	struct damon_sysfs_intervals *intervals = container_of(kobj,
> +			struct damon_sysfs_intervals, kobj);
> +
> +	return sysfs_emit(buf, "%lu\n", intervals->sample_us);
> +}
> +
> +static ssize_t damon_sysfs_intervals_sample_us_store(struct kobject *kobj,
> +		struct kobj_attribute *attr, const char *buf, size_t count)
> +{
> +	struct damon_sysfs_intervals *intervals = container_of(kobj,
> +			struct damon_sysfs_intervals, kobj);
> +	unsigned long us;
> +	int err = kstrtoul(buf, 0, &us);
> +
> +	if (err)
> +		return -EINVAL;
> +
> +	intervals->sample_us = us;
> +	return count;
> +}
> +
> +static ssize_t damon_sysfs_intervals_aggr_us_show(struct kobject *kobj,
> +		struct kobj_attribute *attr, char *buf)
> +{
> +	struct damon_sysfs_intervals *intervals = container_of(kobj,
> +			struct damon_sysfs_intervals, kobj);
> +
> +	return sysfs_emit(buf, "%lu\n", intervals->aggr_us);
> +}
> +
> +static ssize_t damon_sysfs_intervals_aggr_us_store(struct kobject *kobj,
> +		struct kobj_attribute *attr, const char *buf, size_t count)
> +{
> +	struct damon_sysfs_intervals *intervals = container_of(kobj,
> +			struct damon_sysfs_intervals, kobj);
> +	unsigned long us;
> +	int err = kstrtoul(buf, 0, &us);
> +
> +	if (err)
> +		return -EINVAL;
> +
> +	intervals->aggr_us = us;
> +	return count;
> +}
> +
> +static ssize_t damon_sysfs_intervals_update_us_show(struct kobject *kobj,
> +		struct kobj_attribute *attr, char *buf)
> +{
> +	struct damon_sysfs_intervals *intervals = container_of(kobj,
> +			struct damon_sysfs_intervals, kobj);
> +
> +	return sysfs_emit(buf, "%lu\n", intervals->update_us);
> +}
> +
> +static ssize_t damon_sysfs_intervals_update_us_store(struct kobject *kobj,
> +		struct kobj_attribute *attr, const char *buf, size_t count)
> +{
> +	struct damon_sysfs_intervals *intervals = container_of(kobj,
> +			struct damon_sysfs_intervals, kobj);
> +	unsigned long us;
> +	int err = kstrtoul(buf, 0, &us);
> +
> +	if (err)
> +		return -EINVAL;
> +
> +	intervals->update_us = us;
> +	return count;
> +}
> +
> +static void damon_sysfs_intervals_release(struct kobject *kobj)
> +{
> +	kfree(container_of(kobj, struct damon_sysfs_intervals, kobj));
> +}
> +
> +static struct kobj_attribute damon_sysfs_intervals_sample_us_attr =
> +		__ATTR(sample_us, 0600,
> +				damon_sysfs_intervals_sample_us_show,
> +				damon_sysfs_intervals_sample_us_store);
> +
> +static struct kobj_attribute damon_sysfs_intervals_aggr_us_attr =
> +		__ATTR(aggr_us, 0600,
> +				damon_sysfs_intervals_aggr_us_show,
> +				damon_sysfs_intervals_aggr_us_store);
> +
> +static struct kobj_attribute damon_sysfs_intervals_update_us_attr =
> +		__ATTR(update_us, 0600,
> +				damon_sysfs_intervals_update_us_show,
> +				damon_sysfs_intervals_update_us_store);
> +
> +static struct attribute *damon_sysfs_intervals_attrs[] = {
> +	&damon_sysfs_intervals_sample_us_attr.attr,
> +	&damon_sysfs_intervals_aggr_us_attr.attr,
> +	&damon_sysfs_intervals_update_us_attr.attr,
> +	NULL,
> +};
> +ATTRIBUTE_GROUPS(damon_sysfs_intervals);
> +
> +static struct kobj_type damon_sysfs_intervals_ktype = {
> +	.release = damon_sysfs_intervals_release,
> +	.sysfs_ops = &kobj_sysfs_ops,
> +	.default_groups = damon_sysfs_intervals_groups,
> +};
> +
> +/*
> + * monitoring_attrs directory
> + */
> +
> +struct damon_sysfs_attrs {
> +	struct kobject kobj;
> +	struct damon_sysfs_intervals *intervals;
> +	struct damon_sysfs_ul_range *nr_regions_range;
> +};
> +
> +static struct damon_sysfs_attrs *damon_sysfs_attrs_alloc(void)
> +{
> +	struct damon_sysfs_attrs *attrs = kmalloc(sizeof(*attrs), GFP_KERNEL);
> +
> +	if (!attrs)
> +		return NULL;
> +	attrs->kobj = (struct kobject){};
> +	return attrs;
> +}
> +
> +static int damon_sysfs_attrs_add_dirs(struct damon_sysfs_attrs *attrs)
> +{
> +	struct damon_sysfs_intervals *intervals;
> +	struct damon_sysfs_ul_range *nr_regions_range;
> +	int err;
> +
> +	intervals = damon_sysfs_intervals_alloc(5000, 100000, 60000000);
> +	if (!intervals)
> +		return -ENOMEM;
> +
> +	err = kobject_init_and_add(&intervals->kobj,
> +			&damon_sysfs_intervals_ktype, &attrs->kobj,
> +			"intervals");
> +	if (err)
> +		goto put_intervals_out;
> +	attrs->intervals = intervals;
> +
> +	nr_regions_range = damon_sysfs_ul_range_alloc(10, 1000);
> +	if (!nr_regions_range)
> +		goto put_intervals_out;
> +
> +	err = kobject_init_and_add(&nr_regions_range->kobj,
> +			&damon_sysfs_ul_range_ktype, &attrs->kobj,
> +			"nr_regions");
> +	if (err)
> +		goto put_nr_regions_intervals_out;
> +	attrs->nr_regions_range = nr_regions_range;
> +	return 0;
> +
> +put_nr_regions_intervals_out:
> +	kobject_put(&nr_regions_range->kobj);
> +	attrs->nr_regions_range = NULL;
> +put_intervals_out:
> +	kobject_put(&intervals->kobj);
> +	attrs->intervals = NULL;
> +	return err;
> +}
> +
> +static void damon_sysfs_attrs_rm_dirs(struct damon_sysfs_attrs *attrs)
> +{
> +	kobject_put(&attrs->nr_regions_range->kobj);
> +	kobject_put(&attrs->intervals->kobj);
> +}
> +
> +static void damon_sysfs_attrs_release(struct kobject *kobj)
> +{
> +	kfree(container_of(kobj, struct damon_sysfs_attrs, kobj));
> +}
> +
> +static struct attribute *damon_sysfs_attrs_attrs[] = {
> +	NULL,
> +};
> +ATTRIBUTE_GROUPS(damon_sysfs_attrs);
> +
> +static struct kobj_type damon_sysfs_attrs_ktype = {
> +	.release = damon_sysfs_attrs_release,
> +	.sysfs_ops = &kobj_sysfs_ops,
> +	.default_groups = damon_sysfs_attrs_groups,
> +};
> +
> +/*
> + * context directory
> + */
> +
> +/* This should match with enum damon_ops_id */
> +static const char * const damon_sysfs_ops_strs[] = {
> +	"vaddr",
> +	"paddr",
> +};
> +
> +struct damon_sysfs_context {
> +	struct kobject kobj;
> +	enum damon_ops_id ops_id;
> +	struct damon_sysfs_attrs *attrs;
> +	struct damon_sysfs_targets *targets;
> +};
> +
> +static struct damon_sysfs_context *damon_sysfs_context_alloc(
> +		enum damon_ops_id ops_id)
> +{
> +	struct damon_sysfs_context *context = kmalloc(sizeof(*context),
> +				GFP_KERNEL);
> +
> +	if (!context)
> +		return NULL;
> +	context->kobj = (struct kobject){};
> +	context->ops_id = ops_id;
> +	return context;
> +}
> +
> +static int damon_sysfs_context_set_attrs(struct damon_sysfs_context *context)
> +{
> +	struct damon_sysfs_attrs *attrs = damon_sysfs_attrs_alloc();
> +	int err;
> +
> +	if (!attrs)
> +		return -ENOMEM;
> +	err = kobject_init_and_add(&attrs->kobj, &damon_sysfs_attrs_ktype,
> +			&context->kobj, "monitoring_attrs");
> +	if (err)
> +		goto out;
> +	err = damon_sysfs_attrs_add_dirs(attrs);
> +	if (err)
> +		goto out;
> +	context->attrs = attrs;
> +	return 0;
> +
> +out:
> +	kobject_put(&attrs->kobj);
> +	return err;
> +}
> +
> +static int damon_sysfs_context_set_targets(struct damon_sysfs_context *context)
> +{
> +	struct damon_sysfs_targets *targets = damon_sysfs_targets_alloc();
> +	int err;
> +
> +	if (!targets)
> +		return -ENOMEM;
> +	err = kobject_init_and_add(&targets->kobj, &damon_sysfs_targets_ktype,
> +			&context->kobj, "targets");
> +	if (err) {
> +		kobject_put(&targets->kobj);
> +		return err;
> +	}
> +	context->targets = targets;
> +	return 0;
> +}
> +
> +static int damon_sysfs_context_add_dirs(struct damon_sysfs_context *context)
> +{
> +	int err;
> +
> +	err = damon_sysfs_context_set_attrs(context);
> +	if (err)
> +		return err;
> +
> +	err = damon_sysfs_context_set_targets(context);
> +	if (err)
> +		goto put_attrs_out;
> +
> +	return 0;
> +
> +put_attrs_out:
> +	kobject_put(&context->attrs->kobj);
> +	context->attrs = NULL;
> +	return err;
> +}
> +
> +static void damon_sysfs_context_rm_dirs(struct damon_sysfs_context *context)
> +{
> +	damon_sysfs_attrs_rm_dirs(context->attrs);
> +	kobject_put(&context->attrs->kobj);
> +	damon_sysfs_targets_rm_dirs(context->targets);
> +	kobject_put(&context->targets->kobj);
> +}
> +
> +static ssize_t damon_sysfs_context_operations_show(struct kobject *kobj,
> +		struct kobj_attribute *attr, char *buf)
> +{
> +	struct damon_sysfs_context *context = container_of(kobj,
> +			struct damon_sysfs_context, kobj);
> +
> +	return sysfs_emit(buf, "%s\n", damon_sysfs_ops_strs[context->ops_id]);
> +}
> +
> +static ssize_t damon_sysfs_context_operations_store(struct kobject *kobj,
> +		struct kobj_attribute *attr, const char *buf, size_t count)
> +{
> +	struct damon_sysfs_context *context = container_of(kobj,
> +			struct damon_sysfs_context, kobj);
> +
> +	if (sysfs_streq(buf, damon_sysfs_ops_strs[DAMON_OPS_VADDR])) {
> +		context->ops_id = DAMON_OPS_VADDR;
> +		return count;
> +	}
> +	return -EINVAL;
> +}
> +
> +static void damon_sysfs_context_release(struct kobject *kobj)
> +{
> +	kfree(container_of(kobj, struct damon_sysfs_context, kobj));
> +}
> +
> +static struct kobj_attribute damon_sysfs_context_operations_attr = __ATTR(
> +		operations, 0600, damon_sysfs_context_operations_show,
> +		damon_sysfs_context_operations_store);
> +
> +static struct attribute *damon_sysfs_context_attrs[] = {
> +	&damon_sysfs_context_operations_attr.attr,
> +	NULL,
> +};
> +ATTRIBUTE_GROUPS(damon_sysfs_context);
> +
> +static struct kobj_type damon_sysfs_context_ktype = {
> +	.release = damon_sysfs_context_release,
> +	.sysfs_ops = &kobj_sysfs_ops,
> +	.default_groups = damon_sysfs_context_groups,
> +};
> +
> +/*
> + * contexts directory
> + */
> +
> +struct damon_sysfs_contexts {
> +	struct kobject kobj;
> +	struct damon_sysfs_context **contexts_arr;
> +	int nr;
> +};
> +
> +static struct damon_sysfs_contexts *damon_sysfs_contexts_alloc(void)
> +{
> +	return kzalloc(sizeof(struct damon_sysfs_contexts), GFP_KERNEL);
> +}
> +
> +static void damon_sysfs_contexts_rm_dirs(struct damon_sysfs_contexts *contexts)
> +{
> +	struct damon_sysfs_context **contexts_arr = contexts->contexts_arr;
> +	int i;
> +
> +	for (i = 0; i < contexts->nr; i++) {
> +		damon_sysfs_context_rm_dirs(contexts_arr[i]);
> +		kobject_put(&contexts_arr[i]->kobj);
> +	}
> +	contexts->nr = 0;
> +	kfree(contexts_arr);
> +	contexts->contexts_arr = NULL;
> +}
> +
> +static int damon_sysfs_contexts_add_dirs(struct damon_sysfs_contexts *contexts,
> +		int nr_contexts)
> +{
> +	struct damon_sysfs_context **contexts_arr, *context;
> +	int err, i;
> +
> +	damon_sysfs_contexts_rm_dirs(contexts);
> +	if (!nr_contexts)
> +		return 0;
> +
> +	contexts_arr = kmalloc_array(nr_contexts, sizeof(*contexts_arr),
> +			GFP_KERNEL | __GFP_NOWARN);
> +	if (!contexts_arr)
> +		return -ENOMEM;
> +	contexts->contexts_arr = contexts_arr;
> +
> +	for (i = 0; i < nr_contexts; i++) {
> +		context = damon_sysfs_context_alloc(DAMON_OPS_VADDR);
> +		if (!context) {
> +			damon_sysfs_contexts_rm_dirs(contexts);
> +			return -ENOMEM;
> +		}
> +
> +		err = kobject_init_and_add(&context->kobj,
> +				&damon_sysfs_context_ktype, &contexts->kobj,
> +				"%d", i);
> +		if (err)
> +			goto out;
> +
> +		err = damon_sysfs_context_add_dirs(context);
> +		if (err)
> +			goto out;
> +
> +		contexts_arr[i] = context;
> +		contexts->nr++;
> +	}
> +	return 0;
> +
> +out:
> +	damon_sysfs_contexts_rm_dirs(contexts);
> +	kobject_put(&context->kobj);
> +	return err;
> +}
> +
> +static ssize_t damon_sysfs_contexts_nr_show(struct kobject *kobj,
> +		struct kobj_attribute *attr, char *buf)
> +{
> +	struct damon_sysfs_contexts *contexts = container_of(kobj,
> +			struct damon_sysfs_contexts, kobj);
> +
> +	return sysfs_emit(buf, "%d\n", contexts->nr);
> +}
> +
> +static ssize_t damon_sysfs_contexts_nr_store(struct kobject *kobj,
> +		struct kobj_attribute *attr, const char *buf, size_t count)
> +{
> +	struct damon_sysfs_contexts *contexts = container_of(kobj,
> +			struct damon_sysfs_contexts, kobj);
> +	int nr, err;
> +
> +	err = kstrtoint(buf, 0, &nr);
> +	if (err)
> +		return err;
> +	/* TODO: support multiple contexts per kdamond */
> +	if (nr < 0 || 1 < nr)
> +		return -EINVAL;
> +
> +	if (!mutex_trylock(&damon_sysfs_lock))
> +		return -EBUSY;
> +	err = damon_sysfs_contexts_add_dirs(contexts, nr);
> +	mutex_unlock(&damon_sysfs_lock);
> +	if (err)
> +		return err;
> +
> +	return count;
> +}
> +
> +static void damon_sysfs_contexts_release(struct kobject *kobj)
> +{
> +	kfree(container_of(kobj, struct damon_sysfs_contexts, kobj));
> +}
> +
> +static struct kobj_attribute damon_sysfs_contexts_nr_attr = __ATTR(nr, 0600,
> +		damon_sysfs_contexts_nr_show, damon_sysfs_contexts_nr_store);
> +
> +static struct attribute *damon_sysfs_contexts_attrs[] = {
> +	&damon_sysfs_contexts_nr_attr.attr,
> +	NULL,
> +};
> +ATTRIBUTE_GROUPS(damon_sysfs_contexts);
> +
> +static struct kobj_type damon_sysfs_contexts_ktype = {
> +	.release = damon_sysfs_contexts_release,
> +	.sysfs_ops = &kobj_sysfs_ops,
> +	.default_groups = damon_sysfs_contexts_groups,
> +};
> +
> +/*
> + * kdamond directory
> + */
> +
> +struct damon_sysfs_kdamond {
> +	struct kobject kobj;
> +	struct damon_sysfs_contexts *contexts;
> +};
> +
> +static struct damon_sysfs_kdamond *damon_sysfs_kdamond_alloc(void)
> +{
> +	return kzalloc(sizeof(struct damon_sysfs_kdamond), GFP_KERNEL);
> +}
> +
> +static int damon_sysfs_kdamond_add_dirs(struct damon_sysfs_kdamond *kdamond)
> +{
> +	struct damon_sysfs_contexts *contexts;
> +	int err;
> +
> +	contexts = damon_sysfs_contexts_alloc();
> +	if (!contexts)
> +		return -ENOMEM;
> +
> +	err = kobject_init_and_add(&contexts->kobj,
> +			&damon_sysfs_contexts_ktype, &kdamond->kobj,
> +			"contexts");
> +	if (err) {
> +		kobject_put(&contexts->kobj);
> +		return err;
> +	}
> +	kdamond->contexts = contexts;
> +
> +	return err;
> +}
> +
> +static void damon_sysfs_kdamond_rm_dirs(struct damon_sysfs_kdamond *kdamond)
> +{
> +	damon_sysfs_contexts_rm_dirs(kdamond->contexts);
> +	kobject_put(&kdamond->contexts->kobj);
> +}
> +
> +static ssize_t damon_sysfs_kdamond_state_show(struct kobject *kobj,
> +		struct kobj_attribute *attr, char *buf)
> +{
> +	return sysfs_emit(buf, "off\n");
> +}
> +
> +static ssize_t damon_sysfs_kdamond_state_store(struct kobject *kobj,
> +		struct kobj_attribute *attr, const char *buf, size_t count)
> +{
> +	return count;
> +}
> +
> +static ssize_t damon_sysfs_kdamond_pid_show(struct kobject *kobj,
> +		struct kobj_attribute *attr, char *buf)
> +{
> +	return sysfs_emit(buf, "-1\n");
> +}
> +
> +static void damon_sysfs_kdamond_release(struct kobject *kobj)
> +{
> +	kfree(container_of(kobj, struct damon_sysfs_kdamond, kobj));
> +}
> +
> +static struct kobj_attribute damon_sysfs_kdamond_state_attr =
> +	__ATTR(state, 0600, damon_sysfs_kdamond_state_show,
> +		damon_sysfs_kdamond_state_store);
> +
> +static struct kobj_attribute damon_sysfs_kdamond_pid_attr = __ATTR(pid, 0400,
> +		damon_sysfs_kdamond_pid_show, NULL);
> +
> +static struct attribute *damon_sysfs_kdamond_attrs[] = {
> +	&damon_sysfs_kdamond_state_attr.attr,
> +	&damon_sysfs_kdamond_pid_attr.attr,
> +	NULL,
> +};
> +ATTRIBUTE_GROUPS(damon_sysfs_kdamond);
> +
> +static struct kobj_type damon_sysfs_kdamond_ktype = {
> +	.release = damon_sysfs_kdamond_release,
> +	.sysfs_ops = &kobj_sysfs_ops,
> +	.default_groups = damon_sysfs_kdamond_groups,
> +};
> +
> +/*
> + * kdamonds directory
> + */
> +
> +struct damon_sysfs_kdamonds {
> +	struct kobject kobj;
> +	struct damon_sysfs_kdamond **kdamonds_arr;
> +	int nr;
> +};
> +
> +static struct damon_sysfs_kdamonds *damon_sysfs_kdamonds_alloc(void)
> +{
> +	return kzalloc(sizeof(struct damon_sysfs_kdamonds), GFP_KERNEL);
> +}
> +
> +static void damon_sysfs_kdamonds_rm_dirs(struct damon_sysfs_kdamonds *kdamonds)
> +{
> +	struct damon_sysfs_kdamond **kdamonds_arr = kdamonds->kdamonds_arr;
> +	int i;
> +
> +	for (i = 0; i < kdamonds->nr; i++) {
> +		damon_sysfs_kdamond_rm_dirs(kdamonds_arr[i]);
> +		kobject_put(&kdamonds_arr[i]->kobj);
> +	}
> +	kdamonds->nr = 0;
> +	kfree(kdamonds_arr);
> +	kdamonds->kdamonds_arr = NULL;
> +}
> +
> +static int damon_sysfs_kdamonds_add_dirs(struct damon_sysfs_kdamonds *kdamonds,
> +		int nr_kdamonds)
> +{
> +	struct damon_sysfs_kdamond **kdamonds_arr, *kdamond;
> +	int err, i;
> +
> +	damon_sysfs_kdamonds_rm_dirs(kdamonds);
> +	if (!nr_kdamonds)
> +		return 0;
> +
> +	kdamonds_arr = kmalloc_array(nr_kdamonds, sizeof(*kdamonds_arr),
> +			GFP_KERNEL | __GFP_NOWARN);
> +	if (!kdamonds_arr)
> +		return -ENOMEM;
> +	kdamonds->kdamonds_arr = kdamonds_arr;
> +
> +	for (i = 0; i < nr_kdamonds; i++) {
> +		kdamond = damon_sysfs_kdamond_alloc();
> +		if (!kdamond) {
> +			damon_sysfs_kdamonds_rm_dirs(kdamonds);
> +			return -ENOMEM;
> +		}
> +
> +		err = kobject_init_and_add(&kdamond->kobj,
> +				&damon_sysfs_kdamond_ktype, &kdamonds->kobj,
> +				"%d", i);
> +		if (err)
> +			goto out;
> +
> +		err = damon_sysfs_kdamond_add_dirs(kdamond);
> +		if (err)
> +			goto out;
> +
> +		kdamonds_arr[i] = kdamond;
> +		kdamonds->nr++;
> +	}
> +	return 0;
> +
> +out:
> +	damon_sysfs_kdamonds_rm_dirs(kdamonds);
> +	kobject_put(&kdamond->kobj);
> +	return err;
> +}
> +
> +static ssize_t damon_sysfs_kdamonds_nr_show(struct kobject *kobj,
> +		struct kobj_attribute *attr, char *buf)
> +{
> +	struct damon_sysfs_kdamonds *kdamonds = container_of(kobj,
> +			struct damon_sysfs_kdamonds, kobj);
> +
> +	return sysfs_emit(buf, "%d\n", kdamonds->nr);
> +}
> +
> +static ssize_t damon_sysfs_kdamonds_nr_store(struct kobject *kobj,
> +		struct kobj_attribute *attr, const char *buf, size_t count)
> +{
> +	struct damon_sysfs_kdamonds *kdamonds = container_of(kobj,
> +			struct damon_sysfs_kdamonds, kobj);
> +	int nr, err;
> +
> +	err = kstrtoint(buf, 0, &nr);
> +	if (err)
> +		return err;
> +	if (nr < 0)
> +		return -EINVAL;
> +
> +	if (!mutex_trylock(&damon_sysfs_lock))
> +		return -EBUSY;
> +	err = damon_sysfs_kdamonds_add_dirs(kdamonds, nr);
> +	mutex_unlock(&damon_sysfs_lock);
> +	if (err)
> +		return err;
> +
> +	return count;
> +}
> +
> +static void damon_sysfs_kdamonds_release(struct kobject *kobj)
> +{
> +	kfree(container_of(kobj, struct damon_sysfs_kdamonds, kobj));
> +}
> +
> +static struct kobj_attribute damon_sysfs_kdamonds_nr_attr = __ATTR(nr, 0600,
> +		damon_sysfs_kdamonds_nr_show, damon_sysfs_kdamonds_nr_store);
> +
> +static struct attribute *damon_sysfs_kdamonds_attrs[] = {
> +	&damon_sysfs_kdamonds_nr_attr.attr,
> +	NULL,
> +};
> +ATTRIBUTE_GROUPS(damon_sysfs_kdamonds);
> +
> +static struct kobj_type damon_sysfs_kdamonds_ktype = {
> +	.release = damon_sysfs_kdamonds_release,
> +	.sysfs_ops = &kobj_sysfs_ops,
> +	.default_groups = damon_sysfs_kdamonds_groups,
> +};
> +
> +/*
> + * damon user interface directory
> + */
> +
> +struct damon_sysfs_ui_dir {
> +	struct kobject kobj;
> +	struct damon_sysfs_kdamonds *kdamonds;
> +};
> +
> +static struct damon_sysfs_ui_dir *damon_sysfs_ui_dir_alloc(void)
> +{
> +	return kzalloc(sizeof(struct damon_sysfs_ui_dir), GFP_KERNEL);
> +}
> +
> +static int damon_sysfs_ui_dir_add_dirs(struct damon_sysfs_ui_dir *ui_dir)
> +{
> +	struct damon_sysfs_kdamonds *kdamonds;
> +	int err;
> +
> +	kdamonds = damon_sysfs_kdamonds_alloc();
> +	if (!kdamonds)
> +		return -ENOMEM;
> +
> +	err = kobject_init_and_add(&kdamonds->kobj,
> +			&damon_sysfs_kdamonds_ktype, &ui_dir->kobj,
> +			"kdamonds");
> +	if (err) {
> +		kobject_put(&kdamonds->kobj);
> +		return err;
> +	}
> +	ui_dir->kdamonds = kdamonds;
> +	return err;
> +}
> +
> +static void damon_sysfs_ui_dir_release(struct kobject *kobj)
> +{
> +	kfree(container_of(kobj, struct damon_sysfs_ui_dir, kobj));
> +}
> +
> +static struct attribute *damon_sysfs_ui_dir_attrs[] = {
> +	NULL,
> +};
> +ATTRIBUTE_GROUPS(damon_sysfs_ui_dir);
> +
> +static struct kobj_type damon_sysfs_ui_dir_ktype = {
> +	.release = damon_sysfs_ui_dir_release,
> +	.sysfs_ops = &kobj_sysfs_ops,
> +	.default_groups = damon_sysfs_ui_dir_groups,
> +};
> +
> +static int __init damon_sysfs_init(void)
> +{
> +	struct kobject *damon_sysfs_root;
> +	struct damon_sysfs_ui_dir *admin;
> +	int err;
> +
> +	damon_sysfs_root = kobject_create_and_add("damon", mm_kobj);
> +	if (!damon_sysfs_root)
> +		return -ENOMEM;
> +
> +	admin = damon_sysfs_ui_dir_alloc();
> +	if (!admin) {
> +		kobject_put(damon_sysfs_root);
> +		return -ENOMEM;
> +	}
> +	err = kobject_init_and_add(&admin->kobj, &damon_sysfs_ui_dir_ktype,
> +			damon_sysfs_root, "admin");
> +	if (err)
> +		goto out;
> +	err = damon_sysfs_ui_dir_add_dirs(admin);
> +	if (err)
> +		goto out;
> +	return 0;
> +
> +out:
> +	kobject_put(&admin->kobj);
> +	kobject_put(damon_sysfs_root);
> +	return err;
> +}
> +subsys_initcall(damon_sysfs_init);

-- 
Best Regards!
Xin Hao


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 00/12] Introduce DAMON sysfs interface
  2022-02-23 15:20 [PATCH 00/12] Introduce DAMON sysfs interface SeongJae Park
                   ` (12 preceding siblings ...)
  2022-02-23 16:07 ` [PATCH 00/12] Introduce " Greg KH
@ 2022-02-25  7:32 ` xhao
  2022-02-25  8:05   ` SeongJae Park
  13 siblings, 1 reply; 24+ messages in thread
From: xhao @ 2022-02-25  7:32 UTC (permalink / raw)
  To: SeongJae Park, akpm
  Cc: corbet, skhan, rientjes, linux-damon, linux-mm, linux-doc,
	linux-kselftest, linux-kernel

Hi SeongJae:

On 2/23/22 11:20 PM, SeongJae Park wrote:
> Chages from Previous Version (RFC)
> ==================================
>
> Compared to the RFC version of this patchset
> (https://lore.kernel.org/linux-mm/20220217161938.8874-1-sj@kernel.org/), this
> version contains below changes.
>
> - Implement all DAMON debugfs interface providing features
> - Writeup documents
> - Add more selftests
>
> Introduction
> ============
>
> DAMON's debugfs-based user interface (DAMON_DBGFS) 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
> inefficient, hard to be used, and 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 DAMON_DBGFS 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
> having one dedicated file for each 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/operations
>      # echo 1 > kdamonds/0/contexts/0/targets/nr
>      # echo $(pidof <workload>) > kdamonds/0/contexts/0/targets/0/pid
>      # echo on > kdamonds/0/state
>
> A brief representation of the files hierarchy of DAMON sysfs interface is as
> below.  Childs are represented with indentation, directories are having '/'
> suffix, and files in each directory are separated by comma.
>
>      /sys/kernel/mm/damon/admin
>      │ kdamonds/nr
>      │ │ 0/state,pid
>      │ │ │ contexts/nr
>      │ │ │ │ 0/operations
>      │ │ │ │ │ monitoring_attrs/
>      │ │ │ │ │ │ intervals/sample_us,aggr_us,update_us
>      │ │ │ │ │ │ nr_regions/min,max
>      │ │ │ │ │ targets/nr
>      │ │ │ │ │ │ 0/pid
>      │ │ │ │ │ │ │ regions/nr
>      │ │ │ │ │ │ │ │ 0/start,end
>      │ │ │ │ │ │ │ │ ...
>      │ │ │ │ │ │ ...
>      │ │ │ │ │ schemes/nr
>      │ │ │ │ │ 0/action
>      │ │ │ │ │ │ access_pattern/
>      │ │ │ │ │ │ │ sz/min,max
>      │ │ │ │ │ │ │ nr_accesses/min,max
>      │ │ │ │ │ │ │ age/min,max
>      │ │ │ │ │ │ quotas/ms,sz,reset_interval_ms
>      │ │ │ │ │ │ │ weights/sz,nr_accesses,age
>      │ │ │ │ │ │ watermarks/metric,interval_us,high,mid,low
>      │ │ │ │ │ │ stats/nr_tried,sz_tried,nr_applied,sz_applied,qt_exceeds
>      │ │ │ │ │ ...
>      │ │ ...
>
> Detailed usage of the files will be described in the final Documentation patch
> of this patchset.

The introduction of the sys DAMON interface makes DAMON seem more 
hierarchical, but it brings a problem. From a user's perspective,

I find it difficult to operate. For example:

step one:

echo xxx > /sys/kernel/mm/damon/admin/kdamonds/0/contexts/0/targets/nr

step two:

echo /sys/kernel/mm/damon/admin/kdamonds/0/contexts/0/targets/nr/1/pid

echo /sys/kernel/mm/damon/admin/kdamonds/0/contexts/0/targets/nr/0/pid

.........

Alas, it is really too troublesome to operate, can you make it as simple 
as possible, perhaps by referring to the implementation of cgroup.

> Main Difference Between DAMON_DBGFS and DAMON_SYSFS
> ---------------------------------------------------
>
> At the moment, DAMON_DBGFS and DAMON_SYSFS provides same features.  One
> important difference between them is their exclusiveness.  DAMON_DBGFS works in
> an exclusive manner, so that no DAMON worker thread (kdamond) in the system can
> run concurrently and interfere somehow.  For the reason, DAMON_DBGFS asks users
> to construct all monitoring contexts and start them at once.  It's not a big
> problem but makes the operation a little bit complex and unflexible.
>
> For more flexible usage, DAMON_SYSFS moves the responsibility of preventing any
> possible interference to the admins and work in a non-exclusive manner.  That
> is, users can configure and start contexts one by one.  Note that DAMON
> respects both exclusive groups and non-exclusive groups of contexts, in a
> manner similar to that of reader-writer locks.  That is, if any exclusive
> monitoring contexts (e.g., contexts that started via DAMON_DBGFS) are running,
> DAMON_SYSFS does not start new contexts, and vice versa.
>
> Future Plan of DAMON_DBGFS Deprecation
> ======================================
>
> Once this patchset is merged, DAMON_DBGFS development will be frozen.  That is,
> we will maintain it to work as is now so that no users will be break.  But, it
> will not be extended to provide any new feature of DAMON.  The support will be
> continued only until next LTS release.  After that, we will drop DAMON_DBGFS.
>
> User-space Tooling Compatibility
> --------------------------------
>
> As DAMON_SYSFS provides all features of DAMON_DBGFS, all user space tooling can
> move to DAMON_SYSFS.  As we will continue supporting DAMON_DBGFS until next LTS
> kernel release, user space tools would have enough time to move to DAMON_SYSFS.
>
> The official user space tool, damo[1], is already supporting both DAMON_SYSFS
> and DAMON_DBGFS.  Both correctness tests[2] and performance tests[3] of DAMON
> using DAMON_SYSFS also passed.
>
> [1] https://github.com/awslabs/damo
> [2] https://github.com/awslabs/damon-tests/tree/master/corr
> [3] https://github.com/awslabs/damon-tests/tree/master/perf
>
> Complete Git Tree
> =================
>
> You can get the complete git tree from
> https://git.kernel.org/sj/h/damon/sysfs/patches/v1.
>
> Sequence of Patches
> ===================
>
> First two patches (patches 1-2) make core changes for DAMON_SYSFS.  The first
> one (patch 1) allows non-exclusive DAMON contexts so that DAMON_SYSFS can work
> in non-exclusive mode, while the second one (patch 2) adds size of DAMON enum
> types so that DAMON API users can safely iterate the enums.
>
> Third patch (patch 3) implements basic sysfs stub for virtual address spaces
> monitoring.  Note that this implements only sysfs files and DAMON is not
> linked.  Fourth patch (patch 4) links the DAMON_SYSFS to DAMON so that users
> can control DAMON using the sysfs files.
>
> Following six patches (patches 5-10) implements other DAMON features that
> DAMON_DBGFS supports one by one (physical address space monitoring, DAMON-based
> operation schemes, schemes quotas, schemes prioritization weights, schemes
> watermarks, and schemes stats).
>
> Following patch (patch 11) adds a simple selftest for DAMON_SYSFS, and the
> final one (patch 12) documents DAMON_SYSFS.
>
> SeongJae Park (12):
>    mm/damon/core: Allow non-exclusive DAMON start/stop
>    mm/damon/core: Add number of each enum type values
>    mm/damon: Implement a minimal stub for sysfs-based DAMON interface
>    mm/damon/sysfs: Link DAMON for virtual address spaces monitoring
>    mm/damon/sysfs: Support physical address space monitoring
>    mm/damon/sysfs: Support DAMON-based Operation Schemes
>    mm/damon/sysfs: Support DAMOS quotas
>    mm/damon/sysfs: Support schemes prioritization weights
>    mm/damon/sysfs: Support DAMOS watermarks
>    mm/damon/sysfs: Support DAMOS stats
>    selftests/damon: Add a test for DAMON sysfs interface
>    Docs/admin-guide/mm/damon/usage: Document DAMON sysfs interface
>
>   Documentation/admin-guide/mm/damon/usage.rst |  349 ++-
>   include/linux/damon.h                        |    6 +-
>   mm/damon/Kconfig                             |    7 +
>   mm/damon/Makefile                            |    1 +
>   mm/damon/core.c                              |   23 +-
>   mm/damon/dbgfs.c                             |    2 +-
>   mm/damon/reclaim.c                           |    2 +-
>   mm/damon/sysfs.c                             | 2684 ++++++++++++++++++
>   tools/testing/selftests/damon/Makefile       |    1 +
>   tools/testing/selftests/damon/sysfs.sh       |  306 ++
>   10 files changed, 3364 insertions(+), 17 deletions(-)
>   create mode 100644 mm/damon/sysfs.c
>   create mode 100755 tools/testing/selftests/damon/sysfs.sh
>
-- 
Best Regards!
Xin Hao


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 00/12] Introduce DAMON sysfs interface
  2022-02-25  7:32 ` xhao
@ 2022-02-25  8:05   ` SeongJae Park
  0 siblings, 0 replies; 24+ messages in thread
From: SeongJae Park @ 2022-02-25  8:05 UTC (permalink / raw)
  To: xhao
  Cc: SeongJae Park, akpm, corbet, skhan, rientjes, linux-damon,
	linux-mm, linux-doc, linux-kselftest, linux-kernel

Hi Xin,

On Fri, 25 Feb 2022 15:32:47 +0800 xhao@linux.alibaba.com wrote:

> Hi SeongJae:
> 
> On 2/23/22 11:20 PM, SeongJae Park wrote:
[...]
> > Introduction
> > ============
> >
> > DAMON's debugfs-based user interface (DAMON_DBGFS) 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
> > inefficient, hard to be used, and 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 DAMON_DBGFS 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
> > having one dedicated file for each 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/operations
> >      # echo 1 > kdamonds/0/contexts/0/targets/nr
> >      # echo $(pidof <workload>) > kdamonds/0/contexts/0/targets/0/pid
> >      # echo on > kdamonds/0/state
> >
> > A brief representation of the files hierarchy of DAMON sysfs interface is as
> > below.  Childs are represented with indentation, directories are having '/'
> > suffix, and files in each directory are separated by comma.
> >
> >      /sys/kernel/mm/damon/admin
> >      │ kdamonds/nr
> >      │ │ 0/state,pid
> >      │ │ │ contexts/nr
> >      │ │ │ │ 0/operations
> >      │ │ │ │ │ monitoring_attrs/
> >      │ │ │ │ │ │ intervals/sample_us,aggr_us,update_us
> >      │ │ │ │ │ │ nr_regions/min,max
> >      │ │ │ │ │ targets/nr
> >      │ │ │ │ │ │ 0/pid
> >      │ │ │ │ │ │ │ regions/nr
> >      │ │ │ │ │ │ │ │ 0/start,end
> >      │ │ │ │ │ │ │ │ ...
> >      │ │ │ │ │ │ ...
> >      │ │ │ │ │ schemes/nr
> >      │ │ │ │ │ 0/action
> >      │ │ │ │ │ │ access_pattern/
> >      │ │ │ │ │ │ │ sz/min,max
> >      │ │ │ │ │ │ │ nr_accesses/min,max
> >      │ │ │ │ │ │ │ age/min,max
> >      │ │ │ │ │ │ quotas/ms,sz,reset_interval_ms
> >      │ │ │ │ │ │ │ weights/sz,nr_accesses,age
> >      │ │ │ │ │ │ watermarks/metric,interval_us,high,mid,low
> >      │ │ │ │ │ │ stats/nr_tried,sz_tried,nr_applied,sz_applied,qt_exceeds
> >      │ │ │ │ │ ...
> >      │ │ ...
> >
> > Detailed usage of the files will be described in the final Documentation patch
> > of this patchset.
> 
> The introduction of the sys DAMON interface makes DAMON seem more 
> hierarchical, but it brings a problem. From a user's perspective,
> 
> I find it difficult to operate. For example:
> 
> step one:
> 
> echo xxx > /sys/kernel/mm/damon/admin/kdamonds/0/contexts/0/targets/nr
> 
> step two:
> 
> echo /sys/kernel/mm/damon/admin/kdamonds/0/contexts/0/targets/nr/1/pid
> 
> echo /sys/kernel/mm/damon/admin/kdamonds/0/contexts/0/targets/nr/0/pid
> 
> .........
> 
> Alas, it is really too troublesome to operate, can you make it as simple 
> as possible, perhaps by referring to the implementation of cgroup.

Thank you very much for the great comments.  I agree that this interface
requires quite a redundant works.  Nevertheless, this interface is not aimed to
be used by human hand but user space tools.  We provide the DAMON user-space
tool, damo, for the purpose.  Damo already supports this interface while
introducing nearly-zero change to the end user interface.  All you need to do
to use sysfs in background with damo is adding '--damon_interface sysfs' to the
command.

I guess someone might still want low level sysfs control for development and
testing purpose.  For the case, damo is providing a new subcommand, fs[1], for
more low level sysfs control with better interface.  It allows users to
read/write all hierarchies and values in DAMON sysfs via json format.  For
example:

    # ./damo/damo fs --damon_interface sysfs read
    {
        "kdamonds": {
            "0": {
                "contexts": {
                    "nr_contexts": "0\n"
                },
                "pid": "-1\n",
                "state": "off\n"
            },
            "nr_kdamonds": "1\n"
        }
    }
    # cat content.json
    {
        "kdamonds": {
            "0": {
                "contexts": {
                    "nr_contexts": "1\n"
                }
            }
        }
    }
    # ./damo/damo fs --damon_interface sysfs write --content "$(cat content.json)"
    # ./damo/damo fs --damon_interface sysfs read
    {
        "kdamonds": {
            "0": {
                "contexts": {
                    "0": {
                        "monitoring_attrs": {
                            "intervals": {
                                "aggr_us": "100000\n",
                                "sample_us": "5000\n",
                                "update_us": "60000000\n"
                            },
                            "nr_regions": {
                                "max": "1000\n",
                                "min": "10\n"
                            }
                        },
                        "operations": "vaddr\n",
                        "schemes": {
                            "nr_schemes": "0\n"
                        },
                        "targets": {
                            "nr_targets": "0\n"
                        }
                    },
                    "nr_contexts": "1\n"
                },
                "pid": "-1\n",
                "state": "off\n"
            },
            "nr_kdamonds": "1\n"
        }
    }

I admit damo interface is still not perfect.  It has many rooms for
improvement.

If even damo is too heavyweight for you, you could use some general scripts
that can do above work in similar manner:
https://github.com/sjp38/lazybox/blob/master/scripts/fs.py


Thanks,
SJ

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 03/12] mm/damon: Implement a minimal stub for sysfs-based DAMON interface
  2022-02-25  7:21   ` xhao
@ 2022-02-25  8:10     ` SeongJae Park
  0 siblings, 0 replies; 24+ messages in thread
From: SeongJae Park @ 2022-02-25  8:10 UTC (permalink / raw)
  To: xhao
  Cc: SeongJae Park, akpm, corbet, skhan, rientjes, linux-damon,
	linux-mm, linux-doc, linux-kselftest, linux-kernel

Hi Xin,

On Fri, 25 Feb 2022 15:21:05 +0800 xhao@linux.alibaba.com wrote:

> Hi, SeongJae:
> 
> On 2/23/22 11:20 PM, SeongJae Park wrote:
> > 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 separated by
> > white spaces.  As a result, it is ineffient, hard to be used, and
> > 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.
> >
> > To this end, this commit implements a stub of a part of the new user
> > interface of DAMON using sysfs.  Specifically, this commit implements
> > the sysfs control parts for virtual address space monitoring.
> >
> > More specifically, the idea of the new interface is, using directory
> > hierarchies and making one file for one value.  The hierarchy that this
> > commit is introducing is as below.  In the below figure,
> > parents-children relations are represented with indentations, each
> > directory is having ``/`` suffix, and files in each directory are
> > separated by comma (",").
> >
> >      /sys/kernel/mm/damon/admin
> >      │ kdamonds/nr
> >      │ │ 0/state,pid
> >      │ │ │ contexts/nr
> >      │ │ │ │ 0/operations
> >      │ │ │ │ │ monitoring_attrs/
> >      │ │ │ │ │ │ intervals/sample_us,aggr_us,update_us
> >      │ │ │ │ │ │ nr_regions/min,max
> >      │ │ │ │ │ targets/nr
> >      │ │ │ │ │ │ 0/pid
> >      │ │ │ │ │ │ ...
> >      │ │ │ │ ...
> >      │ │ ...
> 
> >
> > Writing a number <N> to each 'nr' file makes directories of name <0> to
> > <N-1> in the directory of the 'nr' file.  That's all this commit does.
> > Writing proper values to relevant files will construct the DAMON
> > contexts, and writing a special keyword, 'on', to 'state' files for each
> > kdamond will ask DAMON to start the constructed contexts.
> >
> > For a short example, using below commands for
> > monitoring virtual address spaces of a given workload is imaginable:
> >
> >      # 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
> 
> I do some test  about the sys interface, like this:
> 
> [root@rt2k03395 0]# tree
> .
> ├── contexts
> │   ├── 0
> │   │   ├── monitoring_attrs
> │   │   │   ├── intervals
> │   │   │   │   ├── aggr_us
> │   │   │   │   ├── sample_us
> │   │   │   │   └── update_us
> │   │   │   └── nr_regions
> │   │   │       ├── max
> │   │   │       └── min
> │   │   ├── operations
> │   │   ├── schemes
> │   │   │   └── nr
> │   │   └── targets
> │   │       ├── 0
> │   │       │   ├── pid
> │   │       │   └── regions
> │   │       │       ├── 0
> │   │       │       │   ├── end
> │   │       │       │   └── start
> │   │       │       ├── 1
> │   │       │       │   ├── end
> │   │       │       │   └── start
> │   │       │       ├── 10
> │   │       │       │   ├── end
> │   │       │       │   └── start
> │   │       │       ├── 11
> │   │       │       │   ├── end
> │   │       │       │   └── start
> │   │       │       ├── 12
> 
> cd regions/
> [root@rt2k03395 regions]# ls
> 0  10  12  14  16  18  2   21  23  25  27  29  30  32  34  36  38 4   
> 41  43  45  47  49  6  8  nr
> 1  11  13  15  17  19  20  22  24  26  28  3   31  33  35  37  39 40  
> 42  44  46  48  5   7  9
> [root@rt2k03395 regions]# cd 44/cat *
> 
> [root@rt2k03395 regions/44]# cat *
> 0  0
> 
> I'm skeptical about the number regions ? And after manually setting the 
> number of nr, the processing of
> 
> "start" and "end" will be very troublesome,I guess you might want to do 
> some special region addresses,
> 
> such as hot or cold region, Is that true ?

The purpose of regions/ directory is for supporting the initial monitoring
regions feature of debugfs, which is optional for virtual address spaces
monitoring, but essential for physical address space monitoing.  If you need to
monitor only virtual address spaces, you don't need to populate the directory.

In a future, we could add nr_accesses and age files under each region directory
and apply the monitoring results there.


> But I think you need to think 
> about how do you deal with too many
> 
> uncontacted reigons that need to be done.

Sysfs interface is not aimed to be used by human hand but user space tools, and
we provide a reference tool, damo.  Please consider using that or implement
your own.  You could also refer to my reply to your other email for this point:
https://lore.kernel.org/linux-mm/20220225080513.1908-1-sj@kernel.org/


Thanks,
SJ
[...]

^ permalink raw reply	[flat|nested] 24+ messages in thread

end of thread, other threads:[~2022-02-25  8:10 UTC | newest]

Thread overview: 24+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-02-23 15:20 [PATCH 00/12] Introduce DAMON sysfs interface SeongJae Park
2022-02-23 15:20 ` [PATCH 01/12] mm/damon/core: Allow non-exclusive DAMON start/stop SeongJae Park
2022-02-23 15:20 ` [PATCH 02/12] mm/damon/core: Add number of each enum type values SeongJae Park
2022-02-23 15:20 ` [PATCH 03/12] mm/damon: Implement a minimal stub for sysfs-based DAMON interface SeongJae Park
2022-02-23 16:09   ` Greg KH
2022-02-23 16:45     ` SeongJae Park
2022-02-23 17:13       ` SeongJae Park
2022-02-23 18:33         ` Greg KH
2022-02-23 19:03           ` SeongJae Park
2022-02-25  7:21   ` xhao
2022-02-25  8:10     ` SeongJae Park
2022-02-23 15:20 ` [PATCH 04/12] mm/damon/sysfs: Link DAMON for virtual address spaces monitoring SeongJae Park
2022-02-23 15:20 ` [PATCH 05/12] mm/damon/sysfs: Support physical address space monitoring SeongJae Park
2022-02-23 15:20 ` [PATCH 06/12] mm/damon/sysfs: Support DAMON-based Operation Schemes SeongJae Park
2022-02-23 15:20 ` [PATCH 07/12] mm/damon/sysfs: Support DAMOS quotas SeongJae Park
2022-02-23 15:20 ` [PATCH 08/12] mm/damon/sysfs: Support schemes prioritization weights SeongJae Park
2022-02-23 15:20 ` [PATCH 09/12] mm/damon/sysfs: Support DAMOS watermarks SeongJae Park
2022-02-23 15:20 ` [PATCH 10/12] mm/damon/sysfs: Support DAMOS stats SeongJae Park
2022-02-23 15:20 ` [PATCH 11/12] selftests/damon: Add a test for DAMON sysfs interface SeongJae Park
2022-02-23 15:20 ` [PATCH 12/12] Docs/admin-guide/mm/damon/usage: Document " SeongJae Park
2022-02-23 16:07 ` [PATCH 00/12] Introduce " Greg KH
2022-02-23 16:44   ` SeongJae Park
2022-02-25  7:32 ` xhao
2022-02-25  8:05   ` SeongJae Park

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).