Some page if marked as workingset, we'd better not touch it. So, for damon pa scheme, this patch just add an new filter DAMOS_FILTER_TYPE_WORKINGSET, which will filter page if it marked as workingset. Like skip anon, add a control skip_workingset. To make code clean, fold reclaim's filter create logic into __damon_reclaim_create_filters. Signed-off-by: Huan Yang <link@xxxxxxxx> --- include/linux/damon.h | 17 +++++++++------- mm/damon/core-test.h | 7 +++++++ mm/damon/paddr.c | 4 ++++ mm/damon/reclaim.c | 47 ++++++++++++++++++++++++++++++++++--------- 4 files changed, 58 insertions(+), 17 deletions(-) diff --git a/include/linux/damon.h b/include/linux/damon.h index ae2664d1d5f1..8e8f35df6a5e 100644 --- a/include/linux/damon.h +++ b/include/linux/damon.h @@ -228,22 +228,25 @@ struct damos_stat { * @DAMOS_FILTER_TYPE_MEMCG: Specific memcg's pages. * @DAMOS_FILTER_TYPE_ADDR: Address range. * @DAMOS_FILTER_TYPE_TARGET: Data Access Monitoring target. + * @DAMOS_FILTER_TYPE_WORKINGSET: Workingset pages, need protect. * @NR_DAMOS_FILTER_TYPES: Number of filter types. * - * The anon pages type and memcg type filters are handled by underlying - * &struct damon_operations as a part of scheme action trying, and therefore - * accounted as 'tried'. In contrast, other types are handled by core layer - * before trying of the action and therefore not accounted as 'tried'. + * The anon pages type, memcg type, workingset page type filters are handled + * by underlying &struct damon_operations as a part of scheme action trying, + * and therefore accounted as 'tried'. In contrast, other types are handled + * by core layer before trying of the action and therefore not accounted as + * 'tried'. * * The support of the filters that handled by &struct damon_operations depend * on the running &struct damon_operations. - * &enum DAMON_OPS_PADDR supports both anon pages type and memcg type filters, - * while &enum DAMON_OPS_VADDR and &enum DAMON_OPS_FVADDR don't support any of - * the two types. + * &enum DAMON_OPS_PADDR supports both anon pages type, memcg type and + * workingset page type filters, while &enum DAMON_OPS_VADDR and &enum + * DAMON_OPS_FVADDR don't support any of the two types. */ enum damos_filter_type { DAMOS_FILTER_TYPE_ANON, DAMOS_FILTER_TYPE_MEMCG, + DAMOS_FILTER_TYPE_WORKINGSET, DAMOS_FILTER_TYPE_ADDR, DAMOS_FILTER_TYPE_TARGET, NR_DAMOS_FILTER_TYPES, diff --git a/mm/damon/core-test.h b/mm/damon/core-test.h index 6cc8b245586d..c752e6e3cf3e 100644 --- a/mm/damon/core-test.h +++ b/mm/damon/core-test.h @@ -351,6 +351,13 @@ static void damos_test_new_filter(struct kunit *test) KUNIT_EXPECT_PTR_EQ(test, filter->list.prev, &filter->list); KUNIT_EXPECT_PTR_EQ(test, filter->list.next, &filter->list); damos_destroy_filter(filter); + + filter = damos_new_filter(DAMOS_FILTER_TYPE_WORKINGSET, true); + KUNIT_EXPECT_EQ(test, filter->type, DAMOS_FILTER_TYPE_WORKINGSET); + KUNIT_EXPECT_EQ(test, filter->matching, true); + KUNIT_EXPECT_PTR_EQ(test, filter->list.prev, &filter->list); + KUNIT_EXPECT_PTR_EQ(test, filter->list.next, &filter->list); + damos_destroy_filter(filter); } static void damos_test_filter_out(struct kunit *test) diff --git a/mm/damon/paddr.c b/mm/damon/paddr.c index 909db25efb35..8a690505e033 100644 --- a/mm/damon/paddr.c +++ b/mm/damon/paddr.c @@ -12,6 +12,7 @@ #include <linux/pagemap.h> #include <linux/rmap.h> #include <linux/swap.h> +#include <linux/mm_inline.h> #include "../internal.h" #include "ops-common.h" @@ -204,6 +205,9 @@ static bool __damos_pa_filter_out(struct damos_filter *filter, matched = filter->memcg_id == mem_cgroup_id(memcg); rcu_read_unlock(); break; + case DAMOS_FILTER_TYPE_WORKINGSET: + matched = folio_test_workingset(folio); + break; default: break; } diff --git a/mm/damon/reclaim.c b/mm/damon/reclaim.c index 648d2a85523a..26ae8fa5d088 100644 --- a/mm/damon/reclaim.c +++ b/mm/damon/reclaim.c @@ -107,6 +107,15 @@ module_param(monitor_region_end, ulong, 0600); static bool skip_anon __read_mostly; module_param(skip_anon, bool, 0600); +/* + * Skip workingset pages reclamation. + * + * If this parameter is set as ``Y``, DAMON_RECLAIM does not reclaim workingset + * pages. By default, ``N``. + */ +static bool skip_workingset __read_mostly; +module_param(skip_workingset, bool, 0600); + /* * PID of the DAMON thread * @@ -148,10 +157,25 @@ static struct damos *damon_reclaim_new_scheme(void) &damon_reclaim_wmarks); } +static int __damon_reclaim_create_filters(struct damos *scheme, + enum damos_filter_type type, + bool matching) +{ + struct damos_filter *filter = damos_new_filter(type, matching); + + if (unlikely(!filter)) { + /* Will be freed by next 'damon_set_schemes()' below */ + damon_destroy_scheme(scheme); + return -ENOMEM; + } + + damos_add_filter(scheme, filter); + return 0; +} + static int damon_reclaim_apply_parameters(void) { struct damos *scheme; - struct damos_filter *filter; int err = 0; err = damon_set_attrs(ctx, &damon_reclaim_mon_attrs); @@ -162,15 +186,18 @@ static int damon_reclaim_apply_parameters(void) scheme = damon_reclaim_new_scheme(); if (!scheme) return -ENOMEM; - if (skip_anon) { - filter = damos_new_filter(DAMOS_FILTER_TYPE_ANON, true); - if (!filter) { - /* Will be freed by next 'damon_set_schemes()' below */ - damon_destroy_scheme(scheme); - return -ENOMEM; - } - damos_add_filter(scheme, filter); - } + + err = skip_anon && __damon_reclaim_create_filters( + scheme, DAMOS_FILTER_TYPE_ANON, true); + if (err) + return err; + + err = skip_workingset && + __damon_reclaim_create_filters( + scheme, DAMOS_FILTER_TYPE_WORKINGSET, true); + if (err) + return err; + damon_set_schemes(ctx, &scheme, 1); return damon_set_region_biggest_system_ram_default(target, -- 2.34.1