Skip to content

Commit ee801b7

Browse files
sjp38torvalds
authored andcommitted
mm/damon/schemes: activate schemes based on a watermarks mechanism
DAMON-based operation schemes need to be manually turned on and off. In some use cases, however, the condition for turning a scheme on and off would depend on the system's situation. For example, schemes for proactive pages reclamation would need to be turned on when some memory pressure is detected, and turned off when the system has enough free memory. For easier control of schemes activation based on the system situation, this introduces a watermarks-based mechanism. The client can describe the watermark metric (e.g., amount of free memory in the system), watermark check interval, and three watermarks, namely high, mid, and low. If the scheme is deactivated, it only gets the metric and compare that to the three watermarks for every check interval. If the metric is higher than the high watermark, the scheme is deactivated. If the metric is between the mid watermark and the low watermark, the scheme is activated. If the metric is lower than the low watermark, the scheme is deactivated again. This is to allow users fall back to traditional page-granularity mechanisms. Link: https://lkml.kernel.org/r/[email protected] Signed-off-by: SeongJae Park <[email protected]> Cc: Amit Shah <[email protected]> Cc: Benjamin Herrenschmidt <[email protected]> Cc: David Hildenbrand <[email protected]> Cc: David Rientjes <[email protected]> Cc: David Woodhouse <[email protected]> Cc: Greg Thelen <[email protected]> Cc: Jonathan Cameron <[email protected]> Cc: Jonathan Corbet <[email protected]> Cc: Leonard Foerster <[email protected]> Cc: Marco Elver <[email protected]> Cc: Markus Boehme <[email protected]> Cc: Shakeel Butt <[email protected]> Cc: Shuah Khan <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 5a0d6a0 commit ee801b7

File tree

3 files changed

+151
-3
lines changed

3 files changed

+151
-3
lines changed

include/linux/damon.h

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,45 @@ struct damos_quota {
146146
unsigned int min_score;
147147
};
148148

