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} 350