Skip to content

Commit f23b8ee

Browse files
sj-awstorvalds
authored andcommitted
mm/damon/core: implement region-based sampling
To avoid the unbounded increase of the overhead, DAMON groups adjacent pages that are assumed to have the same access frequencies into a region. As long as the assumption (pages in a region have the same access frequencies) is kept, only one page in the region is required to be checked. Thus, for each ``sampling interval``, 1. the 'prepare_access_checks' primitive picks one page in each region, 2. waits for one ``sampling interval``, 3. checks whether the page is accessed meanwhile, and 4. increases the access count of the region if so. Therefore, the monitoring overhead is controllable by adjusting the number of regions. DAMON allows both the underlying primitives and user callbacks to adjust regions for the trade-off. In other words, this commit makes DAMON to use not only time-based sampling but also space-based sampling. This scheme, however, cannot preserve the quality of the output if the assumption is not guaranteed. Next commit will address this problem. Link: https://lkml.kernel.org/r/[email protected] Signed-off-by: SeongJae Park <[email protected]> Reviewed-by: Leonard Foerster <[email protected]> Reviewed-by: Fernand Sieber <[email protected]> Acked-by: Shakeel Butt <[email protected]> Cc: Alexander Shishkin <[email protected]> Cc: Amit Shah <[email protected]> Cc: Benjamin Herrenschmidt <[email protected]> Cc: Brendan Higgins <[email protected]> Cc: David Hildenbrand <[email protected]> Cc: David Rientjes <[email protected]> Cc: David Woodhouse <[email protected]> Cc: Fan Du <[email protected]> Cc: Greg Kroah-Hartman <[email protected]> Cc: Greg Thelen <[email protected]> Cc: Ingo Molnar <[email protected]> Cc: Joe Perches <[email protected]> Cc: Jonathan Cameron <[email protected]> Cc: Jonathan Corbet <[email protected]> Cc: Marco Elver <[email protected]> Cc: Markus Boehme <[email protected]> Cc: Maximilian Heyne <[email protected]> Cc: Mel Gorman <[email protected]> Cc: Minchan Kim <[email protected]> Cc: Namhyung Kim <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Rik van Riel <[email protected]> Cc: Shuah Khan <[email protected]> Cc: Steven Rostedt (VMware) <[email protected]> Cc: Vladimir Davydov <[email protected]> Cc: Vlastimil Babka <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 2224d84 commit f23b8ee

File tree

2 files changed

+213
-7
lines changed

2 files changed

+213
-7
lines changed

include/linux/damon.h

Lines changed: 74 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,48 @@
1212
#include <linux/time64.h>
1313
#include <linux/types.h>
1414

15+
/**
16+
* struct damon_addr_range - Represents an address region of [@start, @end).
17+
* @start: Start address of the region (inclusive).
18+
* @end: End address of the region (exclusive).
19+
*/
20+
struct damon_addr_range {
21+
unsigned long start;
22+
unsigned long end;
23+
};
24+
25+
/**
26+
* struct damon_region - Represents a monitoring target region.
27+
* @ar: The address range of the region.
28+
* @sampling_addr: Address of the sample for the next access check.
29+
* @nr_accesses: Access frequency of this region.
30+
* @list: List head for siblings.
31+
*/
32+
struct damon_region {
33+
struct damon_addr_range ar;
34+
unsigned long sampling_addr;
35+
unsigned int nr_accesses;
36+
struct list_head list;
37+
};
38+
39+
/**
40+
* struct damon_target - Represents a monitoring target.
41+
* @id: Unique identifier for this target.
42+
* @regions_list: Head of the monitoring target regions of this target.
43+
* @list: List head for siblings.
44+
*
45+
* Each monitoring context could have multiple targets. For example, a context
46+
* for virtual memory address spaces could have multiple target processes. The
47+
* @id of each target should be unique among the targets of the context. For
48+
* example, in the virtual address monitoring context, it could be a pidfd or
49+
* an address of an mm_struct.
50+
*/
51+
struct damon_target {
52+
unsigned long id;
53+
struct list_head regions_list;
54+
struct list_head list;
55+
};
56+
1557
struct damon_ctx;
1658

