Skip to content

Commit 2b8a248

Browse files
sjp38torvalds
authored andcommitted
mm/damon/schemes: implement size quota for schemes application speed control
There could be arbitrarily large memory regions fulfilling the target data access pattern of a DAMON-based operation scheme. In the case, applying the action of the scheme could incur too high overhead. To provide an intuitive way for avoiding it, this implements a feature called size quota. If the quota is set, DAMON tries to apply the action only up to the given amount of memory regions within a given time window. 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 57223ac commit 2b8a248

File tree

3 files changed

+87
-13
lines changed

3 files changed

+87
-13
lines changed

include/linux/damon.h

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,26 @@ enum damos_action {
8989
DAMOS_STAT, /* Do nothing but only record the stat */
9090
};
9191

92+
/**
93+
* struct damos_quota - Controls the aggressiveness of the given scheme.
94+
* @sz: Maximum bytes of memory that the action can be applied.
95+
* @reset_interval: Charge reset interval in milliseconds.
96+
*
97+
* To avoid consuming too much CPU time or IO resources for applying the
98+
* &struct damos->action to large memory, DAMON allows users to set a size
99+
* quota. The quota can be set by writing non-zero values to &sz. If the size
100+
* quota is set, DAMON tries to apply the action only up to &sz bytes within
101+
* &reset_interval.
102+
*/
103+
struct damos_quota {
104+
unsigned long sz;
105+
unsigned long reset_interval;
106+
107+
/* private: For charging the quota */
108+
unsigned long charged_sz;
109+
unsigned long charged_from;
110+
};
111+
92112
/**
93113
* struct damos - Represents a Data Access Monitoring-based Operation Scheme.
94114
* @min_sz_region: Minimum size of target regions.
@@ -98,13 +118,20 @@ enum damos_action {
98118
* @min_age_region: Minimum age of target regions.
99119
* @max_age_region: Maximum age of target regions.
100120
* @action: &damo_action to be applied to the target regions.
121+
* @quota: Control the aggressiveness of this scheme.
101122
* @stat_count: Total number of regions that this scheme is applied.
102123
* @stat_sz: Total size of regions that this scheme is applied.
103124
* @list: List head for siblings.
104125
*
105-
* For each aggregation interval, DAMON applies @action to monitoring target
106-
* regions fit in the condition and updates the statistics. Note that both
107-
* the minimums and the maximums are inclusive.
126+
* For each aggregation interval, DAMON finds regions which fit in the
127+
* condition (&min_sz_region, &max_sz_region, &min_nr_accesses,
128+
* &max_nr_accesses, &min_age_region, &max_age_region) and applies &action to
129+
* those. To avoid consuming too much CPU time or IO resources for the
130+
* &action, &quota is used.
131+
*
132+
* After applying the &action to each region, &stat_count and &stat_sz is
133+
* updated to reflect the number of regions and total size of regions that the
134+
* &action is applied.
108135
*/
109136
struct damos {
110137
unsigned long min_sz_region;
@@ -114,6 +141,7 @@ struct damos {
114141
unsigned int min_age_region;
115142
unsigned int max_age_region;
116143
enum damos_action action;
144+
struct damos_quota quota;
117145
unsigned long stat_count;
118146
unsigned long stat_sz;
119147
struct list_head list;
@@ -310,7 +338,7 @@ struct damos *damon_new_scheme(
310338
unsigned long min_sz_region, unsigned long max_sz_region,
311339
unsigned int min_nr_accesses, unsigned int max_nr_accesses,
312340
unsigned int min_age_region, unsigned int max_age_region,
313-
enum damos_action action);
341+
enum damos_action action, struct damos_quota *quota);
314342
void damon_add_scheme(struct damon_ctx *ctx, struct damos *s);
315343
void damon_destroy_scheme(struct damos *s);
316344

mm/damon/core.c

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ struct damos *damon_new_scheme(
8989
unsigned long min_sz_region, unsigned long max_sz_region,
9090
unsigned int min_nr_accesses, unsigned int max_nr_accesses,
9191
unsigned int min_age_region, unsigned int max_age_region,
92-
enum damos_action action)
92+
enum damos_action action, struct damos_quota *quota)
9393
{
9494
struct damos *scheme;
9595

@@ -107,6 +107,11 @@ struct damos *damon_new_scheme(
107107
scheme->stat_sz = 0;
108108
INIT_LIST_HEAD(&scheme->list);
109109

110+
scheme->quota.sz = quota->sz;
111+
scheme->quota.reset_interval = quota->reset_interval;
112+
scheme->quota.charged_sz = 0;
113+
scheme->quota.charged_from = 0;
114+
110115
return scheme;
111116
}
112117

@@ -530,38 +535,77 @@ static void kdamond_reset_aggregated(struct damon_ctx *c)
530535
}
531536
}
532537