149+
/**
150+
* enum damos_wmark_metric - Represents the watermark metric.
151+
*
152+
* @DAMOS_WMARK_NONE: Ignore the watermarks of the given scheme.
153+
* @DAMOS_WMARK_FREE_MEM_RATE: Free memory rate of the system in [0,1000].
154+
*/
155+
enum damos_wmark_metric {
156+
DAMOS_WMARK_NONE,
157+
DAMOS_WMARK_FREE_MEM_RATE,
158+
};
159+
160+
/**
161+
* struct damos_watermarks - Controls when a given scheme should be activated.
162+
* @metric: Metric for the watermarks.
163+
* @interval: Watermarks check time interval in microseconds.
164+
* @high: High watermark.
165+
* @mid: Middle watermark.
166+
* @low: Low watermark.
167+
*
168+
* If &metric is &DAMOS_WMARK_NONE, the scheme is always active. Being active
169+
* means DAMON does monitoring and applying the action of the scheme to
170+
* appropriate memory regions. Else, DAMON checks &metric of the system for at
171+
* least every &interval microseconds and works as below.
172+
*
173+
* If &metric is higher than &high, the scheme is inactivated. If &metric is
174+
* between &mid and &low, the scheme is activated. If &metric is lower than
175+
* &low, the scheme is inactivated.
176+
*/
177+
struct damos_watermarks {
178+
enum damos_wmark_metric metric;
179+
unsigned long interval;
180+
unsigned long high;
181+
unsigned long mid;
182+
unsigned long low;
183+
184+
/* private: */
185+
bool activated;
186+
};
187+
149188
/**
150189
* struct damos - Represents a Data Access Monitoring-based Operation Scheme.
151190
* @min_sz_region: Minimum size of target regions.
@@ -156,6 +195,7 @@ struct damos_quota {
156195
* @max_age_region: Maximum age of target regions.
157196
* @action: &damo_action to be applied to the target regions.
158197
* @quota: Control the aggressiveness of this scheme.
198+
* @wmarks: Watermarks for automated (in)activation of this scheme.
159199
* @stat_count: Total number of regions that this scheme is applied.
160200
* @stat_sz: Total size of regions that this scheme is applied.
161201
* @list: List head for siblings.
@@ -166,6 +206,14 @@ struct damos_quota {
166206
* those. To avoid consuming too much CPU time or IO resources for the
167207
* &action, &quota is used.
168208
*
209+
* To do the work only when needed, schemes can be activated for specific
210+
* system situations using &wmarks. If all schemes that registered to the
211+
* monitoring context are inactive, DAMON stops monitoring either, and just
212+
* repeatedly checks the watermarks.
213+
*
214+
* If all schemes that registered to a &struct damon_ctx are inactive, DAMON
215+
* stops monitoring and just repeatedly checks the watermarks.
216+
*
169217
* After applying the &action to each region, &stat_count and &stat_sz is
170218
* updated to reflect the number of regions and total size of regions that the
171219
* &action is applied.
@@ -179,6 +227,7 @@ struct damos {
179227
unsigned int max_age_region;
180228
enum damos_action action;
181229
struct damos_quota quota;
230+
struct damos_watermarks wmarks;
182231
unsigned long stat_count;
183232
unsigned long stat_sz;
184233
struct list_head list;
@@ -384,7 +433,8 @@ struct damos *damon_new_scheme(
384433
unsigned long min_sz_region, unsigned long max_sz_region,
385434
unsigned int min_nr_accesses, unsigned int max_nr_accesses,
386435
unsigned int min_age_region, unsigned int max_age_region,
387-
enum damos_action action, struct damos_quota *quota);
436+
enum damos_action action, struct damos_quota *quota,
437+
struct damos_watermarks *wmarks);
388438
void damon_add_scheme(struct damon_ctx *ctx, struct damos *s);
389439
void damon_destroy_scheme(struct damos *s);
390440

mm/damon/core.c

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <linux/damon.h>
1111
#include <linux/delay.h>
1212
#include <linux/kthread.h>
13+
#include <linux/mm.h>
1314
#include <linux/random.h>
1415
#include <linux/slab.h>
1516
#include <linux/string.h>
@@ -90,7 +91,8 @@ struct damos *damon_new_scheme(
9091
unsigned long min_sz_region, unsigned long max_sz_region,
9192
unsigned int min_nr_accesses, unsigned int max_nr_accesses,
9293
unsigned int min_age_region, unsigned int max_age_region,
93-
enum damos_action action, struct damos_quota *quota)
94+
enum damos_action action, struct damos_quota *quota,
95+
struct damos_watermarks *wmarks)
9496
{
9597
struct damos *scheme;
9698

@@ -122,6 +124,13 @@ struct damos *damon_new_scheme(
122124
scheme->quota.charge_target_from = NULL;
123125
scheme->quota.charge_addr_from = 0;
124126

127+
scheme->wmarks.metric = wmarks->metric;
128+
scheme->wmarks.interval = wmarks->interval;
129+
scheme->wmarks.high = wmarks->high;
130+
scheme->wmarks.mid = wmarks->mid;
131+
scheme->wmarks.low = wmarks->low;
132+
scheme->wmarks.activated = true;
133+
125134
return scheme;
126135
}
127136

@@ -582,6 +591,9 @@ static void damon_do_apply_schemes(struct damon_ctx *c,
582591
unsigned long sz = r->ar.end - r->ar.start;
583592
struct timespec64 begin, end;
584593

594+
if (!s->wmarks.activated)
595+
continue;
596+
585597
/* Check the quota */
586598
if (quota->esz && quota->charged_sz >= quota->esz)
587599
continue;
@@ -684,6 +696,9 @@ static void kdamond_apply_schemes(struct damon_ctx *c)
684696
unsigned long cumulated_sz;
685697
unsigned int score, max_score = 0;
686698

699+
if (!s->wmarks.activated)
700+
continue;
701+
687702
if (!quota->ms && !quota->sz)
688703
continue;
689704

@@ -924,6 +939,83 @@ static bool kdamond_need_stop(struct damon_ctx *ctx)
924939
return true;
925940
}
926941