1759
/**
@@ -36,7 +78,7 @@ struct damon_ctx;
3678
*
3779
* @init should initialize primitive-internal data structures. For example,
3880
* this could be used to construct proper monitoring target regions and link
39-
* those to @damon_ctx.target.
81+
* those to @damon_ctx.adaptive_targets.
4082
* @update should update the primitive-internal data structures. For example,
4183
* this could be used to update monitoring target regions for current status.
4284
* @prepare_access_checks should manipulate the monitoring regions to be
@@ -130,7 +172,7 @@ struct damon_callback {
130172
* @primitive: Set of monitoring primitives for given use cases.
131173
* @callback: Set of callbacks for monitoring events notifications.
132174
*
133-
* @target: Pointer to the user-defined monitoring target.
175+
* @region_targets: Head of monitoring targets (&damon_target) list.
134176
*/
135177
struct damon_ctx {
136178
unsigned long sample_interval;
@@ -149,11 +191,40 @@ struct damon_ctx {
149191
struct damon_primitive primitive;
150192
struct damon_callback callback;
151193

152-
void *target;
194+
struct list_head region_targets;
153195
};
154196

197+
#define damon_next_region(r) \
198+
(container_of(r->list.next, struct damon_region, list))
199+
200+
#define damon_prev_region(r) \
201+
(container_of(r->list.prev, struct damon_region, list))
202+
203+
#define damon_for_each_region(r, t) \
204+
list_for_each_entry(r, &t->regions_list, list)
205+
206+
#define damon_for_each_region_safe(r, next, t) \
207+
list_for_each_entry_safe(r, next, &t->regions_list, list)
208+
209+
#define damon_for_each_target(t, ctx) \
210+
list_for_each_entry(t, &(ctx)->region_targets, list)
211+
212+
#define damon_for_each_target_safe(t, next, ctx) \
213+
list_for_each_entry_safe(t, next, &(ctx)->region_targets, list)
214+
155215
#ifdef CONFIG_DAMON
156216

217+
struct damon_region *damon_new_region(unsigned long start, unsigned long end);
218+
inline void damon_insert_region(struct damon_region *r,
219+
struct damon_region *prev, struct damon_region *next);
220+
void damon_add_region(struct damon_region *r, struct damon_target *t);
221+
void damon_destroy_region(struct damon_region *r);
222+
223+
struct damon_target *damon_new_target(unsigned long id);
224+
void damon_add_target(struct damon_ctx *ctx, struct damon_target *t);
225+
void damon_free_target(struct damon_target *t);
226+
void damon_destroy_target(struct damon_target *t);
227+
157228
struct damon_ctx *damon_new_ctx(void);
158229
void damon_destroy_ctx(struct damon_ctx *ctx);
159230
int damon_set_attrs(struct damon_ctx *ctx, unsigned long sample_int,

mm/damon/core.c

Lines changed: 139 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,101 @@
1515
static DEFINE_MUTEX(damon_lock);
1616
static int nr_running_ctxs;
1717

18+
/*
19+
* Construct a damon_region struct
20+
*
21+
* Returns the pointer to the new struct if success, or NULL otherwise
22+
*/
23+
struct damon_region *damon_new_region(unsigned long start, unsigned long end)
24+
{
25+
struct damon_region *region;
26+
27+
region = kmalloc(sizeof(*region), GFP_KERNEL);
28+
if (!region)
29+
return NULL;
30+
31+
region->ar.start = start;
32+
region->ar.end = end;
33+
region->nr_accesses = 0;
34+
INIT_LIST_HEAD(&region->list);
35+
36+
return region;
37+
}
38+
39+
/*
40+
* Add a region between two other regions
41+
*/
42+
inline void damon_insert_region(struct damon_region *r,
43+
struct damon_region *prev, struct damon_region *next)
44+
{
45+
__list_add(&r->list, &prev->list, &next->list);
46+
}
47+
48+
void damon_add_region(struct damon_region *r, struct damon_target *t)
49+
{
50+
list_add_tail(&r->list, &t->regions_list);
51+
}
52+
53+
static void damon_del_region(struct damon_region *r)
54+
{
55+
list_del(&r->list);
56+
}
57+
58+
static void damon_free_region(struct damon_region *r)
59+
{
60+
kfree(r);
61+
}
62+
63+
void damon_destroy_region(struct damon_region *r)
64+
{
65+
damon_del_region(r);
66+
damon_free_region(r);
67+
}
68+
69+
/*
70+
* Construct a damon_target struct
71+
*
72+
* Returns the pointer to the new struct if success, or NULL otherwise
73+
*/
74+
struct damon_target *damon_new_target(unsigned long id)
75+
{
76+
struct damon_target *t;
77+
78+
t = kmalloc(sizeof(*t), GFP_KERNEL);
79+
if (!t)
80+
return NULL;
81+
82+
t->id = id;
83+
INIT_LIST_HEAD(&t->regions_list);
84+
85+
return t;
86+
}
87+
88+
void damon_add_target(struct damon_ctx *ctx, struct damon_target *t)
89+
{
90+
list_add_tail(&t->list, &ctx->region_targets);
91+
}
92+
93+
static void damon_del_target(struct damon_target *t)
94+
{
95+
list_del(&t->list);
96+
}
97+
98+
void damon_free_target(struct damon_target *t)
99+
{
100+
struct damon_region *r, *next;
101+
102+
damon_for_each_region_safe(r, next, t)
103+
damon_free_region(r);
104+
kfree(t);
105+
}
106+
107+
void damon_destroy_target(struct damon_target *t)
108+
{
109+
damon_del_target(t);
110+
damon_free_target(t);
111+
}
112+
18113
struct damon_ctx *damon_new_ctx(void)
19114
{
20115
struct damon_ctx *ctx;
@@ -32,15 +127,27 @@ struct damon_ctx *damon_new_ctx(void)
32127

33128
mutex_init(&ctx->kdamond_lock);
34129

35-
ctx->target = NULL;
130+
INIT_LIST_HEAD(&ctx->region_targets);
36131

37132
return ctx;
38133
}
39134

40-
void damon_destroy_ctx(struct damon_ctx *ctx)
135+
static void damon_destroy_targets(struct damon_ctx *ctx)
41136
{
42-
if (ctx->primitive.cleanup)
137+
struct damon_target *t, *next_t;
138+
139+
if (ctx->primitive.cleanup) {
43140
ctx->primitive.cleanup(ctx);
141+
return;
142+
}
143+
144+
damon_for_each_target_safe(t, next_t, ctx)
145+
damon_destroy_target(t);
146+
}
147+
148+
void damon_destroy_ctx(struct damon_ctx *ctx)
149+
{
150+
damon_destroy_targets(ctx);
44151
kfree(ctx);
45152
}
46153

@@ -217,6 +324,21 @@ static bool kdamond_aggregate_interval_passed(struct damon_ctx *ctx)
217324
ctx->aggr_interval);
218325
}
219326

