xref: /linux/mm/damon/stat.c (revision fabdd1e911da43cddbf17acf930171ba4b944477)
1369c415eSSeongJae Park // SPDX-License-Identifier: GPL-2.0
2369c415eSSeongJae Park /*
3369c415eSSeongJae Park  * Shows data access monitoring resutls in simple metrics.
4369c415eSSeongJae Park  */
5369c415eSSeongJae Park 
6369c415eSSeongJae Park #define pr_fmt(fmt) "damon-stat: " fmt
7369c415eSSeongJae Park 
8369c415eSSeongJae Park #include <linux/damon.h>
9369c415eSSeongJae Park #include <linux/init.h>
10369c415eSSeongJae Park #include <linux/kernel.h>
11369c415eSSeongJae Park #include <linux/module.h>
12369c415eSSeongJae Park #include <linux/sort.h>
13369c415eSSeongJae Park 
14369c415eSSeongJae Park #ifdef MODULE_PARAM_PREFIX
15369c415eSSeongJae Park #undef MODULE_PARAM_PREFIX
16369c415eSSeongJae Park #endif
17369c415eSSeongJae Park #define MODULE_PARAM_PREFIX "damon_stat."
18369c415eSSeongJae Park 
19369c415eSSeongJae Park static int damon_stat_enabled_store(
20369c415eSSeongJae Park 		const char *val, const struct kernel_param *kp);
21369c415eSSeongJae Park 
22369c415eSSeongJae Park static const struct kernel_param_ops enabled_param_ops = {
23369c415eSSeongJae Park 	.set = damon_stat_enabled_store,
24369c415eSSeongJae Park 	.get = param_get_bool,
25369c415eSSeongJae Park };
26369c415eSSeongJae Park 
27369c415eSSeongJae Park static bool enabled __read_mostly = IS_ENABLED(
28369c415eSSeongJae Park 	CONFIG_DAMON_STAT_ENABLED_DEFAULT);
29369c415eSSeongJae Park module_param_cb(enabled, &enabled_param_ops, &enabled, 0600);
30369c415eSSeongJae Park MODULE_PARM_DESC(enabled, "Enable of disable DAMON_STAT");
31369c415eSSeongJae Park 
32*fabdd1e9SSeongJae Park static unsigned long estimated_memory_bandwidth __read_mostly;
33*fabdd1e9SSeongJae Park module_param(estimated_memory_bandwidth, ulong, 0400);
34*fabdd1e9SSeongJae Park MODULE_PARM_DESC(estimated_memory_bandwidth,
35*fabdd1e9SSeongJae Park 		"Estimated memory bandwidth usage in bytes per second");
36*fabdd1e9SSeongJae Park 
37369c415eSSeongJae Park static struct damon_ctx *damon_stat_context;
38369c415eSSeongJae Park 
39*fabdd1e9SSeongJae Park static void damon_stat_set_estimated_memory_bandwidth(struct damon_ctx *c)
40*fabdd1e9SSeongJae Park {
41*fabdd1e9SSeongJae Park 	struct damon_target *t;
42*fabdd1e9SSeongJae Park 	struct damon_region *r;
43*fabdd1e9SSeongJae Park 	unsigned long access_bytes = 0;
44*fabdd1e9SSeongJae Park 
45*fabdd1e9SSeongJae Park 	damon_for_each_target(t, c) {
46*fabdd1e9SSeongJae Park 		damon_for_each_region(r, t)
47*fabdd1e9SSeongJae Park 			access_bytes += (r->ar.end - r->ar.start) *
48*fabdd1e9SSeongJae Park 				r->nr_accesses;
49*fabdd1e9SSeongJae Park 	}
50*fabdd1e9SSeongJae Park 	estimated_memory_bandwidth = access_bytes * USEC_PER_MSEC *
51*fabdd1e9SSeongJae Park 		MSEC_PER_SEC / c->attrs.aggr_interval;
52*fabdd1e9SSeongJae Park }
53*fabdd1e9SSeongJae Park 
54*fabdd1e9SSeongJae Park static int damon_stat_after_aggregation(struct damon_ctx *c)
55*fabdd1e9SSeongJae Park {
56*fabdd1e9SSeongJae Park 	static unsigned long last_refresh_jiffies;
57*fabdd1e9SSeongJae Park 
58*fabdd1e9SSeongJae Park 	/* avoid unnecessarily frequent stat update */
59*fabdd1e9SSeongJae Park 	if (time_before_eq(jiffies, last_refresh_jiffies +
60*fabdd1e9SSeongJae Park 				msecs_to_jiffies(5 * MSEC_PER_SEC)))
61*fabdd1e9SSeongJae Park 		return 0;
62*fabdd1e9SSeongJae Park 	last_refresh_jiffies = jiffies;
63*fabdd1e9SSeongJae Park 
64*fabdd1e9SSeongJae Park 	damon_stat_set_estimated_memory_bandwidth(c);
65*fabdd1e9SSeongJae Park 	return 0;
66*fabdd1e9SSeongJae Park }
67*fabdd1e9SSeongJae Park 
68369c415eSSeongJae Park static struct damon_ctx *damon_stat_build_ctx(void)
69369c415eSSeongJae Park {
70369c415eSSeongJae Park 	struct damon_ctx *ctx;
71369c415eSSeongJae Park 	struct damon_attrs attrs;
72369c415eSSeongJae Park 	struct damon_target *target;
73369c415eSSeongJae Park 	unsigned long start = 0, end = 0;
74369c415eSSeongJae Park 
75369c415eSSeongJae Park 	ctx = damon_new_ctx();
76369c415eSSeongJae Park 	if (!ctx)
77369c415eSSeongJae Park 		return NULL;
78369c415eSSeongJae Park 	attrs = (struct damon_attrs) {
79369c415eSSeongJae Park 		.sample_interval = 5 * USEC_PER_MSEC,
80369c415eSSeongJae Park 		.aggr_interval = 100 * USEC_PER_MSEC,
81369c415eSSeongJae Park 		.ops_update_interval = 60 * USEC_PER_MSEC * MSEC_PER_SEC,
82369c415eSSeongJae Park 		.min_nr_regions = 10,
83369c415eSSeongJae Park 		.max_nr_regions = 1000,
84369c415eSSeongJae Park 	};
85369c415eSSeongJae Park 	/*
86369c415eSSeongJae Park 	 * auto-tune sampling and aggregation interval aiming 4% DAMON-observed
87369c415eSSeongJae Park 	 * accesses ratio, keeping sampling interval in [5ms, 10s] range.
88369c415eSSeongJae Park 	 */
89369c415eSSeongJae Park 	attrs.intervals_goal = (struct damon_intervals_goal) {
90369c415eSSeongJae Park 		.access_bp = 400, .aggrs = 3,
91369c415eSSeongJae Park 		.min_sample_us = 5000, .max_sample_us = 10000000,
92369c415eSSeongJae Park 	};
93369c415eSSeongJae Park 	if (damon_set_attrs(ctx, &attrs))
94369c415eSSeongJae Park 		goto free_out;
95369c415eSSeongJae Park 
96369c415eSSeongJae Park 	/*
97369c415eSSeongJae Park 	 * auto-tune sampling and aggregation interval aiming 4% DAMON-observed
98369c415eSSeongJae Park 	 * accesses ratio, keeping sampling interval in [5ms, 10s] range.
99369c415eSSeongJae Park 	 */
100369c415eSSeongJae Park 	ctx->attrs.intervals_goal = (struct damon_intervals_goal) {
101369c415eSSeongJae Park 		.access_bp = 400, .aggrs = 3,
102369c415eSSeongJae Park 		.min_sample_us = 5000, .max_sample_us = 10000000,
103369c415eSSeongJae Park 	};
104369c415eSSeongJae Park 	if (damon_select_ops(ctx, DAMON_OPS_PADDR))
105369c415eSSeongJae Park 		goto free_out;
106369c415eSSeongJae Park 
107369c415eSSeongJae Park 	target = damon_new_target();
108369c415eSSeongJae Park 	if (!target)
109369c415eSSeongJae Park 		goto free_out;
110369c415eSSeongJae Park 	damon_add_target(ctx, target);
111369c415eSSeongJae Park 	if (damon_set_region_biggest_system_ram_default(target, &start, &end))
112369c415eSSeongJae Park 		goto free_out;
113*fabdd1e9SSeongJae Park 	ctx->callback.after_aggregation = damon_stat_after_aggregation;
114369c415eSSeongJae Park 	return ctx;
115369c415eSSeongJae Park free_out:
116369c415eSSeongJae Park 	damon_destroy_ctx(ctx);
117369c415eSSeongJae Park 	return NULL;
118369c415eSSeongJae Park }
119369c415eSSeongJae Park 
120369c415eSSeongJae Park static int damon_stat_start(void)
121369c415eSSeongJae Park {
122369c415eSSeongJae Park 	damon_stat_context = damon_stat_build_ctx();
123369c415eSSeongJae Park 	if (!damon_stat_context)
124369c415eSSeongJae Park 		return -ENOMEM;
125369c415eSSeongJae Park 	return damon_start(&damon_stat_context, 1, true);
126369c415eSSeongJae Park }
127369c415eSSeongJae Park 
128369c415eSSeongJae Park static void damon_stat_stop(void)
129369c415eSSeongJae Park {
130369c415eSSeongJae Park 	damon_stop(&damon_stat_context, 1);
131369c415eSSeongJae Park 	damon_destroy_ctx(damon_stat_context);
132369c415eSSeongJae Park }
133369c415eSSeongJae Park 
134369c415eSSeongJae Park static bool damon_stat_init_called;
135369c415eSSeongJae Park 
136369c415eSSeongJae Park static int damon_stat_enabled_store(
137369c415eSSeongJae Park 		const char *val, const struct kernel_param *kp)
138369c415eSSeongJae Park {
139369c415eSSeongJae Park 	bool is_enabled = enabled;
140369c415eSSeongJae Park 	int err;
141369c415eSSeongJae Park 
142369c415eSSeongJae Park 	err = kstrtobool(val, &enabled);
143369c415eSSeongJae Park 	if (err)
144369c415eSSeongJae Park 		return err;
145369c415eSSeongJae Park 
146369c415eSSeongJae Park 	if (is_enabled == enabled)
147369c415eSSeongJae Park 		return 0;
148369c415eSSeongJae Park 
149369c415eSSeongJae Park 	if (!damon_stat_init_called)
150369c415eSSeongJae Park 		/*
151369c415eSSeongJae Park 		 * probably called from command line parsing (parse_args()).
152369c415eSSeongJae Park 		 * Cannot call damon_new_ctx().  Let damon_stat_init() handle.
153369c415eSSeongJae Park 		 */
154369c415eSSeongJae Park 		return 0;
155369c415eSSeongJae Park 
156369c415eSSeongJae Park 	if (enabled) {
157369c415eSSeongJae Park 		err = damon_stat_start();
158369c415eSSeongJae Park 		if (err)
159369c415eSSeongJae Park 			enabled = false;
160369c415eSSeongJae Park 		return err;
161369c415eSSeongJae Park 	}
162369c415eSSeongJae Park 	damon_stat_stop();
163369c415eSSeongJae Park 	return 0;
164369c415eSSeongJae Park }
165369c415eSSeongJae Park 
166369c415eSSeongJae Park static int __init damon_stat_init(void)
167369c415eSSeongJae Park {
168369c415eSSeongJae Park 	int err = 0;
169369c415eSSeongJae Park 
170369c415eSSeongJae Park 	damon_stat_init_called = true;
171369c415eSSeongJae Park 
172369c415eSSeongJae Park 	/* probably set via command line */
173369c415eSSeongJae Park 	if (enabled)
174369c415eSSeongJae Park 		err = damon_stat_start();
175369c415eSSeongJae Park 
176369c415eSSeongJae Park 	if (err && enabled)
177369c415eSSeongJae Park 		enabled = false;
178369c415eSSeongJae Park 	return err;
179369c415eSSeongJae Park }
180369c415eSSeongJae Park 
181369c415eSSeongJae Park module_init(damon_stat_init);
182