2022-02-24 01:21:51

by SeongJae Park

[permalink] [raw]
Subject: [PATCH 00/12] Introduce DAMON sysfs interface

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

Compared to the RFC version of this patchset
(https://lore.kernel.org/linux-mm/[email protected]/), 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


2022-02-24 01:49:47

by SeongJae Park

[permalink] [raw]
Subject: [PATCH 06/12] mm/damon/sysfs: Support DAMON-based Operation Schemes

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 <[email protected]>
---
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

2022-02-24 01:53:55

by SeongJae Park

[permalink] [raw]
Subject: [PATCH 09/12] mm/damon/sysfs: Support DAMOS watermarks

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 <[email protected]>
---
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

2022-02-24 01:55:36

by SeongJae Park

[permalink] [raw]
Subject: [PATCH 08/12] mm/damon/sysfs: Support schemes prioritization weights

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 <[email protected]>
---
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

2022-02-25 12:43:37

by haoxin

[permalink] [raw]
Subject: Re: [PATCH 00/12] Introduce DAMON sysfs interface

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/[email protected]/), 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

2022-02-25 19:13:39

by SeongJae Park

[permalink] [raw]
Subject: Re: [PATCH 00/12] Introduce DAMON sysfs interface

Hi Xin,

On Fri, 25 Feb 2022 15:32:47 +0800 [email protected] 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