Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/mm/damon/stat.c
29266 views
1
// SPDX-License-Identifier: GPL-2.0
2
/*
3
* Shows data access monitoring resutls in simple metrics.
4
*/
5
6
#define pr_fmt(fmt) "damon-stat: " fmt
7
8
#include <linux/damon.h>
9
#include <linux/init.h>
10
#include <linux/kernel.h>
11
#include <linux/module.h>
12
#include <linux/sort.h>
13
14
#ifdef MODULE_PARAM_PREFIX
15
#undef MODULE_PARAM_PREFIX
16
#endif
17
#define MODULE_PARAM_PREFIX "damon_stat."
18
19
static int damon_stat_enabled_store(
20
const char *val, const struct kernel_param *kp);
21
22
static const struct kernel_param_ops enabled_param_ops = {
23
.set = damon_stat_enabled_store,
24
.get = param_get_bool,
25
};
26
27
static bool enabled __read_mostly = IS_ENABLED(
28
CONFIG_DAMON_STAT_ENABLED_DEFAULT);
29
module_param_cb(enabled, &enabled_param_ops, &enabled, 0600);
30
MODULE_PARM_DESC(enabled, "Enable of disable DAMON_STAT");
31
32
static unsigned long estimated_memory_bandwidth __read_mostly;
33
module_param(estimated_memory_bandwidth, ulong, 0400);
34
MODULE_PARM_DESC(estimated_memory_bandwidth,
35
"Estimated memory bandwidth usage in bytes per second");
36
37
static long memory_idle_ms_percentiles[101] __read_mostly = {0,};
38
module_param_array(memory_idle_ms_percentiles, long, NULL, 0400);
39
MODULE_PARM_DESC(memory_idle_ms_percentiles,
40
"Memory idle time percentiles in milliseconds");
41
42
static unsigned long aggr_interval_us;
43
module_param(aggr_interval_us, ulong, 0400);
44
MODULE_PARM_DESC(aggr_interval_us,
45
"Current tuned aggregation interval in microseconds");
46
47
static struct damon_ctx *damon_stat_context;
48
49
static void damon_stat_set_estimated_memory_bandwidth(struct damon_ctx *c)
50
{
51
struct damon_target *t;
52
struct damon_region *r;
53
unsigned long access_bytes = 0;
54
55
damon_for_each_target(t, c) {
56
damon_for_each_region(r, t)
57
access_bytes += (r->ar.end - r->ar.start) *
58
r->nr_accesses;
59
}
60
estimated_memory_bandwidth = access_bytes * USEC_PER_MSEC *
61
MSEC_PER_SEC / c->attrs.aggr_interval;
62
}
63
64
static int damon_stat_idletime(const struct damon_region *r)
65
{
66
if (r->nr_accesses)
67
return -1 * (r->age + 1);
68
return r->age + 1;
69
}
70
71
static int damon_stat_cmp_regions(const void *a, const void *b)
72
{
73
const struct damon_region *ra = *(const struct damon_region **)a;
74
const struct damon_region *rb = *(const struct damon_region **)b;
75
76
return damon_stat_idletime(ra) - damon_stat_idletime(rb);
77
}
78
79
static int damon_stat_sort_regions(struct damon_ctx *c,
80
struct damon_region ***sorted_ptr, int *nr_regions_ptr,
81
unsigned long *total_sz_ptr)
82
{
83
struct damon_target *t;
84
struct damon_region *r;
85
struct damon_region **region_pointers;
86
unsigned int nr_regions = 0;
87
unsigned long total_sz = 0;
88
89
damon_for_each_target(t, c) {
90
/* there is only one target */
91
region_pointers = kmalloc_array(damon_nr_regions(t),
92
sizeof(*region_pointers), GFP_KERNEL);
93
if (!region_pointers)
94
return -ENOMEM;
95
damon_for_each_region(r, t) {
96
region_pointers[nr_regions++] = r;
97
total_sz += r->ar.end - r->ar.start;
98
}
99
}
100
sort(region_pointers, nr_regions, sizeof(*region_pointers),
101
damon_stat_cmp_regions, NULL);
102
*sorted_ptr = region_pointers;
103
*nr_regions_ptr = nr_regions;
104
*total_sz_ptr = total_sz;
105
return 0;
106
}
107
108
static void damon_stat_set_idletime_percentiles(struct damon_ctx *c)
109
{
110
struct damon_region **sorted_regions, *region;
111
int nr_regions;
112
unsigned long total_sz, accounted_bytes = 0;
113
int err, i, next_percentile = 0;
114
115
err = damon_stat_sort_regions(c, &sorted_regions, &nr_regions,
116
&total_sz);
117
if (err)
118
return;
119
for (i = 0; i < nr_regions; i++) {
120
region = sorted_regions[i];
121
accounted_bytes += region->ar.end - region->ar.start;
122
while (next_percentile <= accounted_bytes * 100 / total_sz)
123
memory_idle_ms_percentiles[next_percentile++] =
124
damon_stat_idletime(region) *
125
(long)c->attrs.aggr_interval / USEC_PER_MSEC;
126
}
127
kfree(sorted_regions);
128
}
129
130
static int damon_stat_damon_call_fn(void *data)
131
{
132
struct damon_ctx *c = data;
133
static unsigned long last_refresh_jiffies;
134
135
/* avoid unnecessarily frequent stat update */
136
if (time_before_eq(jiffies, last_refresh_jiffies +
137
msecs_to_jiffies(5 * MSEC_PER_SEC)))
138
return 0;
139
last_refresh_jiffies = jiffies;
140
141
aggr_interval_us = c->attrs.aggr_interval;
142
damon_stat_set_estimated_memory_bandwidth(c);
143
damon_stat_set_idletime_percentiles(c);
144
return 0;
145
}
146
147
static struct damon_ctx *damon_stat_build_ctx(void)
148
{
149
struct damon_ctx *ctx;
150
struct damon_attrs attrs;
151
struct damon_target *target;
152
unsigned long start = 0, end = 0;
153
154
ctx = damon_new_ctx();
155
if (!ctx)
156
return NULL;
157
attrs = (struct damon_attrs) {
158
.sample_interval = 5 * USEC_PER_MSEC,
159
.aggr_interval = 100 * USEC_PER_MSEC,
160
.ops_update_interval = 60 * USEC_PER_MSEC * MSEC_PER_SEC,
161
.min_nr_regions = 10,
162
.max_nr_regions = 1000,
163
};
164
/*
165
* auto-tune sampling and aggregation interval aiming 4% DAMON-observed
166
* accesses ratio, keeping sampling interval in [5ms, 10s] range.
167
*/
168
attrs.intervals_goal = (struct damon_intervals_goal) {
169
.access_bp = 400, .aggrs = 3,
170
.min_sample_us = 5000, .max_sample_us = 10000000,
171
};
172
if (damon_set_attrs(ctx, &attrs))
173
goto free_out;
174
175
/*
176
* auto-tune sampling and aggregation interval aiming 4% DAMON-observed
177
* accesses ratio, keeping sampling interval in [5ms, 10s] range.
178
*/
179
ctx->attrs.intervals_goal = (struct damon_intervals_goal) {
180
.access_bp = 400, .aggrs = 3,
181
.min_sample_us = 5000, .max_sample_us = 10000000,
182
};
183
if (damon_select_ops(ctx, DAMON_OPS_PADDR))
184
goto free_out;
185
186
target = damon_new_target();
187
if (!target)
188
goto free_out;
189
damon_add_target(ctx, target);
190
if (damon_set_region_biggest_system_ram_default(target, &start, &end))
191
goto free_out;
192
return ctx;
193
free_out:
194
damon_destroy_ctx(ctx);
195
return NULL;
196
}
197
198
static struct damon_call_control call_control = {
199
.fn = damon_stat_damon_call_fn,
200
.repeat = true,
201
};
202
203
static int damon_stat_start(void)
204
{
205
int err;
206
207
damon_stat_context = damon_stat_build_ctx();
208
if (!damon_stat_context)
209
return -ENOMEM;
210
err = damon_start(&damon_stat_context, 1, true);
211
if (err)
212
return err;
213
call_control.data = damon_stat_context;
214
return damon_call(damon_stat_context, &call_control);
215
}
216
217
static void damon_stat_stop(void)
218
{
219
damon_stop(&damon_stat_context, 1);
220
damon_destroy_ctx(damon_stat_context);
221
}
222
223
static int damon_stat_enabled_store(
224
const char *val, const struct kernel_param *kp)
225
{
226
bool is_enabled = enabled;
227
int err;
228
229
err = kstrtobool(val, &enabled);
230
if (err)
231
return err;
232
233
if (is_enabled == enabled)
234
return 0;
235
236
if (!damon_initialized())
237
/*
238
* probably called from command line parsing (parse_args()).
239
* Cannot call damon_new_ctx(). Let damon_stat_init() handle.
240
*/
241
return 0;
242
243
if (enabled) {
244
err = damon_stat_start();
245
if (err)
246
enabled = false;
247
return err;
248
}
249
damon_stat_stop();
250
return 0;
251
}
252
253
static int __init damon_stat_init(void)
254
{
255
int err = 0;
256
257
if (!damon_initialized()) {
258
err = -ENOMEM;
259
goto out;
260
}
261
262
/* probably set via command line */
263
if (enabled)
264
err = damon_stat_start();
265
266
out:
267
if (err && enabled)
268
enabled = false;
269
return err;
270
}
271
272
module_init(damon_stat_init);
273
274