xref: /linux/tools/testing/selftests/mm/thp_settings.c (revision eb01fe7abbe2d0b38824d2a93fdb4cc3eaf2ccc1)
1 // SPDX-License-Identifier: GPL-2.0
2 #include <fcntl.h>
3 #include <limits.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <unistd.h>
8 
9 #include "thp_settings.h"
10 
11 #define THP_SYSFS "/sys/kernel/mm/transparent_hugepage/"
12 #define MAX_SETTINGS_DEPTH 4
13 static struct thp_settings settings_stack[MAX_SETTINGS_DEPTH];
14 static int settings_index;
15 static struct thp_settings saved_settings;
16 static char dev_queue_read_ahead_path[PATH_MAX];
17 
18 static const char * const thp_enabled_strings[] = {
19 	"never",
20 	"always",
21 	"inherit",
22 	"madvise",
23 	NULL
24 };
25 
26 static const char * const thp_defrag_strings[] = {
27 	"always",
28 	"defer",
29 	"defer+madvise",
30 	"madvise",
31 	"never",
32 	NULL
33 };
34 
35 static const char * const shmem_enabled_strings[] = {
36 	"always",
37 	"within_size",
38 	"advise",
39 	"never",
40 	"deny",
41 	"force",
42 	NULL
43 };
44 
45 int read_file(const char *path, char *buf, size_t buflen)
46 {
47 	int fd;
48 	ssize_t numread;
49 
50 	fd = open(path, O_RDONLY);
51 	if (fd == -1)
52 		return 0;
53 
54 	numread = read(fd, buf, buflen - 1);
55 	if (numread < 1) {
56 		close(fd);
57 		return 0;
58 	}
59 
60 	buf[numread] = '\0';
61 	close(fd);
62 
63 	return (unsigned int) numread;
64 }
65 
66 int write_file(const char *path, const char *buf, size_t buflen)
67 {
68 	int fd;
69 	ssize_t numwritten;
70 
71 	fd = open(path, O_WRONLY);
72 	if (fd == -1) {
73 		printf("open(%s)\n", path);
74 		exit(EXIT_FAILURE);
75 		return 0;
76 	}
77 
78 	numwritten = write(fd, buf, buflen - 1);
79 	close(fd);
80 	if (numwritten < 1) {
81 		printf("write(%s)\n", buf);
82 		exit(EXIT_FAILURE);
83 		return 0;
84 	}
85 
86 	return (unsigned int) numwritten;
87 }
88 
89 const unsigned long read_num(const char *path)
90 {
91 	char buf[21];
92 
93 	if (read_file(path, buf, sizeof(buf)) < 0) {
94 		perror("read_file()");
95 		exit(EXIT_FAILURE);
96 	}
97 
98 	return strtoul(buf, NULL, 10);
99 }
100 
101 void write_num(const char *path, unsigned long num)
102 {
103 	char buf[21];
104 
105 	sprintf(buf, "%ld", num);
106 	if (!write_file(path, buf, strlen(buf) + 1)) {
107 		perror(path);
108 		exit(EXIT_FAILURE);
109 	}
110 }
111 
112 int thp_read_string(const char *name, const char * const strings[])
113 {
114 	char path[PATH_MAX];
115 	char buf[256];
116 	char *c;
117 	int ret;
118 
119 	ret = snprintf(path, PATH_MAX, THP_SYSFS "%s", name);
120 	if (ret >= PATH_MAX) {
121 		printf("%s: Pathname is too long\n", __func__);
122 		exit(EXIT_FAILURE);
123 	}
124 
125 	if (!read_file(path, buf, sizeof(buf))) {
126 		perror(path);
127 		exit(EXIT_FAILURE);
128 	}
129 
130 	c = strchr(buf, '[');
131 	if (!c) {
132 		printf("%s: Parse failure\n", __func__);
133 		exit(EXIT_FAILURE);
134 	}
135 
136 	c++;
137 	memmove(buf, c, sizeof(buf) - (c - buf));
138 
139 	c = strchr(buf, ']');
140 	if (!c) {
141 		printf("%s: Parse failure\n", __func__);
142 		exit(EXIT_FAILURE);
143 	}
144 	*c = '\0';
145 
146 	ret = 0;
147 	while (strings[ret]) {
148 		if (!strcmp(strings[ret], buf))
149 			return ret;
150 		ret++;
151 	}
152 
153 	printf("Failed to parse %s\n", name);
154 	exit(EXIT_FAILURE);
155 }
156 
157 void thp_write_string(const char *name, const char *val)
158 {
159 	char path[PATH_MAX];
160 	int ret;
161 
162 	ret = snprintf(path, PATH_MAX, THP_SYSFS "%s", name);
163 	if (ret >= PATH_MAX) {
164 		printf("%s: Pathname is too long\n", __func__);
165 		exit(EXIT_FAILURE);
166 	}
167 
168 	if (!write_file(path, val, strlen(val) + 1)) {
169 		perror(path);
170 		exit(EXIT_FAILURE);
171 	}
172 }
173 
174 const unsigned long thp_read_num(const char *name)
175 {
176 	char path[PATH_MAX];
177 	int ret;
178 
179 	ret = snprintf(path, PATH_MAX, THP_SYSFS "%s", name);
180 	if (ret >= PATH_MAX) {
181 		printf("%s: Pathname is too long\n", __func__);
182 		exit(EXIT_FAILURE);
183 	}
184 	return read_num(path);
185 }
186 
187 void thp_write_num(const char *name, unsigned long num)
188 {
189 	char path[PATH_MAX];
190 	int ret;
191 
192 	ret = snprintf(path, PATH_MAX, THP_SYSFS "%s", name);
193 	if (ret >= PATH_MAX) {
194 		printf("%s: Pathname is too long\n", __func__);
195 		exit(EXIT_FAILURE);
196 	}
197 	write_num(path, num);
198 }
199 
200 void thp_read_settings(struct thp_settings *settings)
201 {
202 	unsigned long orders = thp_supported_orders();
203 	char path[PATH_MAX];
204 	int i;
205 
206 	*settings = (struct thp_settings) {
207 		.thp_enabled = thp_read_string("enabled", thp_enabled_strings),
208 		.thp_defrag = thp_read_string("defrag", thp_defrag_strings),
209 		.shmem_enabled =
210 			thp_read_string("shmem_enabled", shmem_enabled_strings),
211 		.use_zero_page = thp_read_num("use_zero_page"),
212 	};
213 	settings->khugepaged = (struct khugepaged_settings) {
214 		.defrag = thp_read_num("khugepaged/defrag"),
215 		.alloc_sleep_millisecs =
216 			thp_read_num("khugepaged/alloc_sleep_millisecs"),
217 		.scan_sleep_millisecs =
218 			thp_read_num("khugepaged/scan_sleep_millisecs"),
219 		.max_ptes_none = thp_read_num("khugepaged/max_ptes_none"),
220 		.max_ptes_swap = thp_read_num("khugepaged/max_ptes_swap"),
221 		.max_ptes_shared = thp_read_num("khugepaged/max_ptes_shared"),
222 		.pages_to_scan = thp_read_num("khugepaged/pages_to_scan"),
223 	};
224 	if (dev_queue_read_ahead_path[0])
225 		settings->read_ahead_kb = read_num(dev_queue_read_ahead_path);
226 
227 	for (i = 0; i < NR_ORDERS; i++) {
228 		if (!((1 << i) & orders)) {
229 			settings->hugepages[i].enabled = THP_NEVER;
230 			continue;
231 		}
232 		snprintf(path, PATH_MAX, "hugepages-%ukB/enabled",
233 			(getpagesize() >> 10) << i);
234 		settings->hugepages[i].enabled =
235 			thp_read_string(path, thp_enabled_strings);
236 	}
237 }
238 
239 void thp_write_settings(struct thp_settings *settings)
240 {
241 	struct khugepaged_settings *khugepaged = &settings->khugepaged;
242 	unsigned long orders = thp_supported_orders();
243 	char path[PATH_MAX];
244 	int enabled;
245 	int i;
246 
247 	thp_write_string("enabled", thp_enabled_strings[settings->thp_enabled]);
248 	thp_write_string("defrag", thp_defrag_strings[settings->thp_defrag]);
249 	thp_write_string("shmem_enabled",
250 			shmem_enabled_strings[settings->shmem_enabled]);
251 	thp_write_num("use_zero_page", settings->use_zero_page);
252 
253 	thp_write_num("khugepaged/defrag", khugepaged->defrag);
254 	thp_write_num("khugepaged/alloc_sleep_millisecs",
255 			khugepaged->alloc_sleep_millisecs);
256 	thp_write_num("khugepaged/scan_sleep_millisecs",
257 			khugepaged->scan_sleep_millisecs);
258 	thp_write_num("khugepaged/max_ptes_none", khugepaged->max_ptes_none);
259 	thp_write_num("khugepaged/max_ptes_swap", khugepaged->max_ptes_swap);
260 	thp_write_num("khugepaged/max_ptes_shared", khugepaged->max_ptes_shared);
261 	thp_write_num("khugepaged/pages_to_scan", khugepaged->pages_to_scan);
262 
263 	if (dev_queue_read_ahead_path[0])
264 		write_num(dev_queue_read_ahead_path, settings->read_ahead_kb);
265 
266 	for (i = 0; i < NR_ORDERS; i++) {
267 		if (!((1 << i) & orders))
268 			continue;
269 		snprintf(path, PATH_MAX, "hugepages-%ukB/enabled",
270 			(getpagesize() >> 10) << i);
271 		enabled = settings->hugepages[i].enabled;
272 		thp_write_string(path, thp_enabled_strings[enabled]);
273 	}
274 }
275 
276 struct thp_settings *thp_current_settings(void)
277 {
278 	if (!settings_index) {
279 		printf("Fail: No settings set");
280 		exit(EXIT_FAILURE);
281 	}
282 	return settings_stack + settings_index - 1;
283 }
284 
285 void thp_push_settings(struct thp_settings *settings)
286 {
287 	if (settings_index >= MAX_SETTINGS_DEPTH) {
288 		printf("Fail: Settings stack exceeded");
289 		exit(EXIT_FAILURE);
290 	}
291 	settings_stack[settings_index++] = *settings;
292 	thp_write_settings(thp_current_settings());
293 }
294 
295 void thp_pop_settings(void)
296 {
297 	if (settings_index <= 0) {
298 		printf("Fail: Settings stack empty");
299 		exit(EXIT_FAILURE);
300 	}
301 	--settings_index;
302 	thp_write_settings(thp_current_settings());
303 }
304 
305 void thp_restore_settings(void)
306 {
307 	thp_write_settings(&saved_settings);
308 }
309 
310 void thp_save_settings(void)
311 {
312 	thp_read_settings(&saved_settings);
313 }
314 
315 void thp_set_read_ahead_path(char *path)
316 {
317 	if (!path) {
318 		dev_queue_read_ahead_path[0] = '\0';
319 		return;
320 	}
321 
322 	strncpy(dev_queue_read_ahead_path, path,
323 		sizeof(dev_queue_read_ahead_path));
324 	dev_queue_read_ahead_path[sizeof(dev_queue_read_ahead_path) - 1] = '\0';
325 }
326 
327 unsigned long thp_supported_orders(void)
328 {
329 	unsigned long orders = 0;
330 	char path[PATH_MAX];
331 	char buf[256];
332 	int ret;
333 	int i;
334 
335 	for (i = 0; i < NR_ORDERS; i++) {
336 		ret = snprintf(path, PATH_MAX, THP_SYSFS "hugepages-%ukB/enabled",
337 			(getpagesize() >> 10) << i);
338 		if (ret >= PATH_MAX) {
339 			printf("%s: Pathname is too long\n", __func__);
340 			exit(EXIT_FAILURE);
341 		}
342 
343 		ret = read_file(path, buf, sizeof(buf));
344 		if (ret)
345 			orders |= 1UL << i;
346 	}
347 
348 	return orders;
349 }
350