942+
static unsigned long damos_wmark_metric_value(enum damos_wmark_metric metric)
943+
{
944+
struct sysinfo i;
945+
946+
switch (metric) {
947+
case DAMOS_WMARK_FREE_MEM_RATE:
948+
si_meminfo(&i);
949+
return i.freeram * 1000 / i.totalram;
950+
default:
951+
break;
952+
}
953+
return -EINVAL;
954+
}
955+
956+
/*
957+
* Returns zero if the scheme is active. Else, returns time to wait for next
958+
* watermark check in micro-seconds.
959+
*/
960+
static unsigned long damos_wmark_wait_us(struct damos *scheme)
961+
{
962+
unsigned long metric;
963+
964+
if (scheme->wmarks.metric == DAMOS_WMARK_NONE)
965+
return 0;
966+
967+
metric = damos_wmark_metric_value(scheme->wmarks.metric);
968+
/* higher than high watermark or lower than low watermark */
969+
if (metric > scheme->wmarks.high || scheme->wmarks.low > metric) {
970+
if (scheme->wmarks.activated)
971+
pr_debug("inactivate a scheme (%d) for %s wmark\n",
972+
scheme->action,
973+
metric > scheme->wmarks.high ?
974+
"high" : "low");
975+
scheme->wmarks.activated = false;
976+
return scheme->wmarks.interval;
977+
}
978+
979+
/* inactive and higher than middle watermark */
980+
if ((scheme->wmarks.high >= metric && metric >= scheme->wmarks.mid) &&
981+
!scheme->wmarks.activated)
982+
return scheme->wmarks.interval;
983+
984+
if (!scheme->wmarks.activated)
985+
pr_debug("activate a scheme (%d)\n", scheme->action);
986+
scheme->wmarks.activated = true;
987+
return 0;
988+
}
989+
990+
static void kdamond_usleep(unsigned long usecs)
991+
{
992+
if (usecs > 100 * 1000)
993+
schedule_timeout_interruptible(usecs_to_jiffies(usecs));
994+
else
995+
usleep_range(usecs, usecs + 1);
996+
}
997+
998+
/* Returns negative error code if it's not activated but should return */
999+
static int kdamond_wait_activation(struct damon_ctx *ctx)
1000+
{
1001+
struct damos *s;
1002+
unsigned long wait_time;
1003+
unsigned long min_wait_time = 0;
1004+
1005+
while (!kdamond_need_stop(ctx)) {
1006+
damon_for_each_scheme(s, ctx) {
1007+
wait_time = damos_wmark_wait_us(s);
1008+
if (!min_wait_time || wait_time < min_wait_time)
1009+
min_wait_time = wait_time;
1010+
}
1011+
if (!min_wait_time)
1012+
return 0;
1013+
1014+
kdamond_usleep(min_wait_time);
1015+
}
1016+
return -EBUSY;
1017+
}
1018+
9271019
static void set_kdamond_stop(struct damon_ctx *ctx)
9281020
{
9291021
mutex_lock(&ctx->kdamond_lock);
@@ -952,6 +1044,9 @@ static int kdamond_fn(void *data)
9521044
sz_limit = damon_region_sz_limit(ctx);
9531045

9541046
while (!kdamond_need_stop(ctx)) {
1047+
if (kdamond_wait_activation(ctx))
1048+
continue;
1049+
9551050
if (ctx->primitive.prepare_access_checks)
9561051
ctx->primitive.prepare_access_checks(ctx);
9571052
if (ctx->callback.after_sampling &&

mm/damon/dbgfs.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,9 @@ static struct damos **str_to_schemes(const char *str, ssize_t len,
195195
*nr_schemes = 0;
196196
while (pos < len && *nr_schemes < max_nr_schemes) {
197197
struct damos_quota quota = {};
198+
struct damos_watermarks wmarks = {
199+
.metric = DAMOS_WMARK_NONE,
200+
};
198201

199202
ret = sscanf(&str[pos],
200203
"%lu %lu %u %u %u %u %u %lu %lu %lu %u %u %u%n",
@@ -212,7 +215,7 @@ static struct damos **str_to_schemes(const char *str, ssize_t len,
212215

213216
pos += parsed;
214217
scheme = damon_new_scheme(min_sz, max_sz, min_nr_a, max_nr_a,
215-
min_age, max_age, action, &quota);
218+
min_age, max_age, action, &quota, &wmarks);
216219
if (!scheme)
217220
goto fail;
218221

0 commit comments

Comments
 (0)