327+
/*
328+
* Reset the aggregated monitoring results ('nr_accesses' of each region).
329+
*/
330+
static void kdamond_reset_aggregated(struct damon_ctx *c)
331+
{
332+
struct damon_target *t;
333+
334+
damon_for_each_target(t, c) {
335+
struct damon_region *r;
336+
337+
damon_for_each_region(r, t)
338+
r->nr_accesses = 0;
339+
}
340+
}
341+
220342
/*
221343
* Check whether it is time to check and apply the target monitoring regions
222344
*
@@ -238,6 +360,7 @@ static bool kdamond_need_update_primitive(struct damon_ctx *ctx)
238360
*/
239361
static bool kdamond_need_stop(struct damon_ctx *ctx)
240362
{
363+
struct damon_target *t;
241364
bool stop;
242365

243366
mutex_lock(&ctx->kdamond_lock);
@@ -249,7 +372,12 @@ static bool kdamond_need_stop(struct damon_ctx *ctx)
249372
if (!ctx->primitive.target_valid)
250373
return false;
251374

252-
return !ctx->primitive.target_valid(ctx->target);
375+
damon_for_each_target(t, ctx) {
376+
if (ctx->primitive.target_valid(t))
377+
return false;
378+
}
379+
380+
return true;
253381
}
254382

255383
static void set_kdamond_stop(struct damon_ctx *ctx)
@@ -265,6 +393,8 @@ static void set_kdamond_stop(struct damon_ctx *ctx)
265393
static int kdamond_fn(void *data)
266394
{
267395
struct damon_ctx *ctx = (struct damon_ctx *)data;
396+
struct damon_target *t;
397+
struct damon_region *r, *next;
268398

269399
mutex_lock(&ctx->kdamond_lock);
270400
pr_info("kdamond (%d) starts\n", ctx->kdamond->pid);
@@ -291,6 +421,7 @@ static int kdamond_fn(void *data)
291421
if (ctx->callback.after_aggregation &&
292422
ctx->callback.after_aggregation(ctx))
293423
set_kdamond_stop(ctx);
424+
kdamond_reset_aggregated(ctx);
294425
if (ctx->primitive.reset_aggregated)
295426
ctx->primitive.reset_aggregated(ctx);
296427
}
@@ -300,6 +431,10 @@ static int kdamond_fn(void *data)
300431
ctx->primitive.update(ctx);
301432
}
302433
}
434+
damon_for_each_target(t, ctx) {
435+
damon_for_each_region_safe(r, next, t)
436+
damon_destroy_region(r);
437+
}
303438

304439
if (ctx->callback.before_terminate &&
305440
ctx->callback.before_terminate(ctx))

0 commit comments

Comments
 (0)