1 // SPDX-License-Identifier: GPL-2.0
9 #include "thp_settings.h"
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
];
18 static const char * const thp_enabled_strings
[] = {
26 static const char * const thp_defrag_strings
[] = {
35 static const char * const shmem_enabled_strings
[] = {
46 int read_file(const char *path
, char *buf
, size_t buflen
)
51 fd
= open(path
, O_RDONLY
);
55 numread
= read(fd
, buf
, buflen
- 1);
64 return (unsigned int) numread
;
67 int write_file(const char *path
, const char *buf
, size_t buflen
)
72 fd
= open(path
, O_WRONLY
);
74 printf("open(%s)\n", path
);
79 numwritten
= write(fd
, buf
, buflen
- 1);
82 printf("write(%s)\n", buf
);
87 return (unsigned int) numwritten
;
90 unsigned long read_num(const char *path
)
94 if (read_file(path
, buf
, sizeof(buf
)) < 0) {
95 perror("read_file()");
99 return strtoul(buf
, NULL
, 10);
102 void write_num(const char *path
, unsigned long num
)
106 sprintf(buf
, "%ld", num
);
107 if (!write_file(path
, buf
, strlen(buf
) + 1)) {
113 int thp_read_string(const char *name
, const char * const strings
[])
120 ret
= snprintf(path
, PATH_MAX
, THP_SYSFS
"%s", name
);
121 if (ret
>= PATH_MAX
) {
122 printf("%s: Pathname is too long\n", __func__
);
126 if (!read_file(path
, buf
, sizeof(buf
))) {
131 c
= strchr(buf
, '[');
133 printf("%s: Parse failure\n", __func__
);
138 memmove(buf
, c
, sizeof(buf
) - (c
- buf
));
140 c
= strchr(buf
, ']');
142 printf("%s: Parse failure\n", __func__
);
148 while (strings
[ret
]) {
149 if (!strcmp(strings
[ret
], buf
))
154 printf("Failed to parse %s\n", name
);
158 void thp_write_string(const char *name
, const char *val
)
163 ret
= snprintf(path
, PATH_MAX
, THP_SYSFS
"%s", name
);
164 if (ret
>= PATH_MAX
) {
165 printf("%s: Pathname is too long\n", __func__
);
169 if (!write_file(path
, val
, strlen(val
) + 1)) {
175 unsigned long thp_read_num(const char *name
)
180 ret
= snprintf(path
, PATH_MAX
, THP_SYSFS
"%s", name
);
181 if (ret
>= PATH_MAX
) {
182 printf("%s: Pathname is too long\n", __func__
);
185 return read_num(path
);
188 void thp_write_num(const char *name
, unsigned long num
)
193 ret
= snprintf(path
, PATH_MAX
, THP_SYSFS
"%s", name
);
194 if (ret
>= PATH_MAX
) {
195 printf("%s: Pathname is too long\n", __func__
);
198 write_num(path
, num
);
201 void thp_read_settings(struct thp_settings
*settings
)
203 unsigned long orders
= thp_supported_orders();
204 unsigned long shmem_orders
= thp_shmem_supported_orders();
208 *settings
= (struct thp_settings
) {
209 .thp_enabled
= thp_read_string("enabled", thp_enabled_strings
),
210 .thp_defrag
= thp_read_string("defrag", thp_defrag_strings
),
212 thp_read_string("shmem_enabled", shmem_enabled_strings
),
213 .use_zero_page
= thp_read_num("use_zero_page"),
215 settings
->khugepaged
= (struct khugepaged_settings
) {
216 .defrag
= thp_read_num("khugepaged/defrag"),
217 .alloc_sleep_millisecs
=
218 thp_read_num("khugepaged/alloc_sleep_millisecs"),
219 .scan_sleep_millisecs
=
220 thp_read_num("khugepaged/scan_sleep_millisecs"),
221 .max_ptes_none
= thp_read_num("khugepaged/max_ptes_none"),
222 .max_ptes_swap
= thp_read_num("khugepaged/max_ptes_swap"),
223 .max_ptes_shared
= thp_read_num("khugepaged/max_ptes_shared"),
224 .pages_to_scan
= thp_read_num("khugepaged/pages_to_scan"),
226 if (dev_queue_read_ahead_path
[0])
227 settings
->read_ahead_kb
= read_num(dev_queue_read_ahead_path
);
229 for (i
= 0; i
< NR_ORDERS
; i
++) {
230 if (!((1 << i
) & orders
)) {
231 settings
->hugepages
[i
].enabled
= THP_NEVER
;
234 snprintf(path
, PATH_MAX
, "hugepages-%ukB/enabled",
235 (getpagesize() >> 10) << i
);
236 settings
->hugepages
[i
].enabled
=
237 thp_read_string(path
, thp_enabled_strings
);
240 for (i
= 0; i
< NR_ORDERS
; i
++) {
241 if (!((1 << i
) & shmem_orders
)) {
242 settings
->shmem_hugepages
[i
].enabled
= SHMEM_NEVER
;
245 snprintf(path
, PATH_MAX
, "hugepages-%ukB/shmem_enabled",
246 (getpagesize() >> 10) << i
);
247 settings
->shmem_hugepages
[i
].enabled
=
248 thp_read_string(path
, shmem_enabled_strings
);
252 void thp_write_settings(struct thp_settings
*settings
)
254 struct khugepaged_settings
*khugepaged
= &settings
->khugepaged
;
255 unsigned long orders
= thp_supported_orders();
256 unsigned long shmem_orders
= thp_shmem_supported_orders();
261 thp_write_string("enabled", thp_enabled_strings
[settings
->thp_enabled
]);
262 thp_write_string("defrag", thp_defrag_strings
[settings
->thp_defrag
]);
263 thp_write_string("shmem_enabled",
264 shmem_enabled_strings
[settings
->shmem_enabled
]);
265 thp_write_num("use_zero_page", settings
->use_zero_page
);
267 thp_write_num("khugepaged/defrag", khugepaged
->defrag
);
268 thp_write_num("khugepaged/alloc_sleep_millisecs",
269 khugepaged
->alloc_sleep_millisecs
);
270 thp_write_num("khugepaged/scan_sleep_millisecs",
271 khugepaged
->scan_sleep_millisecs
);
272 thp_write_num("khugepaged/max_ptes_none", khugepaged
->max_ptes_none
);
273 thp_write_num("khugepaged/max_ptes_swap", khugepaged
->max_ptes_swap
);
274 thp_write_num("khugepaged/max_ptes_shared", khugepaged
->max_ptes_shared
);
275 thp_write_num("khugepaged/pages_to_scan", khugepaged
->pages_to_scan
);
277 if (dev_queue_read_ahead_path
[0])
278 write_num(dev_queue_read_ahead_path
, settings
->read_ahead_kb
);
280 for (i
= 0; i
< NR_ORDERS
; i
++) {
281 if (!((1 << i
) & orders
))
283 snprintf(path
, PATH_MAX
, "hugepages-%ukB/enabled",
284 (getpagesize() >> 10) << i
);
285 enabled
= settings
->hugepages
[i
].enabled
;
286 thp_write_string(path
, thp_enabled_strings
[enabled
]);
289 for (i
= 0; i
< NR_ORDERS
; i
++) {
290 if (!((1 << i
) & shmem_orders
))
292 snprintf(path
, PATH_MAX
, "hugepages-%ukB/shmem_enabled",
293 (getpagesize() >> 10) << i
);
294 enabled
= settings
->shmem_hugepages
[i
].enabled
;
295 thp_write_string(path
, shmem_enabled_strings
[enabled
]);
299 struct thp_settings
*thp_current_settings(void)
301 if (!settings_index
) {
302 printf("Fail: No settings set");
305 return settings_stack
+ settings_index
- 1;
308 void thp_push_settings(struct thp_settings
*settings
)
310 if (settings_index
>= MAX_SETTINGS_DEPTH
) {
311 printf("Fail: Settings stack exceeded");
314 settings_stack
[settings_index
++] = *settings
;
315 thp_write_settings(thp_current_settings());
318 void thp_pop_settings(void)
320 if (settings_index
<= 0) {
321 printf("Fail: Settings stack empty");
325 thp_write_settings(thp_current_settings());
328 void thp_restore_settings(void)
330 thp_write_settings(&saved_settings
);
333 void thp_save_settings(void)
335 thp_read_settings(&saved_settings
);
338 void thp_set_read_ahead_path(char *path
)
341 dev_queue_read_ahead_path
[0] = '\0';
345 strncpy(dev_queue_read_ahead_path
, path
,
346 sizeof(dev_queue_read_ahead_path
));
347 dev_queue_read_ahead_path
[sizeof(dev_queue_read_ahead_path
) - 1] = '\0';
350 static unsigned long __thp_supported_orders(bool is_shmem
)
352 unsigned long orders
= 0;
356 char anon_dir
[] = "enabled";
357 char shmem_dir
[] = "shmem_enabled";
359 for (i
= 0; i
< NR_ORDERS
; i
++) {
360 ret
= snprintf(path
, PATH_MAX
, THP_SYSFS
"hugepages-%ukB/%s",
361 (getpagesize() >> 10) << i
, is_shmem
? shmem_dir
: anon_dir
);
362 if (ret
>= PATH_MAX
) {
363 printf("%s: Pathname is too long\n", __func__
);
367 ret
= read_file(path
, buf
, sizeof(buf
));
375 unsigned long thp_supported_orders(void)
377 return __thp_supported_orders(false);
380 unsigned long thp_shmem_supported_orders(void)
382 return __thp_supported_orders(true);