From nobody Mon Feb 9 07:44:22 2026 Received: from frasgout.his.huawei.com (frasgout.his.huawei.com [185.176.79.56]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D5A2D376473 for ; Mon, 2 Feb 2026 14:57:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.176.79.56 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770044257; cv=none; b=rzeCj3H1M5ZU1127He3ScINzsfB7sryUg25df/NYOAdad3rMx3Tqnxuplb8KSNklkrk0nRPnzY7guNL1tShXqRxamQS/03/RuL4FMk+VtbPbiqMMtTnk6xj2xDC70wC7dCt/hH9n3iflqP3eKb2cBw4najHdyT1VHpHmV0D7lSo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770044257; c=relaxed/simple; bh=jvIqWr/roJaqitIQaLbC5TmcuXVJ+whYeT8cykKkXQg=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=s2hwOFY2L+zPyuH+YjLgxXfSITZDrwVX+wmiF/klUv9OfnLkyfobp9CcJYpOMY1h0sq4sX0OEFDyQjID0uElziBxuDVybzj9QZTrUJwmucW+fNHk3L4khexHqwpoOW2Lp5jzF6tKMbnueBjRq7acvi9fw/PlidnvanP6/mlGHIE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=huawei-partners.com; spf=pass smtp.mailfrom=huawei-partners.com; arc=none smtp.client-ip=185.176.79.56 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=huawei-partners.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=huawei-partners.com Received: from mail.maildlp.com (unknown [172.18.224.107]) by frasgout.his.huawei.com (SkyGuard) with ESMTPS id 4f4V7Q6JvLzHnH83; Mon, 2 Feb 2026 22:56:34 +0800 (CST) Received: from mscpeml500003.china.huawei.com (unknown [7.188.49.51]) by mail.maildlp.com (Postfix) with ESMTPS id 6F48440584; Mon, 2 Feb 2026 22:57:32 +0800 (CST) Received: from mscphis01197.huawei.com (10.123.65.218) by mscpeml500003.china.huawei.com (7.188.49.51) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1544.11; Mon, 2 Feb 2026 17:57:32 +0300 From: To: , , , , , , , , , , Subject: [RFC PATCH v1 1/4] mm/damon: Generic context creation for modules Date: Mon, 2 Feb 2026 14:56:46 +0000 Message-ID: <20260202145650.1795854-2-gutierrez.asier@huawei-partners.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260202145650.1795854-1-gutierrez.asier@huawei-partners.com> References: <20260202145650.1795854-1-gutierrez.asier@huawei-partners.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: mscpeml100003.china.huawei.com (10.199.174.67) To mscpeml500003.china.huawei.com (7.188.49.51) Content-Type: text/plain; charset="utf-8" From: Asier Gutierrez Damon_modules_new_paddr_ctx_target. This works only for physical contexts. In case of virtual addresses, we should duplicate the code. It is more elegant to have a generic version of new context creation which receives the mode as a parameter. Signed-off-by: Asier Gutierrez Co-developed-by: Anatoly Stepanov --- mm/damon/lru_sort.c | 6 ++++-- mm/damon/modules-common.c | 7 ++++--- mm/damon/modules-common.h | 5 +++-- mm/damon/reclaim.c | 5 +++-- 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/mm/damon/lru_sort.c b/mm/damon/lru_sort.c index 49b4bc294f4e..ac34b02dace8 100644 --- a/mm/damon/lru_sort.c +++ b/mm/damon/lru_sort.c @@ -201,7 +201,8 @@ static int damon_lru_sort_apply_parameters(void) unsigned int hot_thres, cold_thres; int err; =20 - err =3D damon_modules_new_paddr_ctx_target(¶m_ctx, ¶m_target); + err =3D damon_modules_new_ctx_target(¶m_ctx, ¶m_target, + DAMON_OPS_PADDR); if (err) return err; =20 @@ -375,7 +376,8 @@ static int __init damon_lru_sort_init(void) err =3D -ENOMEM; goto out; } - err =3D damon_modules_new_paddr_ctx_target(&ctx, &target); + err =3D damon_modules_new_ctx_target(&ctx, &target, + DAMON_OPS_PADDR); if (err) goto out; =20 diff --git a/mm/damon/modules-common.c b/mm/damon/modules-common.c index 86d58f8c4f63..5ba24e0ad9a1 100644 --- a/mm/damon/modules-common.c +++ b/mm/damon/modules-common.c @@ -14,8 +14,9 @@ * @ctxp: Pointer to save the point to the newly created context * @targetp: Pointer to save the point to the newly created target */ -int damon_modules_new_paddr_ctx_target(struct damon_ctx **ctxp, - struct damon_target **targetp) +int damon_modules_new_ctx_target(struct damon_ctx **ctxp, + struct damon_target **targetp, + enum damon_ops_id mode) { struct damon_ctx *ctx; struct damon_target *target; @@ -24,7 +25,7 @@ int damon_modules_new_paddr_ctx_target(struct damon_ctx *= *ctxp, if (!ctx) return -ENOMEM; =20 - if (damon_select_ops(ctx, DAMON_OPS_PADDR)) { + if (damon_select_ops(ctx, mode)) { damon_destroy_ctx(ctx); return -EINVAL; } diff --git a/mm/damon/modules-common.h b/mm/damon/modules-common.h index f103ad556368..87d8058d7d85 100644 --- a/mm/damon/modules-common.h +++ b/mm/damon/modules-common.h @@ -45,5 +45,6 @@ module_param_named(nr_##qt_exceed_name, stat.qt_exceeds, ulong, \ 0400); =20 -int damon_modules_new_paddr_ctx_target(struct damon_ctx **ctxp, - struct damon_target **targetp); +int damon_modules_new_ctx_target(struct damon_ctx **ctxp, + struct damon_target **targetp, + enum damon_ops_id mode); diff --git a/mm/damon/reclaim.c b/mm/damon/reclaim.c index 36a582e09eae..b64fb810e096 100644 --- a/mm/damon/reclaim.c +++ b/mm/damon/reclaim.c @@ -197,7 +197,8 @@ static int damon_reclaim_apply_parameters(void) struct damos_filter *filter; int err; =20 - err =3D damon_modules_new_paddr_ctx_target(¶m_ctx, ¶m_target); + err =3D damon_modules_new_ctx_target(¶m_ctx, ¶m_target, + DAMON_OPS_PADDR); if (err) return err; =20 @@ -379,7 +380,7 @@ static int __init damon_reclaim_init(void) err =3D -ENOMEM; goto out; } - err =3D damon_modules_new_paddr_ctx_target(&ctx, &target); + err =3D damon_modules_new_ctx_target(&ctx, &target, DAMON_OPS_PADDR); if (err) goto out; =20 --=20 2.43.0 From nobody Mon Feb 9 07:44:22 2026 Received: from frasgout.his.huawei.com (frasgout.his.huawei.com [185.176.79.56]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D5DA137649E for ; Mon, 2 Feb 2026 14:57:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.176.79.56 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770044258; cv=none; b=oDNsBbILdDYMKtvIbkuZS80GK17P2AAMK2+58gAzB/Ze+v2iLbvvIj0bO3UZv44V22wudE5aTgbbKVkOgQebQA4kUZrEq/+KOiRgpfKKc9VO0BFl/Bb9ZS0MUQhoXum8iiv71bUH31xK39a0cs0GfrbaHwbvPextuisS9Rsx4J0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770044258; c=relaxed/simple; bh=euhHPSZ9q7E3JiioOhSZA/Ja91LbAlC/CfrqmqmJEdo=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=YtxfsfxdTNLYZgDPNfCMxIJRSGKfdM1PjksvaPg1M1JMu5XGSruJTGljO+iiurm4Q9EF6sCFCK5ba6yfERVegGtw9LN3s4PcGuso8d+h1zHNg66XWEcMMhjoqgtd0K9RQivOgytFQ3Waz81+k+XoouvRxSCF0/yi2W9GrBmnYxY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=huawei-partners.com; spf=pass smtp.mailfrom=huawei-partners.com; arc=none smtp.client-ip=185.176.79.56 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=huawei-partners.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=huawei-partners.com Received: from mail.maildlp.com (unknown [172.18.224.107]) by frasgout.his.huawei.com (SkyGuard) with ESMTPS id 4f4V7f4Z18zJ46cY; Mon, 2 Feb 2026 22:56:46 +0800 (CST) Received: from mscpeml500003.china.huawei.com (unknown [7.188.49.51]) by mail.maildlp.com (Postfix) with ESMTPS id 8438C40571; Mon, 2 Feb 2026 22:57:32 +0800 (CST) Received: from mscphis01197.huawei.com (10.123.65.218) by mscpeml500003.china.huawei.com (7.188.49.51) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1544.11; Mon, 2 Feb 2026 17:57:32 +0300 From: To: , , , , , , , , , , Subject: [RFC PATCH v1 2/4] mm/damon: Support for synchrounous huge pages collapse Date: Mon, 2 Feb 2026 14:56:47 +0000 Message-ID: <20260202145650.1795854-3-gutierrez.asier@huawei-partners.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260202145650.1795854-1-gutierrez.asier@huawei-partners.com> References: <20260202145650.1795854-1-gutierrez.asier@huawei-partners.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: mscpeml100003.china.huawei.com (10.199.174.67) To mscpeml500003.china.huawei.com (7.188.49.51) Content-Type: text/plain; charset="utf-8" From: Asier Gutierrez * Support for huge pages collapse, which will be used by dynamic_hugepages module. * Include the new module for compilation Signed-off-by: Asier Gutierrez Co-developed-by: Anatoly Stepanov --- include/linux/damon.h | 1 + mm/damon/Kconfig | 7 +++++++ mm/damon/Makefile | 1 + mm/damon/vaddr.c | 3 +++ 4 files changed, 12 insertions(+) diff --git a/include/linux/damon.h b/include/linux/damon.h index 3813373a9200..de5a994f92c2 100644 --- a/include/linux/damon.h +++ b/include/linux/damon.h @@ -142,6 +142,7 @@ enum damos_action { DAMOS_LRU_DEPRIO, DAMOS_MIGRATE_HOT, DAMOS_MIGRATE_COLD, + DAMOS_COLLAPSE, DAMOS_STAT, /* Do nothing but only record the stat */ NR_DAMOS_ACTIONS, }; diff --git a/mm/damon/Kconfig b/mm/damon/Kconfig index 8c868f7035fc..2355aacb6d12 100644 --- a/mm/damon/Kconfig +++ b/mm/damon/Kconfig @@ -110,4 +110,11 @@ config DAMON_STAT_ENABLED_DEFAULT Whether to enable DAMON_STAT by default. Users can disable it in boot or runtime using its 'enabled' parameter. =20 +config DAMON_HOT_HUGEPAGE + bool "Build DAMON-based collapse of hot regions (DAMON_HOT_HUGEPAGES)" + depends on DAMON_VADDR + help + Collapse hot region into huge pages. Hot regions are determined by + DAMON-based sampling + endmenu diff --git a/mm/damon/Makefile b/mm/damon/Makefile index d8d6bf5f8bff..998bddc17819 100644 --- a/mm/damon/Makefile +++ b/mm/damon/Makefile @@ -7,3 +7,4 @@ obj-$(CONFIG_DAMON_SYSFS) +=3D sysfs-common.o sysfs-schemes= .o sysfs.o obj-$(CONFIG_DAMON_RECLAIM) +=3D modules-common.o reclaim.o obj-$(CONFIG_DAMON_LRU_SORT) +=3D modules-common.o lru_sort.o obj-$(CONFIG_DAMON_STAT) +=3D modules-common.o stat.o +obj-$(CONFIG_DAMON_HOT_HUGEPAGE) +=3D modules-common.o dynamic_hugepages.o diff --git a/mm/damon/vaddr.c b/mm/damon/vaddr.c index 23ed738a0bd6..4acbc1a6a5be 100644 --- a/mm/damon/vaddr.c +++ b/mm/damon/vaddr.c @@ -970,6 +970,9 @@ static unsigned long damon_va_apply_scheme(struct damon= _ctx *ctx, case DAMOS_NOHUGEPAGE: madv_action =3D MADV_NOHUGEPAGE; break; + case DAMOS_COLLAPSE: + madv_action =3D MADV_COLLAPSE; + break; case DAMOS_MIGRATE_HOT: case DAMOS_MIGRATE_COLD: return damos_va_migrate(t, r, scheme, sz_filter_passed); --=20 2.43.0 From nobody Mon Feb 9 07:44:22 2026 Received: from frasgout.his.huawei.com (frasgout.his.huawei.com [185.176.79.56]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D576034D3AA for ; Mon, 2 Feb 2026 14:57:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.176.79.56 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770044258; cv=none; b=op217HOhLPERGY3Ms0hJ3I5jlbneTBteN2+yxM894YUc8WT+Dan+mjByCEBZbseOdT7ZIM8N96PzSPDIpTiIOLoabmnAKIPqaHB7v0cLZD7tuydJ1HSDb5L3MR7NtH72+rryLyXLr/MxwMl8+78DE4bU8B1FTLz+gmKFW7zRRkQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770044258; c=relaxed/simple; bh=w7vtESjqhSZ7vuU+FaD8BnBrTNmnBrNXIa+XzGnb/hQ=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=tkckxjfuKAX8uyHcjHrv3LovVshb0XaAqz9vtjhn4YiEhk6NtOoAG4QVFbCzzFjmjmc6D/cH+ab411YhQ01gxd4rlLBdJqZtqzcEmpT+dbIVW3v+V0O6Vmp5ihIAEP6vrDRDWx+4jqVCT6PcHrfdhJh0ro5oNmyV2MaU4Pfm/hw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=huawei-partners.com; spf=pass smtp.mailfrom=huawei-partners.com; arc=none smtp.client-ip=185.176.79.56 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=huawei-partners.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=huawei-partners.com Received: from mail.maildlp.com (unknown [172.18.224.107]) by frasgout.his.huawei.com (SkyGuard) with ESMTPS id 4f4V7R0TMvzHnH80; Mon, 2 Feb 2026 22:56:35 +0800 (CST) Received: from mscpeml500003.china.huawei.com (unknown [7.188.49.51]) by mail.maildlp.com (Postfix) with ESMTPS id 9974840571; Mon, 2 Feb 2026 22:57:32 +0800 (CST) Received: from mscphis01197.huawei.com (10.123.65.218) by mscpeml500003.china.huawei.com (7.188.49.51) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1544.11; Mon, 2 Feb 2026 17:57:32 +0300 From: To: , , , , , , , , , , Subject: [RFC PATCH v1 3/4] mm/damon: New module with hot application detection Date: Mon, 2 Feb 2026 14:56:48 +0000 Message-ID: <20260202145650.1795854-4-gutierrez.asier@huawei-partners.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260202145650.1795854-1-gutierrez.asier@huawei-partners.com> References: <20260202145650.1795854-1-gutierrez.asier@huawei-partners.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: mscpeml100003.china.huawei.com (10.199.174.67) To mscpeml500003.china.huawei.com (7.188.49.51) Content-Type: text/plain; charset="utf-8" From: Asier Gutierrez This new module detects hot applications and launches a new kdamond thread for each of them. 1. It first launches a new kthread called damon_dynamic. This thread will monitor the tasks in the system by pooling. The tasks are sorted by utime delta. For the top N tasks, a new kdamond thread will be launched. Applications which turn cold will have their kdamond stopped. 2. Initially we don't know the min_access for each of the task. We want to find the highest min_access when collapses start happening. For that we have an initial threashold of 90, which we will lower until a collpase occurs. Signed-off-by: Asier Gutierrez Co-developed-by: Anatoly Stepanov --- mm/damon/dynamic_hugepages.c (new) | 579 +++++++++++++++++++++++++++++ 1 file changed, 579 insertions(+) diff --git a/mm/damon/dynamic_hugepages.c b/mm/damon/dynamic_hugepages.c new file mode 100644 index 000000000000..8b7c1e4d5840 --- /dev/null +++ b/mm/damon/dynamic_hugepages.c @@ -0,0 +1,579 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2025 HUAWEI, Inc. + * https://www.huawei.com + * + * Author: Asier Gutierrez + */ + +#define pr_fmt(fmt) "damon-dynamic-hotpages: " fmt + +#include +#include +#include +#include +#include + +#include "modules-common.h" + +#ifdef MODULE_PARAM_PREFIX +#undef MODULE_PARAM_PREFIX +#endif +#define MODULE_PARAM_PREFIX "damon_dynamic_hotpages." + +#define MAX_MONITORED_PIDS 3 +#define HIGHEST_MIN_ACCESS 90 +#define HIGH_ACC_THRESHOLD 50 +#define MID_ACC_THRESHOLD 15 +#define LOW_ACC_THRESHOLD 2 + +static struct task_struct *monitor_thread; + +struct mutex enable_disable_lock; + +/* + * Enable or disable DAMON_HOT_HUGEPAGE. + * + * You can enable DAMON_HOT_HUGEPAGE by setting the value of this parameter + * as ``Y``. Setting it as ``N`` disables DAMON_HOT_HUGEPAGE. Note that + * DAMON_HOT_HUGEPAGE could do no real monitoring and reclamation due to t= he + * watermarks-based activation condition. Refer to below descriptions for= the + * watermarks parameter for this. + */ +static bool enabled __read_mostly; + +/* + * DAMON_HOT_HUGEPAGE monitoring period. + */ +static unsigned long monitor_period __read_mostly =3D 5000000; +module_param(monitor_period, ulong, 0600); + +static long monitored_pids[MAX_MONITORED_PIDS]; +module_param_array(monitored_pids, long, NULL, 0400); + +static int damon_dynamic_hotpages_turn(bool on); + +static struct damos_quota damon_dynamic_hotpages_quota =3D { + /* use up to 10 ms time, reclaim up to 128 MiB per 1 sec by default */ + .ms =3D 10, + .sz =3D 0, + .reset_interval =3D 1000, + /* Within the quota, page out older regions first. */ + .weight_sz =3D 0, + .weight_nr_accesses =3D 0, + .weight_age =3D 1 +}; +DEFINE_DAMON_MODULES_DAMOS_TIME_QUOTA(damon_dynamic_hotpages_quota); + +static struct damos_watermarks damon_dynamic_hotpages_wmarks =3D { + .metric =3D DAMOS_WMARK_FREE_MEM_RATE, + .interval =3D 5000000, /* 5 seconds */ + .high =3D 900, /* 90 percent */ + .mid =3D 800, /* 80 percent */ + .low =3D 50, /* 5 percent */ +}; +DEFINE_DAMON_MODULES_WMARKS_PARAMS(damon_dynamic_hotpages_wmarks); + +static struct damon_attrs damon_dynamic_hotpages_mon_attrs =3D { + .sample_interval =3D 5000, /* 5 ms */ + .aggr_interval =3D 100000, /* 100 ms */ + .ops_update_interval =3D 0, + .min_nr_regions =3D 10, + .max_nr_regions =3D 1000, +}; +DEFINE_DAMON_MODULES_MON_ATTRS_PARAMS(damon_dynamic_hotpages_mon_attrs); + +struct task_monitor_node { + pid_t pid; + + struct damon_ctx *ctx; + struct damon_target *target; + struct damon_call_control call_control; + u64 previous_utime; + unsigned long load; + struct damos_stat stat; + int min_access; + + struct list_head list; + struct list_head sorted_list; + struct list_head active_monitoring; +}; + +static void find_top_n(struct list_head *task_monitor, + struct list_head *sorted_tasks) +{ + struct task_monitor_node *entry, *to_test, *tmp; + struct list_head *pos; + int i; + + list_for_each_entry(entry, task_monitor, list) { + i =3D 0; + list_for_each(pos, sorted_tasks) { + i++; + to_test =3D list_entry(pos, struct task_monitor_node, sorted_list); + + if (entry->load > to_test->load) { + list_add_tail(&entry->sorted_list, pos); + + i =3D MAX_MONITORED_PIDS; + } + + if (i =3D=3D MAX_MONITORED_PIDS) + break; + } + + if (i < MAX_MONITORED_PIDS) + list_add_tail(&entry->sorted_list, sorted_tasks); + } + + i =3D 0; + list_for_each_entry_safe(entry, tmp, sorted_tasks, sorted_list) { + if (i < MAX_MONITORED_PIDS) + continue; + list_del_init(&entry->sorted_list); + + } +} + +static struct damos *damon_dynamic_hotpages_new_scheme(int min_access, + enum damos_action action) +{ + struct damos_access_pattern pattern =3D { + /* Find regions having PAGE_SIZE or larger size */ + .min_sz_region =3D PMD_SIZE, + .max_sz_region =3D ULONG_MAX, + /* and not accessed at all */ + .min_nr_accesses =3D min_access, + .max_nr_accesses =3D 100, + /* for min_age or more micro-seconds */ + .min_age_region =3D 0, + .max_age_region =3D UINT_MAX, + }; + + return damon_new_scheme( + &pattern, + /* synchrounous partial collapse as soon as found */ + action, 0, + /* under the quota. */ + &damon_dynamic_hotpages_quota, + /* (De)activate this according to the watermarks. */ + &damon_dynamic_hotpages_wmarks, NUMA_NO_NODE); +} + +static int damon_dynamic_hotpages_apply_parameters( + struct task_monitor_node *monitored_task, + int min_access, + enum damos_action action) +{ + struct damos *scheme; + struct damon_ctx *param_ctx; + struct damon_target *param_target; + struct damos_filter *filter; + struct pid *spid; + int err; + + err =3D damon_modules_new_ctx_target(¶m_ctx, ¶m_target, + DAMON_OPS_VADDR); + if (err) + return err; + + err =3D -EINVAL; + spid =3D find_get_pid(monitored_task->pid); + if (!spid) { + put_pid(spid); + goto out; + } + + param_target->pid =3D spid; + + err =3D damon_set_attrs(param_ctx, &damon_dynamic_hotpages_mon_attrs); + if (err) + goto out; + + err =3D -ENOMEM; + scheme =3D damon_dynamic_hotpages_new_scheme(min_access, action); + if (!scheme) + goto out; + + damon_set_schemes(param_ctx, &scheme, 1); + + filter =3D damos_new_filter(DAMOS_FILTER_TYPE_ANON, true, false); + if (!filter) + goto out; + damos_add_filter(scheme, filter); + + err =3D damon_commit_ctx(monitored_task->ctx, param_ctx); +out: + damon_destroy_ctx(param_ctx); + return err; +} + +static int damon_dynamic_hotpages_damon_call_fn(void *arg) +{ + struct task_monitor_node *monitored_task =3D arg; + struct damon_ctx *ctx =3D monitored_task->ctx; + struct damos *scheme; + int err =3D 0; + int min_access; + struct damos_stat stat; + + damon_for_each_scheme(scheme, ctx) + stat =3D scheme->stat; + scheme =3D list_first_entry(&ctx->schemes, struct damos, list); + + if (ctx->passed_sample_intervals < scheme->next_apply_sis) + return err; + + if (stat.nr_applied) + return err; + + min_access =3D scheme->pattern.min_nr_accesses; + + if (min_access > HIGH_ACC_THRESHOLD) { + min_access =3D min_access - 10; + err =3D damon_dynamic_hotpages_apply_parameters( + monitored_task, min_access, DAMOS_COLLAPSE); + } else if (min_access > MID_ACC_THRESHOLD) { + min_access =3D min_access - 5; + err =3D damon_dynamic_hotpages_apply_parameters( + monitored_task, min_access, DAMOS_COLLAPSE); + } else if (min_access > LOW_ACC_THRESHOLD) { + min_access =3D min_access - 1; + err =3D damon_dynamic_hotpages_apply_parameters( + monitored_task, min_access, DAMOS_COLLAPSE); + } + return err; +} + +static int damon_dynamic_hotpages_init_task( + struct task_monitor_node *task_monitor) +{ + int err =3D 0; + struct pid *spid; + struct damon_ctx *ctx =3D task_monitor->ctx; + struct damon_target *target =3D task_monitor->target; + + if (!ctx || !target) + damon_modules_new_ctx_target(&ctx, &target, DAMON_OPS_VADDR); + + if (ctx->kdamond) + return 0; + + spid =3D find_get_pid(task_monitor->pid); + if (!spid) { + put_pid(spid); + return -ESRCH; + } + + target->pid =3D spid; + + if (err) + return err; + + task_monitor->call_control.fn =3D damon_dynamic_hotpages_damon_call_fn; + task_monitor->call_control.repeat =3D true; + task_monitor->call_control.data =3D task_monitor; + + struct damos *scheme =3D + damon_dynamic_hotpages_new_scheme(HIGHEST_MIN_ACCESS, DAMOS_COLLAPSE); + if (!scheme) + return -ENOMEM; + + damon_set_schemes(ctx, &scheme, 1); + + task_monitor->ctx =3D ctx; + err =3D damon_start(&task_monitor->ctx, 1, false); + if (err) + return err; + + return damon_call(task_monitor->ctx, &task_monitor->call_control); +} + +static int add_monitored_task(struct task_struct *task, + struct list_head *task_monitor) +{ + struct task_struct *thread; + struct task_monitor_node *task_node; + u64 total_time =3D 0; + + task_node =3D kzalloc(sizeof(struct task_monitor_node), GFP_KERNEL); + if (!task_node) + return -ENOMEM; + + INIT_LIST_HEAD(&task_node->list); + INIT_LIST_HEAD(&task_node->sorted_list); + INIT_LIST_HEAD(&task_node->active_monitoring); + + task_node->min_access =3D HIGHEST_MIN_ACCESS; + task_node->pid =3D task_pid_nr(task); + + list_add_tail(&task_node->list, task_monitor); + + for_each_thread(task, thread) + total_time +=3D thread->utime; + + task_node->previous_utime =3D total_time; + return 0; +} + +static int damon_dynamic_hotpages_attach_tasks( + struct list_head *task_monitor_sorted, + struct list_head *task_monitor_active) +{ + struct task_monitor_node *sorted_task_node, *tmp; + int err; + int i =3D 0; + + sorted_task_node =3D list_first_entry( + task_monitor_sorted, struct task_monitor_node, sorted_list); + while (i < MAX_MONITORED_PIDS && !list_entry_is_head(sorted_task_node, + task_monitor_sorted, sorted_list)) { + if (sorted_task_node->ctx && sorted_task_node->ctx->kdamond) + list_move(&sorted_task_node->active_monitoring, + task_monitor_active); + else { + rcu_read_lock(); + if (!find_vpid(sorted_task_node->pid)) { + sorted_task_node->ctx =3D NULL; + sorted_task_node =3D list_next_entry( + sorted_task_node, sorted_list); + + rcu_read_unlock(); + continue; + } + rcu_read_unlock(); + + err =3D damon_dynamic_hotpages_init_task(sorted_task_node); + if (err) { + sorted_task_node->ctx =3D NULL; + sorted_task_node =3D list_next_entry( + sorted_task_node, sorted_list); + continue; + } + + list_add(&sorted_task_node->active_monitoring, + task_monitor_active); + } + + monitored_pids[i] =3D sorted_task_node->pid; + sorted_task_node =3D list_next_entry(sorted_task_node, sorted_list); + + i++; + } + + i =3D 0; + list_for_each_entry_safe(sorted_task_node, tmp, task_monitor_active, + active_monitoring) { + if (i < MAX_MONITORED_PIDS) { + i++; + continue; + } + + if (sorted_task_node->ctx) { + damon_stop(&sorted_task_node->ctx, 1); + damon_destroy_ctx(sorted_task_node->ctx); + sorted_task_node->ctx =3D NULL; + } + + list_del_init(&sorted_task_node->active_monitoring); + } + return 0; +} + +static int damon_dynamic_hugepage_sync_monitored_pids( + struct list_head *task_monitor, + struct list_head *task_monitor_active) +{ + u64 total_time, delta; + struct task_struct *entry_task, *thread, *current_task; + struct task_monitor_node *entry, *next_ptr; + struct list_head *pos =3D task_monitor->next; + struct list_head *next_pos; + int err =3D 0; + + LIST_HEAD(task_to_be_removed); + + rcu_read_lock(); + current_task =3D next_task(&init_task); + + while ((current_task !=3D &init_task)) { + if (pos =3D=3D task_monitor) { + /* We reached the end of monited tasks while still having tasks + * in the system. This means that we have new tasks and we should + * add the to the monitored tasks + */ + err =3D add_monitored_task(current_task, task_monitor); + if (err) + goto out; + current_task =3D next_task(current_task); + continue; + } + + entry =3D list_entry(pos, struct task_monitor_node, list); + entry_task =3D find_get_task_by_vpid(entry->pid); + + if (!entry_task) { + /* task doesn't exist, remove it */ + next_pos =3D pos->next; + list_move(&entry->list, &task_to_be_removed); + pos =3D next_pos; + + continue; + } else { + /* We had this task before, update load times */ + total_time =3D 0; + for_each_thread(entry_task, thread) + total_time +=3D thread->utime; + + delta =3D total_time - entry->previous_utime; + entry->previous_utime =3D total_time; + + /* Exponential load average to avoid peaks of short time usage, + * which may lead to running DAMON for a not really hot task + */ + entry->load =3D calc_load(entry->load, EXP_15, delta); + pos =3D pos->next; + current_task =3D next_task(current_task); + } + put_task_struct(entry_task); + } +out: + rcu_read_unlock(); + + list_for_each_entry_safe(entry, next_ptr, &task_to_be_removed, list) { + list_del_init(&entry->list); + list_del_init(&entry->sorted_list); + + if (!list_is_head(&entry->active_monitoring, + task_monitor_active)) { + if (entry->ctx) { + damon_stop(&entry->ctx, 1); + damon_destroy_ctx(entry->ctx); + entry->ctx =3D NULL; + } + list_del_init(&entry->active_monitoring); + } + kfree(entry); + } + + return err; +} + +static int damon_manager_monitor_thread(void *data) +{ + int err =3D 0; + struct task_monitor_node *entry, *tmp; + + LIST_HEAD(task_monitor); + LIST_HEAD(task_monitor_sorted); + LIST_HEAD(task_monitor_active); + + while (!kthread_should_stop()) { + err =3D damon_dynamic_hugepage_sync_monitored_pids(&task_monitor, + &task_monitor_active); + if (err) + return err; + + find_top_n(&task_monitor, &task_monitor_sorted); + + err =3D damon_dynamic_hotpages_attach_tasks(&task_monitor_sorted, + &task_monitor_active); + if (err) + return err; + + schedule_timeout_idle(usecs_to_jiffies(monitor_period)); + + list_for_each_entry_safe(entry, tmp, &task_monitor_sorted, + sorted_list) + list_del_init(&entry->sorted_list); + } + + list_for_each_entry_safe(entry, tmp, &task_monitor_active, + active_monitoring) { + if (entry->ctx) { + err =3D damon_stop(&entry->ctx, 1); + damon_destroy_ctx(entry->ctx); + entry->ctx =3D NULL; + } + + list_del_init(&entry->active_monitoring); + } + + return err; +} + +static int damon_dynamic_hotpages_start_monitor_thread(void) +{ + monitor_thread =3D kthread_create(damon_manager_monitor_thread, NULL, + "damon_dynamic"); + + if (IS_ERR(monitor_thread)) + return PTR_ERR(monitor_thread); + + wake_up_process(monitor_thread); + return 0; +} + +static int damon_dynamic_hotpages_turn(bool on) +{ + int err =3D 0; + + mutex_lock(&enable_disable_lock); + if (!on) { + if (monitor_thread) { + kthread_stop(monitor_thread); + monitor_thread =3D NULL; + } + goto out; + } + err =3D damon_dynamic_hotpages_start_monitor_thread(); +out: + mutex_unlock(&enable_disable_lock); + return err; +} + +static int damon_dynamic_hotpages_enabled_store(const char *val, + const struct kernel_param *kp) +{ + bool is_enabled =3D enabled; + bool enable; + int err; + + err =3D kstrtobool(val, &enable); + if (err) + return err; + + if (is_enabled =3D=3D enable) + return 0; + + err =3D damon_dynamic_hotpages_turn(enable); + if (err) + return err; + + enabled =3D enable; + return err; +} + +static const struct kernel_param_ops enabled_param_ops =3D { + .set =3D damon_dynamic_hotpages_enabled_store, + .get =3D param_get_bool, +}; + +module_param_cb(enabled, &enabled_param_ops, &enabled, 0600); +MODULE_PARM_DESC(enabled, + "Enable or disable DAMON_DYNAMIC_HUGEPAGES (default: disabled)"); + +static int __init damon_dynamic_hotpages_init(void) +{ + int err; + + /* 'enabled' has set before this function, probably via command line */ + if (enabled) + err =3D damon_dynamic_hotpages_turn(true); + + if (err && enabled) + enabled =3D false; + return err; +} + +module_init(damon_dynamic_hotpages_init); --=20 2.43.0 From nobody Mon Feb 9 07:44:22 2026 Received: from frasgout.his.huawei.com (frasgout.his.huawei.com [185.176.79.56]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D5C8337649C for ; Mon, 2 Feb 2026 14:57:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.176.79.56 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770044257; cv=none; b=dWLrWBmRLpMv5LifNAXvgYI7RbWMcxCEGeTGhrwDwqsr9ks3NC83od0dtaJx/1xjCTK1TvsIpKX/L/4jI6pSKDwqSQPawRj/mr8eXMWNGD3wMMLXs0DBuz5NUqidz0cNTZ7QbE84KWjVTk7UjjTeKvFSymLboQLVBCw78LFylHg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770044257; c=relaxed/simple; bh=M9aIX50H57oCX25mt1AlnsOavPdb1afHkaJpeelUWRA=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=Q45RzkRomptZhEXTsHORtDkhuigeeTV4tZVJBA6A/7AQqfBeqY+bt47tr1W1D0MzYeKDVVmZpKa/xdteSlOeoZL8O3lyLRyMnKo7xBznU5HbS8Ann6Hk45ZUJKOJbjT10x+xH7m2QxtMsl+WpHKman9PsdL4oF5xk4qkeQpofKc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=huawei-partners.com; spf=pass smtp.mailfrom=huawei-partners.com; arc=none smtp.client-ip=185.176.79.56 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=huawei-partners.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=huawei-partners.com Received: from mail.maildlp.com (unknown [172.18.224.150]) by frasgout.his.huawei.com (SkyGuard) with ESMTPS id 4f4V7g0ZwwzJ46cb; Mon, 2 Feb 2026 22:56:47 +0800 (CST) Received: from mscpeml500003.china.huawei.com (unknown [7.188.49.51]) by mail.maildlp.com (Postfix) with ESMTPS id EF3A740539; Mon, 2 Feb 2026 22:57:32 +0800 (CST) Received: from mscphis01197.huawei.com (10.123.65.218) by mscpeml500003.china.huawei.com (7.188.49.51) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1544.11; Mon, 2 Feb 2026 17:57:32 +0300 From: To: , , , , , , , , , , Subject: [RFC PATCH v1 4/4] documentation/mm/damon: Documentation for the dynamic_hugepages module Date: Mon, 2 Feb 2026 14:56:49 +0000 Message-ID: <20260202145650.1795854-5-gutierrez.asier@huawei-partners.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260202145650.1795854-1-gutierrez.asier@huawei-partners.com> References: <20260202145650.1795854-1-gutierrez.asier@huawei-partners.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: mscpeml100003.china.huawei.com (10.199.174.67) To mscpeml500003.china.huawei.com (7.188.49.51) Content-Type: text/plain; charset="utf-8" From: Asier Gutierrez Documentation for dynamic_hugepage DAMON module. Signed-off-by: Asier Gutierrez Co-developed-by: Anatoly Stepanov --- .../mm/damon/dynamic_hugepages.rst (new) | 173 ++++++++++++++++++ 1 file changed, 173 insertions(+) diff --git a/Documentation/admin-guide/mm/damon/dynamic_hugepages.rst b/Doc= umentation/admin-guide/mm/damon/dynamic_hugepages.rst new file mode 100644 index 000000000000..a6afb0910661 --- /dev/null +++ b/Documentation/admin-guide/mm/damon/dynamic_hugepages.rst @@ -0,0 +1,173 @@ +.. SPDX-License-Identifier: GPL-2.0 + +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +DAMON-based Dynamic Huge Pages Collapsing +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + + +DAMON-based dynamic hugepages collapsing (DAMON_HOT_HUGEPAGE) is a kernel = module +that monitors the system and picks the 3 most active tasks. Then, it start= s a +new DAMON thread for each of those 3 tasks, which will collapse hot region= s into +huge pages. + +Where Dynamic Huge Pages Collapsing is Required? +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +As main memory availability increases, the number of TLB entries does not +increase proportionally. This adds more pressure to TLB. Huge pages are a +solution. However, since turning on transparent huge pages globally may le= ad to +fragmentation and memory waste, it is usually turned off. + +This module allows to automatically detect hot applications and collapse V= MA +that are hot. + +How It Works? +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +DAMON_HOT_HUGEPAGE spawns a new kthread which will monitor the application= s in +the system. The monitor thread will calculate the moving average of the su= m of +utimes of all the threads for all the processes. Then, pick the top three = and +launch a damon process to monitor the hot regions in those tasks. + +Since we don't know the minaccess number in advance, we set it to 90 initi= ally, +and we keep decreasing that minaccess until a collapse happens. + +If a task turns cold, the monitor thread will detect it (it will not fall = in the +top three hot tasks), and stop the damon thread for that task. + +Interface: Module Parameters +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D + +To use this feature, you should first ensure your system is running on a k= ernel +that is built with CONFIG_DAMON_HOT_HUGEPAGE=3Dy. + +To let sysadmins enable or disable it and tune for the given system, +DAMON_HOT_HUGEPAGE utilizes module parameters. That is, you can put +damon_dynamic_hotpages.=3D on the kernel boot command li= ne or +write proper values to /sys/module/damon_dynamic_hotpages/parameters/ +files. + +Below are the description of each parameter. + +enabled +------- + +Enable or disable DAMON_HOT_HUGEPAGE. + +You can enable DAMON_HOT_HUGEPAGE by setting the value of this parameter a= s Y. +Setting it as N disables DAMON_HOT_HUGEPAGE. Note that, although +DAMON_HOT_HUGEPAGE monitors the system and starts damon threads, those thr= eads +could do no real monitoring due to the watermarks-based activation conditi= on. +Refer to below descriptions for the watermarks parameter for this. + +quota_ms +-------- + +Limit of time for collapsing memory regions in milliseconds. + +DAMON_HOT_HUGEPAGE tries to use only up to this time within a time window +(quota_reset_interval_ms) for trying memory collapse. This can be used for +limiting CPU consumption of DAMONHOT_HUGEPAGE. If the value is zero, the l= imit +is disabled. + +10 ms by default. + +quota_reset_interval_ms +----------------------- + +The time quota charge reset interval in milliseconds. + +The charge reset interval for the quota of time (quota_ms). That is, +DAMON_HOT_HUGEPAGE does not collapse memory for more than quota_ms millise= conds +or quotasz bytes within quota_reset_interval_ms milliseconds. + +1 second by default. + +wmarks_interval +--------------- + +The watermarks check time interval in microseconds. + +Minimal time to wait before checking the watermarks, when DAMON_HOT_HUGEPA= GE is +enabled but inactive due to its watermarks rule. 5 seconds by default. + +wmarks_high +----------- + +Free memory rate (per thousand) for the high watermark. + +If free memory of the system in bytes per thousand bytes is higher than th= is, +DAMON_HOT_HUGEPAGE becomes inactive, so it does nothing but periodically c= hecks +the watermarks. 200 (20%) by default. + +wmarks_mid +---------- + +Free memory rate (per thousand) for the middle watermark. + +If free memory of the system in bytes per thousand bytes is between this a= nd the +low watermark, DAMON_HOT_HUGEPAGE becomes active, so starts the monitoring= and +the memory collapsing. 150 (15%) by default. + +wmarks_low +---------- + +Free memory rate (per thousand) for the low watermark. + +If free memory of the system in bytes per thousand bytes is lower than thi= s, +DAMON_HOT_HUGEPAGE becomes inactive, so it does nothing but periodically c= hecks +the watermarks. 50 (5%) by default. + +sample_interval +--------------- + +Sampling interval for the monitoring in microseconds. + +The sampling interval of DAMON for the cold memory monitoring. Please refe= r to +the DAMON documentation (:doc:usage) for more detail. 5ms by default. + +aggr_interval +------------- + +Aggregation interval for the monitoring in microseconds. + +The aggregation interval of DAMON for the cold memory monitoring. Please r= efer +to the DAMON documentation (:doc:usage) for more detail. 100ms by default. + +min_nr_regions +-------------- + +Minimum number of monitoring regions. + +The minimal number of monitoring regions of DAMON for the cold memory +monitoring. This can be used to set lower-bound of the monitoring quality.= But, +setting this too high could result in increased monitoring overhead. Please +refer to the DAMON documentation (:doc:usage) for more detail. 10 by defau= lt. + +max_nr_regions +-------------- + +Maximum number of monitoring regions. + +The maximum number of monitoring regions of DAMON for the cold memory +monitoring. This can be used to set upper-bound of the monitoring overhead. +However, setting this too low could result in bad monitoring quality. Plea= se +refer to the DAMON documentation (:doc:usage) for more detail. 1000 by def= aults. + +monitored_pids The PIDs of the tasks that are being actively monitored by = DAMON +threads + +Example +=3D=3D=3D=3D=3D=3D=3D +Below runtime example commands make DAMON_HOT_HUGEPAGE to find memory regi= ons in +the 3 most active tasks. It also asks DAMON_HOT_HUGEPAGE to do nothing if = the +system's free memory rate is more than 50%, but start the real works if it +becomes lower than 40%. + + # cd /sys/module/damon_dynamic_hotpages/parameters/ + # echo 10 > quota_ms + # echo 1000 > quota_reset_interval_ms + # echo 500 > wmarks_high + # echo 400 > wmarks_mid + # echo 200 > wmarks_low + # echo Y > enabled \ No newline at end of file --=20 2.43.0