Skip to content

Commit 1cd2430

Browse files
sjp38torvalds
authored andcommitted
mm/damon/schemes: implement time quota
The size quota feature of DAMOS is useful for IO resource-critical systems, but not so intuitive for CPU time-critical systems. Systems using zram or zswap-like swap device would be examples. To provide another intuitive ways for such systems, this implements time-based quota for DAMON-based Operation Schemes. If the quota is set, DAMOS tries to use only up to the user-defined quota of CPU time within a given time window. Link: https://lkml.kernel.org/r/20211019150731.16699-5-sj@kernel.org Signed-off-by: SeongJae Park <sj@kernel.org> Cc: Amit Shah <amit@kernel.org> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org> Cc: David Hildenbrand <david@redhat.com> Cc: David Rientjes <rientjes@google.com> Cc: David Woodhouse <dwmw@amazon.com> Cc: Greg Thelen <gthelen@google.com> Cc: Jonathan Cameron <Jonathan.Cameron@huawei.com> Cc: Jonathan Corbet <corbet@lwn.net> Cc: Leonard Foerster <foersleo@amazon.de> Cc: Marco Elver <elver@google.com> Cc: Markus Boehme <markubo@amazon.de> Cc: Shakeel Butt <shakeelb@google.com> Cc: Shuah Khan <shuah@kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
1 parent 5058519 commit 1cd2430

File tree

2 files changed

+60
-10
lines changed

2 files changed

+60
-10
lines changed

include/linux/damon.h

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -91,20 +91,35 @@ enum damos_action {
9191

9292
/**
9393
* struct damos_quota - Controls the aggressiveness of the given scheme.
94+
* @ms: Maximum milliseconds that the scheme can use.
9495
* @sz: Maximum bytes of memory that the action can be applied.
9596
* @reset_interval: Charge reset interval in milliseconds.
9697
*
9798
* 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.
99+
* &struct damos->action to large memory, DAMON allows users to set time and/or
100+
* size quotas. The quotas can be set by writing non-zero values to &ms and
101+
* &sz, respectively. If the time quota is set, DAMON tries to use only up to
102+
* &ms milliseconds within &reset_interval for applying the action. If the
103+
* size quota is set, DAMON tries to apply the action only up to &sz bytes
104+
* within &reset_interval.
105+
*
106+
* Internally, the time quota is transformed to a size quota using estimated
107+
* throughput of the scheme's action. DAMON then compares it against &sz and
108+
* uses smaller one as the effective quota.
102109
*/
103110
struct damos_quota {
111+
unsigned long ms;
104112
unsigned long sz;
105113
unsigned long reset_interval;
106114

107-
/* private: For charging the quota */
115+
/* private: */
116+
/* For throughput estimation */
117+
unsigned long total_charged_sz;
118+
unsigned long total_charged_ns;
119+
120+
unsigned long esz; /* Effective size quota in bytes */
121+
122+
/* For charging the quota */
108123
unsigned long charged_sz;
109124
unsigned long charged_from;
110125
struct damon_target *charge_target_from;

mm/damon/core.c

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,12 @@ struct damos *damon_new_scheme(
107107
scheme->stat_sz = 0;
108108
INIT_LIST_HEAD(&scheme->list);
109109

110+
scheme->quota.ms = quota->ms;
110111
scheme->quota.sz = quota->sz;
111112
scheme->quota.reset_interval = quota->reset_interval;
113+
scheme->quota.total_charged_sz = 0;
114+
scheme->quota.total_charged_ns = 0;
115+
scheme->quota.esz = 0;
112116
scheme->quota.charged_sz = 0;
113117
scheme->quota.charged_from = 0;
114118
scheme->quota.charge_target_from = NULL;
@@ -550,9 +554,10 @@ static void damon_do_apply_schemes(struct damon_ctx *c,
550554
damon_for_each_scheme(s, c) {
551555
struct damos_quota *quota = &s->quota;
552556
unsigned long sz = r->ar.end - r->ar.start;
557+
struct timespec64 begin, end;
553558

554559
/* Check the quota */
555-
if (quota->sz && quota->charged_sz >= quota->sz)
560+
if (quota->esz && quota->charged_sz >= quota->esz)
556561
continue;
557562

558563
/* Skip previously charged regions */
@@ -597,16 +602,21 @@ static void damon_do_apply_schemes(struct damon_ctx *c,
597602

598603
/* Apply the scheme */
599604
if (c->primitive.apply_scheme) {
600-
if (quota->sz && quota->charged_sz + sz > quota->sz) {
601-
sz = ALIGN_DOWN(quota->sz - quota->charged_sz,
605+
if (quota->esz &&
606+
quota->charged_sz + sz > quota->esz) {
607+
sz = ALIGN_DOWN(quota->esz - quota->charged_sz,
602608
DAMON_MIN_REGION);
603609
if (!sz)
604610
goto update_stat;
605611
damon_split_region_at(c, t, r, sz);
606612
}
613+
ktime_get_coarse_ts64(&begin);
607614
c->primitive.apply_scheme(c, t, r, s);
615+
ktime_get_coarse_ts64(&end);
616+
quota->total_charged_ns += timespec64_to_ns(&end) -
617+
timespec64_to_ns(&begin);
608618
quota->charged_sz += sz;
609-
if (quota->sz && quota->charged_sz >= quota->sz) {
619+
if (quota->esz && quota->charged_sz >= quota->esz) {
610620
quota->charge_target_from = t;
611621
quota->charge_addr_from = r->ar.end + 1;
612622
}
@@ -620,6 +630,29 @@ static void damon_do_apply_schemes(struct damon_ctx *c,
620630
}
621631
}
622632

633+
/* Shouldn't be called if quota->ms and quota->sz are zero */
634+
static void damos_set_effective_quota(struct damos_quota *quota)
635+
{
636+
unsigned long throughput;
637+
unsigned long esz;
638+
639+
if (!quota->ms) {
640+
quota->esz = quota->sz;
641+
return;
642+
}
643+
644+
if (quota->total_charged_ns)
645+
throughput = quota->total_charged_sz * 1000000 /
646+
quota->total_charged_ns;
647+
else
648+
throughput = PAGE_SIZE * 1024;
649+
esz = throughput * quota->ms;
650+
651+
if (quota->sz && quota->sz < esz)
652+
esz = quota->sz;
653+
quota->esz = esz;
654+
}
655+
623656
static void kdamond_apply_schemes(struct damon_ctx *c)
624657
{
625658
struct damon_target *t;
@@ -629,15 +662,17 @@ static void kdamond_apply_schemes(struct damon_ctx *c)
629662
damon_for_each_scheme(s, c) {
630663
struct damos_quota *quota = &s->quota;
631664

632-
if (!quota->sz)
665+
if (!quota->ms && !quota->sz)
633666
continue;
634667

635668
/* New charge window starts */
636669
if (time_after_eq(jiffies, quota->charged_from +
637670
msecs_to_jiffies(
638671
quota->reset_interval))) {
672+
quota->total_charged_sz += quota->charged_sz;
639673
quota->charged_from = jiffies;
640674
quota->charged_sz = 0;
675+
damos_set_effective_quota(quota);
641676
}
642677
}
643678

0 commit comments

Comments
 (0)