xref: /linux/samples/damon/mtier.c (revision cf85f810f911234a06a4ef2439e8694b93b717fc)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * memory tiering: migrate cold pages in node 0 and hot pages in node 1 to node
4  * 1 and node 0, respectively.  Adjust the hotness/coldness threshold aiming
5  * resulting 99.6 % node 0 utilization ratio.
6  */
7 
8 #define pr_fmt(fmt) "damon_sample_mtier: " fmt
9 
10 #include <linux/damon.h>
11 #include <linux/init.h>
12 #include <linux/kernel.h>
13 #include <linux/module.h>
14 
15 #ifdef MODULE_PARAM_PREFIX
16 #undef MODULE_PARAM_PREFIX
17 #endif
18 #define MODULE_PARAM_PREFIX "damon_sample_mtier."
19 
20 static unsigned long node0_start_addr __read_mostly;
21 module_param(node0_start_addr, ulong, 0600);
22 
23 static unsigned long node0_end_addr __read_mostly;
24 module_param(node0_end_addr, ulong, 0600);
25 
26 static unsigned long node1_start_addr __read_mostly;
27 module_param(node1_start_addr, ulong, 0600);
28 
29 static unsigned long node1_end_addr __read_mostly;
30 module_param(node1_end_addr, ulong, 0600);
31 
32 static unsigned long node0_mem_used_bp __read_mostly = 9970;
33 module_param(node0_mem_used_bp, ulong, 0600);
34 
35 static unsigned long node0_mem_free_bp __read_mostly = 50;
36 module_param(node0_mem_free_bp, ulong, 0600);
37 
38 static int damon_sample_mtier_enable_store(
39 		const char *val, const struct kernel_param *kp);
40 
41 static const struct kernel_param_ops enabled_param_ops = {
42 	.set = damon_sample_mtier_enable_store,
43 	.get = param_get_bool,
44 };
45 
46 static bool enabled __read_mostly;
47 module_param_cb(enabled, &enabled_param_ops, &enabled, 0600);
48 MODULE_PARM_DESC(enabled, "Enable or disable DAMON_SAMPLE_MTIER");
49 
50 static bool detect_node_addresses __read_mostly;
51 module_param(detect_node_addresses, bool, 0600);
52 
53 static struct damon_ctx *ctxs[2];
54 
55 struct region_range {
56 	phys_addr_t start;
57 	phys_addr_t end;
58 };
59 
60 static int nid_to_phys(int target_node, struct region_range *range)
61 {
62 	if (!node_online(target_node)) {
63 		pr_err("NUMA node %d is not online\n", target_node);
64 		return -EINVAL;
65 	}
66 
67 	range->start = PFN_PHYS(node_start_pfn(target_node));
68 	range->end  = PFN_PHYS(node_end_pfn(target_node));
69 
70 	return 0;
71 }
72 
73 static struct damon_ctx *damon_sample_mtier_build_ctx(bool promote)
74 {
75 	struct damon_ctx *ctx;
76 	struct damon_attrs attrs;
77 	struct damon_target *target;
78 	struct damos *scheme;
79 	struct damos_quota_goal *quota_goal;
80 	struct damos_filter *filter;
81 	struct region_range addr;
82 	struct damon_addr_range range;
83 	int ret;
84 
85 	ctx = damon_new_ctx();
86 	if (!ctx)
87 		return NULL;
88 	attrs = (struct damon_attrs) {
89 		.sample_interval = 5 * USEC_PER_MSEC,
90 		.aggr_interval = 100 * USEC_PER_MSEC,
91 		.ops_update_interval = 60 * USEC_PER_MSEC * MSEC_PER_SEC,
92 		.min_nr_regions = 10,
93 		.max_nr_regions = 1000,
94 	};
95 
96 	/*
97 	 * auto-tune sampling and aggregation interval aiming 4% DAMON-observed
98 	 * accesses ratio, keeping sampling interval in [5ms, 10s] range.
99 	 */
100 	attrs.intervals_goal = (struct damon_intervals_goal) {
101 		.access_bp = 400, .aggrs = 3,
102 		.min_sample_us = 5000, .max_sample_us = 10000000,
103 	};
104 	if (damon_set_attrs(ctx, &attrs))
105 		goto free_out;
106 	if (damon_select_ops(ctx, DAMON_OPS_PADDR))
107 		goto free_out;
108 
109 	target = damon_new_target();
110 	if (!target)
111 		goto free_out;
112 	damon_add_target(ctx, target);
113 
114 	if (detect_node_addresses) {
115 		ret = promote ? nid_to_phys(1, &addr) : nid_to_phys(0, &addr);
116 		if (ret)
117 			goto free_out;
118 	} else {
119 		addr.start = promote ? node1_start_addr : node0_start_addr;
120 		addr.end = promote ? node1_end_addr : node0_end_addr;
121 	}
122 
123 	if (addr.start >= addr.end)
124 		goto free_out;
125 
126 	range.start = addr.start;
127 	range.end = addr.end;
128 
129 	ret = damon_set_regions(target, &range, 1, DAMON_MIN_REGION_SZ);
130 	if (ret)
131 		goto free_out;
132 
133 	scheme = damon_new_scheme(
134 			/* access pattern */
135 			&(struct damos_access_pattern) {
136 				.min_sz_region = PAGE_SIZE,
137 				.max_sz_region = ULONG_MAX,
138 				.min_nr_accesses = promote ? 1 : 0,
139 				.max_nr_accesses = promote ? UINT_MAX : 0,
140 				.min_age_region = 0,
141 				.max_age_region = UINT_MAX},
142 			/* action */
143 			promote ? DAMOS_MIGRATE_HOT : DAMOS_MIGRATE_COLD,
144 			1000000,	/* apply interval (1s) */
145 			&(struct damos_quota){
146 				/* 200 MiB per sec by most */
147 				.reset_interval = 1000,
148 				.sz = 200 * 1024 * 1024,
149 				/* ignore size of region when prioritizing */
150 				.weight_sz = 0,
151 				.weight_nr_accesses = 100,
152 				.weight_age = 100,
153 			},
154 			&(struct damos_watermarks){},
155 			promote ? 0 : 1);	/* migrate target node id */
156 	if (!scheme)
157 		goto free_out;
158 	damon_set_schemes(ctx, &scheme, 1);
159 	/* zero target value causes division by zero in damos_quota_store() */
160 	if (!node0_mem_used_bp || !node0_mem_free_bp)
161 		goto free_out;
162 	quota_goal = damos_new_quota_goal(
163 			promote ? DAMOS_QUOTA_NODE_MEM_USED_BP :
164 			DAMOS_QUOTA_NODE_MEM_FREE_BP,
165 			promote ? node0_mem_used_bp : node0_mem_free_bp);
166 	if (!quota_goal)
167 		goto free_out;
168 	quota_goal->nid = 0;
169 	damos_add_quota_goal(&scheme->quota, quota_goal);
170 	filter = damos_new_filter(DAMOS_FILTER_TYPE_YOUNG, true, promote);
171 	if (!filter)
172 		goto free_out;
173 	damos_add_filter(scheme, filter);
174 	return ctx;
175 free_out:
176 	damon_destroy_ctx(ctx);
177 	return NULL;
178 }
179 
180 static int damon_sample_mtier_start(void)
181 {
182 	struct damon_ctx *ctx;
183 
184 	ctx = damon_sample_mtier_build_ctx(true);
185 	if (!ctx)
186 		return -ENOMEM;
187 	ctxs[0] = ctx;
188 	ctx = damon_sample_mtier_build_ctx(false);
189 	if (!ctx) {
190 		damon_destroy_ctx(ctxs[0]);
191 		return -ENOMEM;
192 	}
193 	ctxs[1] = ctx;
194 	return damon_start(ctxs, 2, true);
195 }
196 
197 static void damon_sample_mtier_stop(void)
198 {
199 	damon_stop(ctxs, 2);
200 	damon_destroy_ctx(ctxs[0]);
201 	damon_destroy_ctx(ctxs[1]);
202 }
203 
204 static int damon_sample_mtier_enable_store(
205 		const char *val, const struct kernel_param *kp)
206 {
207 	bool is_enabled = enabled;
208 	int err;
209 
210 	err = kstrtobool(val, &enabled);
211 	if (err)
212 		return err;
213 
214 	if (enabled == is_enabled)
215 		return 0;
216 
217 	if (!damon_initialized())
218 		return 0;
219 
220 	if (enabled) {
221 		err = damon_sample_mtier_start();
222 		if (err)
223 			enabled = false;
224 		return err;
225 	}
226 	damon_sample_mtier_stop();
227 	return 0;
228 }
229 
230 static int __init damon_sample_mtier_init(void)
231 {
232 	int err = 0;
233 
234 	if (!damon_initialized()) {
235 		if (enabled)
236 			enabled = false;
237 		return -ENOMEM;
238 	}
239 
240 	if (enabled) {
241 		err = damon_sample_mtier_start();
242 		if (err)
243 			enabled = false;
244 	}
245 	return 0;
246 }
247 
248 module_init(damon_sample_mtier_init);
249