Skip to content

Commit 1f366e4

Browse files
sjp38torvalds
authored andcommitted
mm/damon/core: implement DAMON-based Operation Schemes (DAMOS)
In many cases, users might use DAMON for simple data access aware memory management optimizations such as applying an operation scheme to a memory region of a specific size having a specific access frequency for a specific time. For example, "page out a memory region larger than 100 MiB but having a low access frequency more than 10 minutes", or "Use THP for a memory region larger than 2 MiB having a high access frequency for more than 2 seconds". Most simple form of the solution would be doing offline data access pattern profiling using DAMON and modifying the application source code or system configuration based on the profiling results. Or, developing a daemon constructed with two modules (one for access monitoring and the other for applying memory management actions via mlock(), madvise(), sysctl, etc) is imaginable. To avoid users spending their time for implementation of such simple data access monitoring-based operation schemes, this makes DAMON to handle such schemes directly. With this change, users can simply specify their desired schemes to DAMON. Then, DAMON will automatically apply the schemes to the user-specified target processes. Each of the schemes is composed with conditions for filtering of the target memory regions and desired memory management action for the target. Specifically, the format is:: <min/max size> <min/max access frequency> <min/max age> <action> The filtering conditions are size of memory region, number of accesses to the region monitored by DAMON, and the age of the region. The age of region is incremented periodically but reset when its addresses or access frequency has significantly changed or the action of a scheme was applied. For the action, current implementation supports a few of madvise()-like hints, ``WILLNEED``, ``COLD``, ``PAGEOUT``, ``HUGEPAGE``, and ``NOHUGEPAGE``. Because DAMON supports various address spaces and application of the actions to a monitoring target region is dependent to the type of the target address space, the application code should be implemented by each primitives and registered to the framework. Note that this only implements the framework part. Following commit will implement the action applications for virtual address spaces primitives. 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 Rienjes <[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 fda504f commit 1f366e4

File tree

2 files changed

+175
-0
lines changed

2 files changed

+175
-0
lines changed

include/linux/damon.h

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,48 @@ struct damon_target {
6969
struct list_head list;
7070
};
7171

72+
/**
73+
* enum damos_action - Represents an action of a Data Access Monitoring-based
74+
* Operation Scheme.
75+
*
76+
* @DAMOS_WILLNEED: Call ``madvise()`` for the region with MADV_WILLNEED.
77+
* @DAMOS_COLD: Call ``madvise()`` for the region with MADV_COLD.
78+
* @DAMOS_PAGEOUT: Call ``madvise()`` for the region with MADV_PAGEOUT.
79+
* @DAMOS_HUGEPAGE: Call ``madvise()`` for the region with MADV_HUGEPAGE.
80+
* @DAMOS_NOHUGEPAGE: Call ``madvise()`` for the region with MADV_NOHUGEPAGE.
81+
*/
82+
enum damos_action {
83+
DAMOS_WILLNEED,
84+
DAMOS_COLD,
85+
DAMOS_PAGEOUT,
86+
DAMOS_HUGEPAGE,
87+
DAMOS_NOHUGEPAGE,
88+
};
89+
90+
/**
91+
* struct damos - Represents a Data Access Monitoring-based Operation Scheme.
92+
* @min_sz_region: Minimum size of target regions.
93+
* @max_sz_region: Maximum size of target regions.
94+
* @min_nr_accesses: Minimum ``->nr_accesses`` of target regions.
95+
* @max_nr_accesses: Maximum ``->nr_accesses`` of target regions.
96+
* @min_age_region: Minimum age of target regions.
97+
* @max_age_region: Maximum age of target regions.
98+
* @action: &damo_action to be applied to the target regions.
99+
* @list: List head for siblings.
100+
*
101+
* Note that both the minimums and the maximums are inclusive.
102+
*/
103+
struct damos {
104+
unsigned long min_sz_region;
105+
unsigned long max_sz_region;
106+
unsigned int min_nr_accesses;
107+
unsigned int max_nr_accesses;
108+
unsigned int min_age_region;
109+
unsigned int max_age_region;
110+
enum damos_action action;
111+
struct list_head list;
112+
};
113+
72114
struct damon_ctx;
73115

74116
/**
@@ -79,6 +121,7 @@ struct damon_ctx;
79121
* @prepare_access_checks: Prepare next access check of target regions.
80122
* @check_accesses: Check the accesses to target regions.
81123
* @reset_aggregated: Reset aggregated accesses monitoring results.
124+
* @apply_scheme: Apply a DAMON-based operation scheme.
82125
* @target_valid: Determine if the target is valid.
83126
* @cleanup: Clean up the context.
84127
*
@@ -104,6 +147,9 @@ struct damon_ctx;
104147
* of its update. The value will be used for regions adjustment threshold.
105148
* @reset_aggregated should reset the access monitoring results that aggregated
106149
* by @check_accesses.
150+
* @apply_scheme is called from @kdamond when a region for user provided
151+
* DAMON-based operation scheme is found. It should apply the scheme's action
152+
* to the region. This is not used for &DAMON_ARBITRARY_TARGET case.
107153
* @target_valid should check whether the target is still valid for the
108154
* monitoring.
109155
* @cleanup is called from @kdamond just before its termination.
@@ -114,6 +160,8 @@ struct damon_primitive {
114160
void (*prepare_access_checks)(struct damon_ctx *context);
115161
unsigned int (*check_accesses)(struct damon_ctx *context);
116162
void (*reset_aggregated)(struct damon_ctx *context);
163+
int (*apply_scheme)(struct damon_ctx *context, struct damon_target *t,
164+
struct damon_region *r, struct damos *scheme);
117165
bool (*target_valid)(void *target);
118166
void (*cleanup)(struct damon_ctx *context);
119167
};
@@ -192,6 +240,7 @@ struct damon_callback {
192240
* @min_nr_regions: The minimum number of adaptive monitoring regions.
193241
* @max_nr_regions: The maximum number of adaptive monitoring regions.
194242
* @adaptive_targets: Head of monitoring targets (&damon_target) list.
243+
* @schemes: Head of schemes (&damos) list.
195244
*/
196245
struct damon_ctx {
197246
unsigned long sample_interval;
@@ -213,6 +262,7 @@ struct damon_ctx {
213262
unsigned long min_nr_regions;
214263
unsigned long max_nr_regions;
215264
struct list_head adaptive_targets;
265+
struct list_head schemes;
216266
};
217267

218268
#define damon_next_region(r) \
@@ -233,6 +283,12 @@ struct damon_ctx {
233283
#define damon_for_each_target_safe(t, next, ctx) \
234284
list_for_each_entry_safe(t, next, &(ctx)->adaptive_targets, list)
235285

286+
#define damon_for_each_scheme(s, ctx) \
287+
list_for_each_entry(s, &(ctx)->schemes, list)
288+
289+
#define damon_for_each_scheme_safe(s, next, ctx) \
290+
list_for_each_entry_safe(s, next, &(ctx)->schemes, list)
291+
236292
#ifdef CONFIG_DAMON
237293

238294
struct damon_region *damon_new_region(unsigned long start, unsigned long end);
@@ -242,6 +298,14 @@ inline void damon_insert_region(struct damon_region *r,
242298
void damon_add_region(struct damon_region *r, struct damon_target *t);
243299
void damon_destroy_region(struct damon_region *r, struct damon_target *t);
244300

301+
struct damos *damon_new_scheme(
302+
unsigned long min_sz_region, unsigned long max_sz_region,
303+
unsigned int min_nr_accesses, unsigned int max_nr_accesses,
304+
unsigned int min_age_region, unsigned int max_age_region,
305+
enum damos_action action);
306+
void damon_add_scheme(struct damon_ctx *ctx, struct damos *s);
307+
void damon_destroy_scheme(struct damos *s);
308+
245309
struct damon_target *damon_new_target(unsigned long id);
246310
void damon_add_target(struct damon_ctx *ctx, struct damon_target *t);
247311
void damon_free_target(struct damon_target *t);
@@ -255,6 +319,8 @@ int damon_set_targets(struct damon_ctx *ctx,
255319
int damon_set_attrs(struct damon_ctx *ctx, unsigned long sample_int,
256320
unsigned long aggr_int, unsigned long primitive_upd_int,
257321
unsigned long min_nr_reg, unsigned long max_nr_reg);
322+
int damon_set_schemes(struct damon_ctx *ctx,
323+
struct damos **schemes, ssize_t nr_schemes);
258324
int damon_nr_running_ctxs(void);
259325

260326
int damon_start(struct damon_ctx **ctxs, int nr_ctxs);

mm/damon/core.c

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,50 @@ void damon_destroy_region(struct damon_region *r, struct damon_target *t)
8585
damon_free_region(r);
8686
}
8787

88+
struct damos *damon_new_scheme(
89+
unsigned long min_sz_region, unsigned long max_sz_region,
90+
unsigned int min_nr_accesses, unsigned int max_nr_accesses,
91+
unsigned int min_age_region, unsigned int max_age_region,
92+
enum damos_action action)
93+
{
94+
struct damos *scheme;
95+
96+
scheme = kmalloc(sizeof(*scheme), GFP_KERNEL);
97+
if (!scheme)
98+
return NULL;
99+
scheme->min_sz_region = min_sz_region;
100+
scheme->max_sz_region = max_sz_region;
101+
scheme->min_nr_accesses = min_nr_accesses;
102+
scheme->max_nr_accesses = max_nr_accesses;
103+
scheme->min_age_region = min_age_region;
104+
scheme->max_age_region = max_age_region;
105+
scheme->action = action;
106+
INIT_LIST_HEAD(&scheme->list);
107+
108+
return scheme;
109+
}
110+
111+
void damon_add_scheme(struct damon_ctx *ctx, struct damos *s)
112+
{
113+
list_add_tail(&s->list, &ctx->schemes);
114+
}
115+
116+
static void damon_del_scheme(struct damos *s)
117+
{
118+
list_del(&s->list);
119+
}
120+
121+
static void damon_free_scheme(struct damos *s)
122+
{
123+
kfree(s);
124+
}
125+
126+
void damon_destroy_scheme(struct damos *s)
127+
{
128+
damon_del_scheme(s);
129+
damon_free_scheme(s);
130+
}
131+
88132
/*
89133
* Construct a damon_target struct
90134
*
@@ -156,6 +200,7 @@ struct damon_ctx *damon_new_ctx(void)
156200
ctx->max_nr_regions = 1000;
157201

158202
INIT_LIST_HEAD(&ctx->adaptive_targets);
203+
INIT_LIST_HEAD(&ctx->schemes);
159204

160205
return ctx;
161206
}
@@ -175,7 +220,13 @@ static void damon_destroy_targets(struct damon_ctx *ctx)
175220

176221
void damon_destroy_ctx(struct damon_ctx *ctx)
177222
{
223+
struct damos *s, *next_s;
224+
178225
damon_destroy_targets(ctx);
226+
227+
damon_for_each_scheme_safe(s, next_s, ctx)
228+
damon_destroy_scheme(s);
229+
179230
kfree(ctx);
180231
}
181232

@@ -250,6 +301,30 @@ int damon_set_attrs(struct damon_ctx *ctx, unsigned long sample_int,
250301
return 0;
251302
}
252303

304+
/**
305+
* damon_set_schemes() - Set data access monitoring based operation schemes.
306+
* @ctx: monitoring context
307+
* @schemes: array of the schemes
308+
* @nr_schemes: number of entries in @schemes
309+
*
310+
* This function should not be called while the kdamond of the context is
311+
* running.
312+
*
313+
* Return: 0 if success, or negative error code otherwise.
314+
*/
315+
int damon_set_schemes(struct damon_ctx *ctx, struct damos **schemes,
316+
ssize_t nr_schemes)
317+
{
318+
struct damos *s, *next;
319+
ssize_t i;
320+
321+
damon_for_each_scheme_safe(s, next, ctx)
322+
damon_destroy_scheme(s);
323+
for (i = 0; i < nr_schemes; i++)
324+
damon_add_scheme(ctx, schemes[i]);
325+
return 0;
326+
}
327+
253328
/**
254329
* damon_nr_running_ctxs() - Return number of currently running contexts.
255330
*/
@@ -453,6 +528,39 @@ static void kdamond_reset_aggregated(struct damon_ctx *c)
453528
}
454529
}
455530

531+
static void damon_do_apply_schemes(struct damon_ctx *c,
532+
struct damon_target *t,
533+
struct damon_region *r)
534+
{
535+
struct damos *s;
536+
unsigned long sz;
537+
538+
damon_for_each_scheme(s, c) {
539+
sz = r->ar.end - r->ar.start;
540+
if (sz < s->min_sz_region || s->max_sz_region < sz)
541+
continue;
542+
if (r->nr_accesses < s->min_nr_accesses ||
543+
s->max_nr_accesses < r->nr_accesses)
544+
continue;
545+
if (r->age < s->min_age_region || s->max_age_region < r->age)
546+
continue;
547+
if (c->primitive.apply_scheme)
548+
c->primitive.apply_scheme(c, t, r, s);
549+
r->age = 0;
550+
}
551+
}
552+
553+
static void kdamond_apply_schemes(struct damon_ctx *c)
554+
{
555+
struct damon_target *t;
556+
struct damon_region *r;
557+
558+
damon_for_each_target(t, c) {
559+
damon_for_each_region(r, t)
560+
damon_do_apply_schemes(c, t, r);
561+
}
562+
}
563+
456564
#define sz_damon_region(r) (r->ar.end - r->ar.start)
457565

458566
/*
@@ -693,6 +801,7 @@ static int kdamond_fn(void *data)
693801
if (ctx->callback.after_aggregation &&
694802
ctx->callback.after_aggregation(ctx))
695803
set_kdamond_stop(ctx);
804+
kdamond_apply_schemes(ctx);
696805
kdamond_reset_aggregated(ctx);
697806
kdamond_split_regions(ctx);
698807
if (ctx->primitive.reset_aggregated)

0 commit comments

Comments
 (0)