Loading...
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
13static struct thp_settings settings_stack[MAX_SETTINGS_DEPTH];
14static int settings_index;
15static struct thp_settings saved_settings;
16static char dev_queue_read_ahead_path[PATH_MAX];
17
18static const char * const thp_enabled_strings[] = {
19 "never",
20 "always",
21 "inherit",
22 "madvise",
23 NULL
24};
25
26static const char * const thp_defrag_strings[] = {
27 "always",
28 "defer",
29 "defer+madvise",
30 "madvise",
31 "never",
32 NULL
33};
34
35static const char * const shmem_enabled_strings[] = {
36 "always",
37 "within_size",
38 "advise",
39 "never",
40 "deny",
41 "force",
42 NULL
43};
44
45int 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
66int 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
89const 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
101void 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
112int 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
157void 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
174const 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
187void 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
200void 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
239void 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
276struct 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
285void 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
295void 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
305void thp_restore_settings(void)
306{
307 thp_write_settings(&saved_settings);
308}
309
310void thp_save_settings(void)
311{
312 thp_read_settings(&saved_settings);
313}
314
315void 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
327unsigned 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}