2021-11-06 04:47:57 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
/*
|
|
|
|
* DAMON-based page reclamation
|
|
|
|
*
|
|
|
|
* Author: SeongJae Park <sj@kernel.org>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define pr_fmt(fmt) "damon-reclaim: " fmt
|
|
|
|
|
|
|
|
#include <linux/damon.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
|
2022-09-14 01:44:38 +08:00
|
|
|
#include "modules-common.h"
|
|
|
|
|
2021-11-06 04:47:57 +08:00
|
|
|
#ifdef MODULE_PARAM_PREFIX
|
|
|
|
#undef MODULE_PARAM_PREFIX
|
|
|
|
#endif
|
|
|
|
#define MODULE_PARAM_PREFIX "damon_reclaim."
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Enable or disable DAMON_RECLAIM.
|
|
|
|
*
|
|
|
|
* You can enable DAMON_RCLAIM by setting the value of this parameter as ``Y``.
|
|
|
|
* Setting it as ``N`` disables DAMON_RECLAIM. Note that DAMON_RECLAIM could
|
|
|
|
* do no real monitoring and reclamation due to the watermarks-based activation
|
|
|
|
* condition. Refer to below descriptions for the watermarks parameter for
|
|
|
|
* this.
|
|
|
|
*/
|
|
|
|
static bool enabled __read_mostly;
|
|
|
|
|
2022-05-10 09:20:56 +08:00
|
|
|
/*
|
|
|
|
* Make DAMON_RECLAIM reads the input parameters again, except ``enabled``.
|
|
|
|
*
|
|
|
|
* Input parameters that updated while DAMON_RECLAIM is running are not applied
|
|
|
|
* by default. Once this parameter is set as ``Y``, DAMON_RECLAIM reads values
|
|
|
|
* of parametrs except ``enabled`` again. Once the re-reading is done, this
|
|
|
|
* parameter is set as ``N``. If invalid parameters are found while the
|
|
|
|
* re-reading, DAMON_RECLAIM will be disabled.
|
|
|
|
*/
|
|
|
|
static bool commit_inputs __read_mostly;
|
|
|
|
module_param(commit_inputs, bool, 0600);
|
|
|
|
|
2021-11-06 04:47:57 +08:00
|
|
|
/*
|
|
|
|
* Time threshold for cold memory regions identification in microseconds.
|
|
|
|
*
|
|
|
|
* If a memory region is not accessed for this or longer time, DAMON_RECLAIM
|
|
|
|
* identifies the region as cold, and reclaims. 120 seconds by default.
|
|
|
|
*/
|
|
|
|
static unsigned long min_age __read_mostly = 120000000;
|
|
|
|
module_param(min_age, ulong, 0600);
|
|
|
|
|
2022-09-14 01:44:47 +08:00
|
|
|
static struct damos_quota damon_reclaim_quota = {
|
|
|
|
/* use up to 10 ms time, reclaim up to 128 MiB per 1 sec by default */
|
|
|
|
.ms = 10,
|
|
|
|
.sz = 128 * 1024 * 1024,
|
|
|
|
.reset_interval = 1000,
|
|
|
|
/* Within the quota, page out older regions first. */
|
|
|
|
.weight_sz = 0,
|
|
|
|
.weight_nr_accesses = 0,
|
|
|
|
.weight_age = 1
|
|
|
|
};
|
|
|
|
DEFINE_DAMON_MODULES_DAMOS_QUOTAS(damon_reclaim_quota);
|
2021-11-06 04:47:57 +08:00
|
|
|
|
2022-09-15 10:10:23 +08:00
|
|
|
static struct damos_watermarks damon_reclaim_wmarks = {
|
2022-09-14 01:44:41 +08:00
|
|
|
.metric = DAMOS_WMARK_FREE_MEM_RATE,
|
|
|
|
.interval = 5000000, /* 5 seconds */
|
|
|
|
.high = 500, /* 50 percent */
|
|
|
|
.mid = 400, /* 40 percent */
|
|
|
|
.low = 200, /* 20 percent */
|
|
|
|
};
|
|
|
|
DEFINE_DAMON_MODULES_WMARKS_PARAMS(damon_reclaim_wmarks);
|
2021-11-06 04:47:57 +08:00
|
|
|
|
2022-09-14 01:44:34 +08:00
|
|
|
static struct damon_attrs damon_reclaim_mon_attrs = {
|
2022-09-14 01:44:38 +08:00
|
|
|
.sample_interval = 5000, /* 5 ms */
|
|
|
|
.aggr_interval = 100000, /* 100 ms */
|
2022-09-14 01:44:34 +08:00
|
|
|
.ops_update_interval = 0,
|
|
|
|
.min_nr_regions = 10,
|
|
|
|
.max_nr_regions = 1000,
|
|
|
|
};
|
2022-09-14 01:44:38 +08:00
|
|
|
DEFINE_DAMON_MODULES_MON_ATTRS_PARAMS(damon_reclaim_mon_attrs);
|
2021-11-06 04:47:57 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Start of the target memory region in physical address.
|
|
|
|
*
|
|
|
|
* The start physical address of memory region that DAMON_RECLAIM will do work
|
|
|
|
* against. By default, biggest System RAM is used as the region.
|
|
|
|
*/
|
|
|
|
static unsigned long monitor_region_start __read_mostly;
|
|
|
|
module_param(monitor_region_start, ulong, 0600);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* End of the target memory region in physical address.
|
|
|
|
*
|
|
|
|
* The end physical address of memory region that DAMON_RECLAIM will do work
|
|
|
|
* against. By default, biggest System RAM is used as the region.
|
|
|
|
*/
|
|
|
|
static unsigned long monitor_region_end __read_mostly;
|
|
|
|
module_param(monitor_region_end, ulong, 0600);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* PID of the DAMON thread
|
|
|
|
*
|
|
|
|
* If DAMON_RECLAIM is enabled, this becomes the PID of the worker thread.
|
|
|
|
* Else, -1.
|
|
|
|
*/
|
|
|
|
static int kdamond_pid __read_mostly = -1;
|
|
|
|
module_param(kdamond_pid, int, 0400);
|
|
|
|
|
2022-09-14 01:44:43 +08:00
|
|
|
static struct damos_stat damon_reclaim_stat;
|
|
|
|
DEFINE_DAMON_MODULES_DAMOS_STATS_PARAMS(damon_reclaim_stat,
|
|
|
|
reclaim_tried_regions, reclaimed_regions, quota_exceeds);
|
2022-01-15 06:10:23 +08:00
|
|
|
|
2021-11-06 04:47:57 +08:00
|
|
|
static struct damon_ctx *ctx;
|
|
|
|
static struct damon_target *target;
|
|
|
|
|
|
|
|
static struct damos *damon_reclaim_new_scheme(void)
|
|
|
|
{
|
2022-09-09 03:14:43 +08:00
|
|
|
struct damos_access_pattern pattern = {
|
|
|
|
/* Find regions having PAGE_SIZE or larger size */
|
|
|
|
.min_sz_region = PAGE_SIZE,
|
|
|
|
.max_sz_region = ULONG_MAX,
|
|
|
|
/* and not accessed at all */
|
|
|
|
.min_nr_accesses = 0,
|
|
|
|
.max_nr_accesses = 0,
|
|
|
|
/* for min_age or more micro-seconds */
|
2022-09-14 01:44:34 +08:00
|
|
|
.min_age_region = min_age /
|
|
|
|
damon_reclaim_mon_attrs.aggr_interval,
|
2022-09-09 03:14:43 +08:00
|
|
|
.max_age_region = UINT_MAX,
|
|
|
|
};
|
|
|
|
|
|
|
|
return damon_new_scheme(
|
|
|
|
&pattern,
|
2021-11-06 04:47:57 +08:00
|
|
|
/* page out those, as soon as found */
|
|
|
|
DAMOS_PAGEOUT,
|
|
|
|
/* under the quota. */
|
2022-09-14 01:44:47 +08:00
|
|
|
&damon_reclaim_quota,
|
2021-11-06 04:47:57 +08:00
|
|
|
/* (De)activate this according to the watermarks. */
|
2022-09-14 01:44:41 +08:00
|
|
|
&damon_reclaim_wmarks);
|
2021-11-06 04:47:57 +08:00
|
|
|
}
|
|
|
|
|
2022-05-10 09:20:56 +08:00
|
|
|
static int damon_reclaim_apply_parameters(void)
|
2021-11-06 04:47:57 +08:00
|
|
|
{
|
|
|
|
struct damos *scheme;
|
2022-05-10 09:20:56 +08:00
|
|
|
int err = 0;
|
2021-11-06 04:47:57 +08:00
|
|
|
|
2022-09-14 01:44:34 +08:00
|
|
|
err = damon_set_attrs(ctx, &damon_reclaim_mon_attrs);
|
2021-11-06 04:47:57 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2022-05-10 09:20:56 +08:00
|
|
|
/* Will be freed by next 'damon_set_schemes()' below */
|
|
|
|
scheme = damon_reclaim_new_scheme();
|
|
|
|
if (!scheme)
|
|
|
|
return -ENOMEM;
|
2022-09-16 23:20:35 +08:00
|
|
|
damon_set_schemes(ctx, &scheme, 1);
|
2022-05-10 09:20:56 +08:00
|
|
|
|
2022-09-21 00:53:22 +08:00
|
|
|
return damon_set_region_biggest_system_ram_default(target,
|
|
|
|
&monitor_region_start,
|
|
|
|
&monitor_region_end);
|
2022-05-10 09:20:56 +08:00
|
|
|
}
|
2021-11-06 04:47:57 +08:00
|
|
|
|
2022-05-10 09:20:56 +08:00
|
|
|
static int damon_reclaim_turn(bool on)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (!on) {
|
|
|
|
err = damon_stop(&ctx, 1);
|
|
|
|
if (!err)
|
|
|
|
kdamond_pid = -1;
|
|
|
|
return err;
|
2021-11-06 04:47:57 +08:00
|
|
|
}
|
2022-05-10 09:20:56 +08:00
|
|
|
|
|
|
|
err = damon_reclaim_apply_parameters();
|
2021-11-06 04:47:57 +08:00
|
|
|
if (err)
|
2022-05-10 09:20:56 +08:00
|
|
|
return err;
|
2021-11-06 04:47:57 +08:00
|
|
|
|
mm/damon/core: allow non-exclusive DAMON start/stop
Patch series "Introduce DAMON sysfs interface", v3.
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_kdamonds
# echo 1 > kdamonds/0/contexts/nr_contexts
# echo vaddr > kdamonds/0/contexts/0/operations
# echo 1 > kdamonds/0/contexts/0/targets/nr_targets
# echo $(pidof <workload>) > kdamonds/0/contexts/0/targets/0/pid_target
# 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_kdamonds
│ │ 0/state,pid
│ │ │ contexts/nr_contexts
│ │ │ │ 0/operations
│ │ │ │ │ monitoring_attrs/
│ │ │ │ │ │ intervals/sample_us,aggr_us,update_us
│ │ │ │ │ │ nr_regions/min,max
│ │ │ │ │ targets/nr_targets
│ │ │ │ │ │ 0/pid_target
│ │ │ │ │ │ │ regions/nr_regions
│ │ │ │ │ │ │ │ 0/start,end
│ │ │ │ │ │ │ │ ...
│ │ │ │ │ │ ...
│ │ │ │ │ schemes/nr_schemes
│ │ │ │ │ │ 0/action
│ │ │ │ │ │ │ access_pattern/
│ │ │ │ │ │ │ │ sz/min,max
│ │ │ │ │ │ │ │ nr_accesses/min,max
│ │ │ │ │ │ │ │ age/min,max
│ │ │ │ │ │ │ quotas/ms,bytes,reset_interval_ms
│ │ │ │ │ │ │ │ weights/sz_permil,nr_accesses_permil,age_permil
│ │ │ │ │ │ │ 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
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.
This patch (of 13):
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.
Link: https://lkml.kernel.org/r/20220228081314.5770-1-sj@kernel.org
Link: https://lkml.kernel.org/r/20220228081314.5770-2-sj@kernel.org
Signed-off-by: SeongJae Park <sj@kernel.org>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Shuah Khan <skhan@linuxfoundation.org>
Cc: David Rientjes <rientjes@google.com>
Cc: Xin Hao <xhao@linux.alibaba.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2022-03-23 05:49:21 +08:00
|
|
|
err = damon_start(&ctx, 1, true);
|
2022-05-10 09:20:56 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
kdamond_pid = ctx->kdamond->pid;
|
|
|
|
return 0;
|
2021-11-06 04:47:57 +08:00
|
|
|
}
|
|
|
|
|
2022-06-07 02:23:10 +08:00
|
|
|
static int damon_reclaim_enabled_store(const char *val,
|
2022-04-30 05:37:00 +08:00
|
|
|
const struct kernel_param *kp)
|
|
|
|
{
|
2022-10-26 01:36:47 +08:00
|
|
|
bool is_enabled = enabled;
|
|
|
|
bool enable;
|
|
|
|
int err;
|
2022-04-30 05:37:00 +08:00
|
|
|
|
2022-10-26 01:36:47 +08:00
|
|
|
err = strtobool(val, &enable);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2022-04-30 05:37:00 +08:00
|
|
|
|
2022-10-26 01:36:47 +08:00
|
|
|
if (is_enabled == enable)
|
|
|
|
return 0;
|
2022-06-05 03:50:51 +08:00
|
|
|
|
2022-10-26 01:36:47 +08:00
|
|
|
/* Called before init function. The function will handle this. */
|
|
|
|
if (!ctx)
|
|
|
|
goto set_param_out;
|
|
|
|
|
|
|
|
err = damon_reclaim_turn(enable);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
set_param_out:
|
|
|
|
enabled = enable;
|
|
|
|
return err;
|
2022-04-30 05:37:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static const struct kernel_param_ops enabled_param_ops = {
|
2022-06-07 02:23:10 +08:00
|
|
|
.set = damon_reclaim_enabled_store,
|
2022-04-30 05:37:00 +08:00
|
|
|
.get = param_get_bool,
|
|
|
|
};
|
|
|
|
|
|
|
|
module_param_cb(enabled, &enabled_param_ops, &enabled, 0600);
|
|
|
|
MODULE_PARM_DESC(enabled,
|
|
|
|
"Enable or disable DAMON_RECLAIM (default: disabled)");
|
|
|
|
|
2022-06-07 02:23:07 +08:00
|
|
|
static int damon_reclaim_handle_commit_inputs(void)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (!commit_inputs)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
err = damon_reclaim_apply_parameters();
|
|
|
|
commit_inputs = false;
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2022-01-15 06:10:23 +08:00
|
|
|
static int damon_reclaim_after_aggregation(struct damon_ctx *c)
|
|
|
|
{
|
|
|
|
struct damos *s;
|
|
|
|
|
|
|
|
/* update the stats parameter */
|
2022-09-14 01:44:43 +08:00
|
|
|
damon_for_each_scheme(s, c)
|
|
|
|
damon_reclaim_stat = s->stat;
|
2022-05-10 09:20:56 +08:00
|
|
|
|
2022-06-07 02:23:07 +08:00
|
|
|
return damon_reclaim_handle_commit_inputs();
|
2022-05-10 09:20:56 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int damon_reclaim_after_wmarks_check(struct damon_ctx *c)
|
|
|
|
{
|
2022-06-07 02:23:07 +08:00
|
|
|
return damon_reclaim_handle_commit_inputs();
|
2022-01-15 06:10:23 +08:00
|
|
|
}
|
|
|
|
|
2021-11-06 04:47:57 +08:00
|
|
|
static int __init damon_reclaim_init(void)
|
|
|
|
{
|
2022-10-27 06:59:42 +08:00
|
|
|
int err = damon_modules_new_paddr_ctx_target(&ctx, &target);
|
2021-11-06 04:47:57 +08:00
|
|
|
|
2022-10-27 06:59:42 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
2022-03-23 05:48:55 +08:00
|
|
|
|
2022-05-10 09:20:56 +08:00
|
|
|
ctx->callback.after_wmarks_check = damon_reclaim_after_wmarks_check;
|
2022-01-15 06:10:23 +08:00
|
|
|
ctx->callback.after_aggregation = damon_reclaim_after_aggregation;
|
2021-11-06 04:47:57 +08:00
|
|
|
|
2022-10-26 01:36:47 +08:00
|
|
|
/* 'enabled' has set before this function, probably via command line */
|
|
|
|
if (enabled)
|
|
|
|
err = damon_reclaim_turn(true);
|
2022-06-05 03:50:51 +08:00
|
|
|
|
2022-10-26 01:36:47 +08:00
|
|
|
return err;
|
2021-11-06 04:47:57 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
module_init(damon_reclaim_init);
|