538+
static void damon_split_region_at(struct damon_ctx *ctx,
539+
struct damon_target *t, struct damon_region *r,
540+
unsigned long sz_r);
541+
533542
static void damon_do_apply_schemes(struct damon_ctx *c,
534543
struct damon_target *t,
535544
struct damon_region *r)
536545
{
537546
struct damos *s;
538-
unsigned long sz;
539547

540548
damon_for_each_scheme(s, c) {
541-
sz = r->ar.end - r->ar.start;
549+
struct damos_quota *quota = &s->quota;
550+
unsigned long sz = r->ar.end - r->ar.start;
551+
552+
/* Check the quota */
553+
if (quota->sz && quota->charged_sz >= quota->sz)
554+
continue;
555+
556+
/* Check the target regions condition */
542557
if (sz < s->min_sz_region || s->max_sz_region < sz)
543558
continue;
544559
if (r->nr_accesses < s->min_nr_accesses ||
545560
s->max_nr_accesses < r->nr_accesses)
546561
continue;
547562
if (r->age < s->min_age_region || s->max_age_region < r->age)
548563
continue;
549-
s->stat_count++;
550-
s->stat_sz += sz;
551-
if (c->primitive.apply_scheme)
564+
565+
/* Apply the scheme */
566+
if (c->primitive.apply_scheme) {
567+
if (quota->sz && quota->charged_sz + sz > quota->sz) {
568+
sz = ALIGN_DOWN(quota->sz - quota->charged_sz,
569+
DAMON_MIN_REGION);
570+
if (!sz)
571+
goto update_stat;
572+
damon_split_region_at(c, t, r, sz);
573+
}
552574
c->primitive.apply_scheme(c, t, r, s);
575+
quota->charged_sz += sz;
576+
}
553577
if (s->action != DAMOS_STAT)
554578
r->age = 0;
579+
580+
update_stat:
581+
s->stat_count++;
582+
s->stat_sz += sz;
555583
}
556584
}
557585

558586
static void kdamond_apply_schemes(struct damon_ctx *c)
559587
{
560588
struct damon_target *t;
561-
struct damon_region *r;
589+
struct damon_region *r, *next_r;
590+
struct damos *s;
591+
592+
damon_for_each_scheme(s, c) {
593+
struct damos_quota *quota = &s->quota;
594+
595+
if (!quota->sz)
596+
continue;
597+
598+
/* New charge window starts */
599+
if (time_after_eq(jiffies, quota->charged_from +
600+
msecs_to_jiffies(
601+
quota->reset_interval))) {
602+
quota->charged_from = jiffies;
603+
quota->charged_sz = 0;
604+
}
605+
}
562606

563607
damon_for_each_target(t, c) {
564-
damon_for_each_region(r, t)
608+
damon_for_each_region_safe(r, next_r, t)
565609
damon_do_apply_schemes(c, t, r);
566610
}
567611
}

mm/damon/dbgfs.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,8 @@ static struct damos **str_to_schemes(const char *str, ssize_t len,
188188

189189
*nr_schemes = 0;
190190
while (pos < len && *nr_schemes < max_nr_schemes) {
191+
struct damos_quota quota = {};
192+
191193
ret = sscanf(&str[pos], "%lu %lu %u %u %u %u %u%n",
192194
&min_sz, &max_sz, &min_nr_a, &max_nr_a,
193195
&min_age, &max_age, &action, &parsed);
@@ -200,7 +202,7 @@ static struct damos **str_to_schemes(const char *str, ssize_t len,
200202

201203
pos += parsed;
202204
scheme = damon_new_scheme(min_sz, max_sz, min_nr_a, max_nr_a,
203-
min_age, max_age, action);
205+
min_age, max_age, action, &quota);
204206
if (!scheme)
205207
goto fail;
206208

0 commit comments

Comments
 (0)