mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2026-08-09 06:14:34 +02:00
The comment on top of `struct damon_region` clearly says that
For any use case, @ar should be non-zero positive size.
which is now verified in damon_verify_new_region() if the kernel is built
with DAMON_DEBUG_SANITY.
The WARN_ONCE() can be triggered if the mtier sample module is enabled
before node{0,1}_{start,end}_addr have been properly initialized, which is
obviously not good.
------------[ cut here ]------------
start 0 >= end 0
WARNING: mm/damon/core.c:217 at damon_new_region+0xf4/0x118, CPU#59: bash/341468
Call trace:
damon_new_region+0xf4/0x118 (P)
damon_set_regions+0xfc/0x3c0
damon_sample_mtier_build_ctx+0xe8/0x3a8
damon_sample_mtier_start+0x1c/0x90
damon_sample_mtier_enable_store+0x98/0xb0
param_attr_store+0xb4/0x128
module_attr_store+0x2c/0x50
sysfs_kf_write+0x58/0x90
kernfs_fop_write_iter+0x16c/0x238
vfs_write+0x2c0/0x370
ksys_write+0x74/0x118
__arm64_sys_write+0x24/0x38
invoke_syscall+0xa8/0x118
el0_svc_common.constprop.0+0x48/0xf0
do_el0_svc+0x24/0x38
el0_svc+0x54/0x370
el0t_64_sync_handler+0xa0/0xe8
el0t_64_sync+0x1ac/0x1b0
---[ end trace 0000000000000000 ]---
Note that the same issue can happen if detect_node_addresses is true, and
node 0 or 1 is memoryless. Fix it together by checking the validity of
parameters right before damon_new_region() and fail early if they're
invalid.
Link: https://lore.kernel.org/20260629144432.133962-1-sj@kernel.org
Fixes: 82a08bde3c ("samples/damon: implement a DAMON module for memory tiering")
Signed-off-by: Zenghui Yu <yuzenghui@huawei.com>
Signed-off-by: SJ Park <sj@kernel.org>
Reviewed-by: SJ Park <sj@kernel.org>
Cc: <stable@vger.kernel.org> # 6.16.x
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
246 lines
5.8 KiB
C
246 lines
5.8 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* memory tiering: migrate cold pages in node 0 and hot pages in node 1 to node
|
|
* 1 and node 0, respectively. Adjust the hotness/coldness threshold aiming
|
|
* resulting 99.6 % node 0 utilization ratio.
|
|
*/
|
|
|
|
#define pr_fmt(fmt) "damon_sample_mtier: " fmt
|
|
|
|
#include <linux/damon.h>
|
|
#include <linux/init.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
|
|
#ifdef MODULE_PARAM_PREFIX
|
|
#undef MODULE_PARAM_PREFIX
|
|
#endif
|
|
#define MODULE_PARAM_PREFIX "damon_sample_mtier."
|
|
|
|
static unsigned long node0_start_addr __read_mostly;
|
|
module_param(node0_start_addr, ulong, 0600);
|
|
|
|
static unsigned long node0_end_addr __read_mostly;
|
|
module_param(node0_end_addr, ulong, 0600);
|
|
|
|
static unsigned long node1_start_addr __read_mostly;
|
|
module_param(node1_start_addr, ulong, 0600);
|
|
|
|
static unsigned long node1_end_addr __read_mostly;
|
|
module_param(node1_end_addr, ulong, 0600);
|
|
|
|
static unsigned long node0_mem_used_bp __read_mostly = 9970;
|
|
module_param(node0_mem_used_bp, ulong, 0600);
|
|
|
|
static unsigned long node0_mem_free_bp __read_mostly = 50;
|
|
module_param(node0_mem_free_bp, ulong, 0600);
|
|
|
|
static int damon_sample_mtier_enable_store(
|
|
const char *val, const struct kernel_param *kp);
|
|
|
|
static const struct kernel_param_ops enabled_param_ops = {
|
|
.set = damon_sample_mtier_enable_store,
|
|
.get = param_get_bool,
|
|
};
|
|
|
|
static bool enabled __read_mostly;
|
|
module_param_cb(enabled, &enabled_param_ops, &enabled, 0600);
|
|
MODULE_PARM_DESC(enabled, "Enable or disable DAMON_SAMPLE_MTIER");
|
|
|
|
static bool detect_node_addresses __read_mostly;
|
|
module_param(detect_node_addresses, bool, 0600);
|
|
|
|
static struct damon_ctx *ctxs[2];
|
|
|
|
struct region_range {
|
|
phys_addr_t start;
|
|
phys_addr_t end;
|
|
};
|
|
|
|
static int nid_to_phys(int target_node, struct region_range *range)
|
|
{
|
|
if (!node_online(target_node)) {
|
|
pr_err("NUMA node %d is not online\n", target_node);
|
|
return -EINVAL;
|
|
}
|
|
|
|
range->start = PFN_PHYS(node_start_pfn(target_node));
|
|
range->end = PFN_PHYS(node_end_pfn(target_node));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct damon_ctx *damon_sample_mtier_build_ctx(bool promote)
|
|
{
|
|
struct damon_ctx *ctx;
|
|
struct damon_attrs attrs;
|
|
struct damon_target *target;
|
|
struct damos *scheme;
|
|
struct damos_quota_goal *quota_goal;
|
|
struct damos_filter *filter;
|
|
struct region_range addr;
|
|
struct damon_addr_range range;
|
|
int ret;
|
|
|
|
ctx = damon_new_ctx();
|
|
if (!ctx)
|
|
return NULL;
|
|
attrs = (struct damon_attrs) {
|
|
.sample_interval = 5 * USEC_PER_MSEC,
|
|
.aggr_interval = 100 * USEC_PER_MSEC,
|
|
.ops_update_interval = 60 * USEC_PER_MSEC * MSEC_PER_SEC,
|
|
.min_nr_regions = 10,
|
|
.max_nr_regions = 1000,
|
|
};
|
|
|
|
/*
|
|
* auto-tune sampling and aggregation interval aiming 4% DAMON-observed
|
|
* accesses ratio, keeping sampling interval in [5ms, 10s] range.
|
|
*/
|
|
attrs.intervals_goal = (struct damon_intervals_goal) {
|
|
.access_bp = 400, .aggrs = 3,
|
|
.min_sample_us = 5000, .max_sample_us = 10000000,
|
|
};
|
|
if (damon_set_attrs(ctx, &attrs))
|
|
goto free_out;
|
|
if (damon_select_ops(ctx, DAMON_OPS_PADDR))
|
|
goto free_out;
|
|
|
|
target = damon_new_target();
|
|
if (!target)
|
|
goto free_out;
|
|
damon_add_target(ctx, target);
|
|
|
|
if (detect_node_addresses) {
|
|
ret = promote ? nid_to_phys(1, &addr) : nid_to_phys(0, &addr);
|
|
if (ret)
|
|
goto free_out;
|
|
} else {
|
|
addr.start = promote ? node1_start_addr : node0_start_addr;
|
|
addr.end = promote ? node1_end_addr : node0_end_addr;
|
|
}
|
|
|
|
if (addr.start >= addr.end)
|
|
goto free_out;
|
|
|
|
range.start = addr.start;
|
|
range.end = addr.end;
|
|
|
|
ret = damon_set_regions(target, &range, 1, DAMON_MIN_REGION_SZ);
|
|
if (ret)
|
|
goto free_out;
|
|
|
|
scheme = damon_new_scheme(
|
|
/* access pattern */
|
|
&(struct damos_access_pattern) {
|
|
.min_sz_region = PAGE_SIZE,
|
|
.max_sz_region = ULONG_MAX,
|
|
.min_nr_accesses = promote ? 1 : 0,
|
|
.max_nr_accesses = promote ? UINT_MAX : 0,
|
|
.min_age_region = 0,
|
|
.max_age_region = UINT_MAX},
|
|
/* action */
|
|
promote ? DAMOS_MIGRATE_HOT : DAMOS_MIGRATE_COLD,
|
|
1000000, /* apply interval (1s) */
|
|
&(struct damos_quota){
|
|
/* 200 MiB per sec by most */
|
|
.reset_interval = 1000,
|
|
.sz = 200 * 1024 * 1024,
|
|
/* ignore size of region when prioritizing */
|
|
.weight_sz = 0,
|
|
.weight_nr_accesses = 100,
|
|
.weight_age = 100,
|
|
},
|
|
&(struct damos_watermarks){},
|
|
promote ? 0 : 1); /* migrate target node id */
|
|
if (!scheme)
|
|
goto free_out;
|
|
damon_set_schemes(ctx, &scheme, 1);
|
|
quota_goal = damos_new_quota_goal(
|
|
promote ? DAMOS_QUOTA_NODE_MEM_USED_BP :
|
|
DAMOS_QUOTA_NODE_MEM_FREE_BP,
|
|
promote ? node0_mem_used_bp : node0_mem_free_bp);
|
|
if (!quota_goal)
|
|
goto free_out;
|
|
quota_goal->nid = 0;
|
|
damos_add_quota_goal(&scheme->quota, quota_goal);
|
|
filter = damos_new_filter(DAMOS_FILTER_TYPE_YOUNG, true, promote);
|
|
if (!filter)
|
|
goto free_out;
|
|
damos_add_filter(scheme, filter);
|
|
return ctx;
|
|
free_out:
|
|
damon_destroy_ctx(ctx);
|
|
return NULL;
|
|
}
|
|
|
|
static int damon_sample_mtier_start(void)
|
|
{
|
|
struct damon_ctx *ctx;
|
|
|
|
ctx = damon_sample_mtier_build_ctx(true);
|
|
if (!ctx)
|
|
return -ENOMEM;
|
|
ctxs[0] = ctx;
|
|
ctx = damon_sample_mtier_build_ctx(false);
|
|
if (!ctx) {
|
|
damon_destroy_ctx(ctxs[0]);
|
|
return -ENOMEM;
|
|
}
|
|
ctxs[1] = ctx;
|
|
return damon_start(ctxs, 2, true);
|
|
}
|
|
|
|
static void damon_sample_mtier_stop(void)
|
|
{
|
|
damon_stop(ctxs, 2);
|
|
damon_destroy_ctx(ctxs[0]);
|
|
damon_destroy_ctx(ctxs[1]);
|
|
}
|
|
|
|
static int damon_sample_mtier_enable_store(
|
|
const char *val, const struct kernel_param *kp)
|
|
{
|
|
bool is_enabled = enabled;
|
|
int err;
|
|
|
|
err = kstrtobool(val, &enabled);
|
|
if (err)
|
|
return err;
|
|
|
|
if (enabled == is_enabled)
|
|
return 0;
|
|
|
|
if (!damon_initialized())
|
|
return 0;
|
|
|
|
if (enabled) {
|
|
err = damon_sample_mtier_start();
|
|
if (err)
|
|
enabled = false;
|
|
return err;
|
|
}
|
|
damon_sample_mtier_stop();
|
|
return 0;
|
|
}
|
|
|
|
static int __init damon_sample_mtier_init(void)
|
|
{
|
|
int err = 0;
|
|
|
|
if (!damon_initialized()) {
|
|
if (enabled)
|
|
enabled = false;
|
|
return -ENOMEM;
|
|
}
|
|
|
|
if (enabled) {
|
|
err = damon_sample_mtier_start();
|
|
if (err)
|
|
enabled = false;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
module_init(damon_sample_mtier_init);
|