181afb1eeSMike Rapoport (Microsoft) // SPDX-License-Identifier: GPL-2.0 21bdc1698SMike Rapoport (Microsoft) #include <dirent.h> 381afb1eeSMike Rapoport (Microsoft) #include <fcntl.h> 481afb1eeSMike Rapoport (Microsoft) #include <limits.h> 581afb1eeSMike Rapoport (Microsoft) #include <signal.h> 61bdc1698SMike Rapoport (Microsoft) #include <stddef.h> 781afb1eeSMike Rapoport (Microsoft) #include <stdio.h> 881afb1eeSMike Rapoport (Microsoft) #include <stdlib.h> 981afb1eeSMike Rapoport (Microsoft) #include <string.h> 1081afb1eeSMike Rapoport (Microsoft) #include <unistd.h> 1181afb1eeSMike Rapoport (Microsoft) 1281afb1eeSMike Rapoport (Microsoft) #include "vm_util.h" 1381afb1eeSMike Rapoport (Microsoft) #include "hugepage_settings.h" 1481afb1eeSMike Rapoport (Microsoft) 1581afb1eeSMike Rapoport (Microsoft) #define THP_SYSFS "/sys/kernel/mm/transparent_hugepage/" 1681afb1eeSMike Rapoport (Microsoft) #define MAX_SETTINGS_DEPTH 4 1781afb1eeSMike Rapoport (Microsoft) static struct thp_settings settings_stack[MAX_SETTINGS_DEPTH]; 1881afb1eeSMike Rapoport (Microsoft) static int settings_index; 1981afb1eeSMike Rapoport (Microsoft) static struct thp_settings saved_settings; 2081afb1eeSMike Rapoport (Microsoft) static char dev_queue_read_ahead_path[PATH_MAX]; 2181afb1eeSMike Rapoport (Microsoft) static bool thp_settings_saved; 2281afb1eeSMike Rapoport (Microsoft) 2381afb1eeSMike Rapoport (Microsoft) static const char * const thp_enabled_strings[] = { 2481afb1eeSMike Rapoport (Microsoft) "never", 2581afb1eeSMike Rapoport (Microsoft) "always", 2681afb1eeSMike Rapoport (Microsoft) "inherit", 2781afb1eeSMike Rapoport (Microsoft) "madvise", 2881afb1eeSMike Rapoport (Microsoft) NULL 2981afb1eeSMike Rapoport (Microsoft) }; 3081afb1eeSMike Rapoport (Microsoft) 3181afb1eeSMike Rapoport (Microsoft) static const char * const thp_defrag_strings[] = { 3281afb1eeSMike Rapoport (Microsoft) "always", 3381afb1eeSMike Rapoport (Microsoft) "defer", 3481afb1eeSMike Rapoport (Microsoft) "defer+madvise", 3581afb1eeSMike Rapoport (Microsoft) "madvise", 3681afb1eeSMike Rapoport (Microsoft) "never", 3781afb1eeSMike Rapoport (Microsoft) NULL 3881afb1eeSMike Rapoport (Microsoft) }; 3981afb1eeSMike Rapoport (Microsoft) 4081afb1eeSMike Rapoport (Microsoft) static const char * const shmem_enabled_strings[] = { 4181afb1eeSMike Rapoport (Microsoft) "never", 4281afb1eeSMike Rapoport (Microsoft) "always", 4381afb1eeSMike Rapoport (Microsoft) "within_size", 4481afb1eeSMike Rapoport (Microsoft) "advise", 4581afb1eeSMike Rapoport (Microsoft) "inherit", 4681afb1eeSMike Rapoport (Microsoft) "deny", 4781afb1eeSMike Rapoport (Microsoft) "force", 4881afb1eeSMike Rapoport (Microsoft) NULL 4981afb1eeSMike Rapoport (Microsoft) }; 5081afb1eeSMike Rapoport (Microsoft) 5181afb1eeSMike Rapoport (Microsoft) int thp_read_string(const char *name, const char * const strings[]) 5281afb1eeSMike Rapoport (Microsoft) { 5381afb1eeSMike Rapoport (Microsoft) char path[PATH_MAX]; 5481afb1eeSMike Rapoport (Microsoft) char buf[256]; 5581afb1eeSMike Rapoport (Microsoft) char *c; 5681afb1eeSMike Rapoport (Microsoft) int ret; 5781afb1eeSMike Rapoport (Microsoft) 5881afb1eeSMike Rapoport (Microsoft) ret = snprintf(path, PATH_MAX, THP_SYSFS "%s", name); 5981afb1eeSMike Rapoport (Microsoft) if (ret >= PATH_MAX) { 6081afb1eeSMike Rapoport (Microsoft) printf("%s: Pathname is too long\n", __func__); 6181afb1eeSMike Rapoport (Microsoft) exit(EXIT_FAILURE); 6281afb1eeSMike Rapoport (Microsoft) } 6381afb1eeSMike Rapoport (Microsoft) 6481afb1eeSMike Rapoport (Microsoft) if (!read_file(path, buf, sizeof(buf))) { 6581afb1eeSMike Rapoport (Microsoft) perror(path); 6681afb1eeSMike Rapoport (Microsoft) exit(EXIT_FAILURE); 6781afb1eeSMike Rapoport (Microsoft) } 6881afb1eeSMike Rapoport (Microsoft) 6981afb1eeSMike Rapoport (Microsoft) c = strchr(buf, '['); 7081afb1eeSMike Rapoport (Microsoft) if (!c) { 7181afb1eeSMike Rapoport (Microsoft) printf("%s: Parse failure\n", __func__); 7281afb1eeSMike Rapoport (Microsoft) exit(EXIT_FAILURE); 7381afb1eeSMike Rapoport (Microsoft) } 7481afb1eeSMike Rapoport (Microsoft) 7581afb1eeSMike Rapoport (Microsoft) c++; 7681afb1eeSMike Rapoport (Microsoft) memmove(buf, c, sizeof(buf) - (c - buf)); 7781afb1eeSMike Rapoport (Microsoft) 7881afb1eeSMike Rapoport (Microsoft) c = strchr(buf, ']'); 7981afb1eeSMike Rapoport (Microsoft) if (!c) { 8081afb1eeSMike Rapoport (Microsoft) printf("%s: Parse failure\n", __func__); 8181afb1eeSMike Rapoport (Microsoft) exit(EXIT_FAILURE); 8281afb1eeSMike Rapoport (Microsoft) } 8381afb1eeSMike Rapoport (Microsoft) *c = '\0'; 8481afb1eeSMike Rapoport (Microsoft) 8581afb1eeSMike Rapoport (Microsoft) ret = 0; 8681afb1eeSMike Rapoport (Microsoft) while (strings[ret]) { 8781afb1eeSMike Rapoport (Microsoft) if (!strcmp(strings[ret], buf)) 8881afb1eeSMike Rapoport (Microsoft) return ret; 8981afb1eeSMike Rapoport (Microsoft) ret++; 9081afb1eeSMike Rapoport (Microsoft) } 9181afb1eeSMike Rapoport (Microsoft) 9281afb1eeSMike Rapoport (Microsoft) printf("Failed to parse %s\n", name); 9381afb1eeSMike Rapoport (Microsoft) exit(EXIT_FAILURE); 9481afb1eeSMike Rapoport (Microsoft) } 9581afb1eeSMike Rapoport (Microsoft) 9681afb1eeSMike Rapoport (Microsoft) void thp_write_string(const char *name, const char *val) 9781afb1eeSMike Rapoport (Microsoft) { 9881afb1eeSMike Rapoport (Microsoft) char path[PATH_MAX]; 9981afb1eeSMike Rapoport (Microsoft) int ret; 10081afb1eeSMike Rapoport (Microsoft) 10181afb1eeSMike Rapoport (Microsoft) ret = snprintf(path, PATH_MAX, THP_SYSFS "%s", name); 10281afb1eeSMike Rapoport (Microsoft) if (ret >= PATH_MAX) { 10381afb1eeSMike Rapoport (Microsoft) printf("%s: Pathname is too long\n", __func__); 10481afb1eeSMike Rapoport (Microsoft) exit(EXIT_FAILURE); 10581afb1eeSMike Rapoport (Microsoft) } 10681afb1eeSMike Rapoport (Microsoft) write_file(path, val, strlen(val) + 1); 10781afb1eeSMike Rapoport (Microsoft) } 10881afb1eeSMike Rapoport (Microsoft) 10981afb1eeSMike Rapoport (Microsoft) unsigned long thp_read_num(const char *name) 11081afb1eeSMike Rapoport (Microsoft) { 11181afb1eeSMike Rapoport (Microsoft) char path[PATH_MAX]; 11281afb1eeSMike Rapoport (Microsoft) int ret; 11381afb1eeSMike Rapoport (Microsoft) 11481afb1eeSMike Rapoport (Microsoft) ret = snprintf(path, PATH_MAX, THP_SYSFS "%s", name); 11581afb1eeSMike Rapoport (Microsoft) if (ret >= PATH_MAX) { 11681afb1eeSMike Rapoport (Microsoft) printf("%s: Pathname is too long\n", __func__); 11781afb1eeSMike Rapoport (Microsoft) exit(EXIT_FAILURE); 11881afb1eeSMike Rapoport (Microsoft) } 11981afb1eeSMike Rapoport (Microsoft) return read_num(path); 12081afb1eeSMike Rapoport (Microsoft) } 12181afb1eeSMike Rapoport (Microsoft) 12281afb1eeSMike Rapoport (Microsoft) void thp_write_num(const char *name, unsigned long num) 12381afb1eeSMike Rapoport (Microsoft) { 12481afb1eeSMike Rapoport (Microsoft) char path[PATH_MAX]; 12581afb1eeSMike Rapoport (Microsoft) int ret; 12681afb1eeSMike Rapoport (Microsoft) 12781afb1eeSMike Rapoport (Microsoft) ret = snprintf(path, PATH_MAX, THP_SYSFS "%s", name); 12881afb1eeSMike Rapoport (Microsoft) if (ret >= PATH_MAX) { 12981afb1eeSMike Rapoport (Microsoft) printf("%s: Pathname is too long\n", __func__); 13081afb1eeSMike Rapoport (Microsoft) exit(EXIT_FAILURE); 13181afb1eeSMike Rapoport (Microsoft) } 13281afb1eeSMike Rapoport (Microsoft) write_num(path, num); 13381afb1eeSMike Rapoport (Microsoft) } 13481afb1eeSMike Rapoport (Microsoft) 13581afb1eeSMike Rapoport (Microsoft) void thp_read_settings(struct thp_settings *settings) 13681afb1eeSMike Rapoport (Microsoft) { 13781afb1eeSMike Rapoport (Microsoft) unsigned long orders = thp_supported_orders(); 13881afb1eeSMike Rapoport (Microsoft) unsigned long shmem_orders = thp_shmem_supported_orders(); 13981afb1eeSMike Rapoport (Microsoft) char path[PATH_MAX]; 14081afb1eeSMike Rapoport (Microsoft) int i; 14181afb1eeSMike Rapoport (Microsoft) 14281afb1eeSMike Rapoport (Microsoft) *settings = (struct thp_settings) { 14381afb1eeSMike Rapoport (Microsoft) .thp_enabled = thp_read_string("enabled", thp_enabled_strings), 14481afb1eeSMike Rapoport (Microsoft) .thp_defrag = thp_read_string("defrag", thp_defrag_strings), 14581afb1eeSMike Rapoport (Microsoft) .shmem_enabled = 14681afb1eeSMike Rapoport (Microsoft) thp_read_string("shmem_enabled", shmem_enabled_strings), 14781afb1eeSMike Rapoport (Microsoft) .use_zero_page = thp_read_num("use_zero_page"), 14881afb1eeSMike Rapoport (Microsoft) }; 14981afb1eeSMike Rapoport (Microsoft) settings->khugepaged = (struct khugepaged_settings) { 15081afb1eeSMike Rapoport (Microsoft) .defrag = thp_read_num("khugepaged/defrag"), 15181afb1eeSMike Rapoport (Microsoft) .alloc_sleep_millisecs = 15281afb1eeSMike Rapoport (Microsoft) thp_read_num("khugepaged/alloc_sleep_millisecs"), 15381afb1eeSMike Rapoport (Microsoft) .scan_sleep_millisecs = 15481afb1eeSMike Rapoport (Microsoft) thp_read_num("khugepaged/scan_sleep_millisecs"), 15581afb1eeSMike Rapoport (Microsoft) .max_ptes_none = thp_read_num("khugepaged/max_ptes_none"), 15681afb1eeSMike Rapoport (Microsoft) .max_ptes_swap = thp_read_num("khugepaged/max_ptes_swap"), 15781afb1eeSMike Rapoport (Microsoft) .max_ptes_shared = thp_read_num("khugepaged/max_ptes_shared"), 15881afb1eeSMike Rapoport (Microsoft) .pages_to_scan = thp_read_num("khugepaged/pages_to_scan"), 15981afb1eeSMike Rapoport (Microsoft) }; 16081afb1eeSMike Rapoport (Microsoft) if (dev_queue_read_ahead_path[0]) 16181afb1eeSMike Rapoport (Microsoft) settings->read_ahead_kb = read_num(dev_queue_read_ahead_path); 16281afb1eeSMike Rapoport (Microsoft) 16381afb1eeSMike Rapoport (Microsoft) for (i = 0; i < NR_ORDERS; i++) { 16481afb1eeSMike Rapoport (Microsoft) if (!((1 << i) & orders)) { 16581afb1eeSMike Rapoport (Microsoft) settings->hugepages[i].enabled = THP_NEVER; 16681afb1eeSMike Rapoport (Microsoft) continue; 16781afb1eeSMike Rapoport (Microsoft) } 16881afb1eeSMike Rapoport (Microsoft) snprintf(path, PATH_MAX, "hugepages-%ukB/enabled", 16981afb1eeSMike Rapoport (Microsoft) (getpagesize() >> 10) << i); 17081afb1eeSMike Rapoport (Microsoft) settings->hugepages[i].enabled = 17181afb1eeSMike Rapoport (Microsoft) thp_read_string(path, thp_enabled_strings); 17281afb1eeSMike Rapoport (Microsoft) } 17381afb1eeSMike Rapoport (Microsoft) 17481afb1eeSMike Rapoport (Microsoft) for (i = 0; i < NR_ORDERS; i++) { 17581afb1eeSMike Rapoport (Microsoft) if (!((1 << i) & shmem_orders)) { 17681afb1eeSMike Rapoport (Microsoft) settings->shmem_hugepages[i].enabled = SHMEM_NEVER; 17781afb1eeSMike Rapoport (Microsoft) continue; 17881afb1eeSMike Rapoport (Microsoft) } 17981afb1eeSMike Rapoport (Microsoft) snprintf(path, PATH_MAX, "hugepages-%ukB/shmem_enabled", 18081afb1eeSMike Rapoport (Microsoft) (getpagesize() >> 10) << i); 18181afb1eeSMike Rapoport (Microsoft) settings->shmem_hugepages[i].enabled = 18281afb1eeSMike Rapoport (Microsoft) thp_read_string(path, shmem_enabled_strings); 18381afb1eeSMike Rapoport (Microsoft) } 18481afb1eeSMike Rapoport (Microsoft) } 18581afb1eeSMike Rapoport (Microsoft) 18681afb1eeSMike Rapoport (Microsoft) void thp_write_settings(struct thp_settings *settings) 18781afb1eeSMike Rapoport (Microsoft) { 18881afb1eeSMike Rapoport (Microsoft) struct khugepaged_settings *khugepaged = &settings->khugepaged; 18981afb1eeSMike Rapoport (Microsoft) unsigned long orders = thp_supported_orders(); 19081afb1eeSMike Rapoport (Microsoft) unsigned long shmem_orders = thp_shmem_supported_orders(); 19181afb1eeSMike Rapoport (Microsoft) char path[PATH_MAX]; 19281afb1eeSMike Rapoport (Microsoft) int enabled; 19381afb1eeSMike Rapoport (Microsoft) int i; 19481afb1eeSMike Rapoport (Microsoft) 19581afb1eeSMike Rapoport (Microsoft) thp_write_string("enabled", thp_enabled_strings[settings->thp_enabled]); 19681afb1eeSMike Rapoport (Microsoft) thp_write_string("defrag", thp_defrag_strings[settings->thp_defrag]); 19781afb1eeSMike Rapoport (Microsoft) thp_write_string("shmem_enabled", 19881afb1eeSMike Rapoport (Microsoft) shmem_enabled_strings[settings->shmem_enabled]); 19981afb1eeSMike Rapoport (Microsoft) thp_write_num("use_zero_page", settings->use_zero_page); 20081afb1eeSMike Rapoport (Microsoft) 20181afb1eeSMike Rapoport (Microsoft) thp_write_num("khugepaged/defrag", khugepaged->defrag); 20281afb1eeSMike Rapoport (Microsoft) thp_write_num("khugepaged/alloc_sleep_millisecs", 20381afb1eeSMike Rapoport (Microsoft) khugepaged->alloc_sleep_millisecs); 20481afb1eeSMike Rapoport (Microsoft) thp_write_num("khugepaged/scan_sleep_millisecs", 20581afb1eeSMike Rapoport (Microsoft) khugepaged->scan_sleep_millisecs); 20681afb1eeSMike Rapoport (Microsoft) thp_write_num("khugepaged/max_ptes_none", khugepaged->max_ptes_none); 20781afb1eeSMike Rapoport (Microsoft) thp_write_num("khugepaged/max_ptes_swap", khugepaged->max_ptes_swap); 20881afb1eeSMike Rapoport (Microsoft) thp_write_num("khugepaged/max_ptes_shared", khugepaged->max_ptes_shared); 20981afb1eeSMike Rapoport (Microsoft) thp_write_num("khugepaged/pages_to_scan", khugepaged->pages_to_scan); 21081afb1eeSMike Rapoport (Microsoft) 21181afb1eeSMike Rapoport (Microsoft) if (dev_queue_read_ahead_path[0]) 21281afb1eeSMike Rapoport (Microsoft) write_num(dev_queue_read_ahead_path, settings->read_ahead_kb); 21381afb1eeSMike Rapoport (Microsoft) 21481afb1eeSMike Rapoport (Microsoft) for (i = 0; i < NR_ORDERS; i++) { 21581afb1eeSMike Rapoport (Microsoft) if (!((1 << i) & orders)) 21681afb1eeSMike Rapoport (Microsoft) continue; 21781afb1eeSMike Rapoport (Microsoft) snprintf(path, PATH_MAX, "hugepages-%ukB/enabled", 21881afb1eeSMike Rapoport (Microsoft) (getpagesize() >> 10) << i); 21981afb1eeSMike Rapoport (Microsoft) enabled = settings->hugepages[i].enabled; 22081afb1eeSMike Rapoport (Microsoft) thp_write_string(path, thp_enabled_strings[enabled]); 22181afb1eeSMike Rapoport (Microsoft) } 22281afb1eeSMike Rapoport (Microsoft) 22381afb1eeSMike Rapoport (Microsoft) for (i = 0; i < NR_ORDERS; i++) { 22481afb1eeSMike Rapoport (Microsoft) if (!((1 << i) & shmem_orders)) 22581afb1eeSMike Rapoport (Microsoft) continue; 22681afb1eeSMike Rapoport (Microsoft) snprintf(path, PATH_MAX, "hugepages-%ukB/shmem_enabled", 22781afb1eeSMike Rapoport (Microsoft) (getpagesize() >> 10) << i); 22881afb1eeSMike Rapoport (Microsoft) enabled = settings->shmem_hugepages[i].enabled; 22981afb1eeSMike Rapoport (Microsoft) thp_write_string(path, shmem_enabled_strings[enabled]); 23081afb1eeSMike Rapoport (Microsoft) } 23181afb1eeSMike Rapoport (Microsoft) } 23281afb1eeSMike Rapoport (Microsoft) 23381afb1eeSMike Rapoport (Microsoft) struct thp_settings *thp_current_settings(void) 23481afb1eeSMike Rapoport (Microsoft) { 23581afb1eeSMike Rapoport (Microsoft) if (!settings_index) { 23681afb1eeSMike Rapoport (Microsoft) printf("Fail: No settings set"); 23781afb1eeSMike Rapoport (Microsoft) exit(EXIT_FAILURE); 23881afb1eeSMike Rapoport (Microsoft) } 23981afb1eeSMike Rapoport (Microsoft) return settings_stack + settings_index - 1; 24081afb1eeSMike Rapoport (Microsoft) } 24181afb1eeSMike Rapoport (Microsoft) 24281afb1eeSMike Rapoport (Microsoft) void thp_push_settings(struct thp_settings *settings) 24381afb1eeSMike Rapoport (Microsoft) { 24481afb1eeSMike Rapoport (Microsoft) if (settings_index >= MAX_SETTINGS_DEPTH) { 24581afb1eeSMike Rapoport (Microsoft) printf("Fail: Settings stack exceeded"); 24681afb1eeSMike Rapoport (Microsoft) exit(EXIT_FAILURE); 24781afb1eeSMike Rapoport (Microsoft) } 24881afb1eeSMike Rapoport (Microsoft) settings_stack[settings_index++] = *settings; 24981afb1eeSMike Rapoport (Microsoft) thp_write_settings(thp_current_settings()); 25081afb1eeSMike Rapoport (Microsoft) } 25181afb1eeSMike Rapoport (Microsoft) 25281afb1eeSMike Rapoport (Microsoft) void thp_pop_settings(void) 25381afb1eeSMike Rapoport (Microsoft) { 25481afb1eeSMike Rapoport (Microsoft) if (settings_index <= 0) { 25581afb1eeSMike Rapoport (Microsoft) printf("Fail: Settings stack empty"); 25681afb1eeSMike Rapoport (Microsoft) exit(EXIT_FAILURE); 25781afb1eeSMike Rapoport (Microsoft) } 25881afb1eeSMike Rapoport (Microsoft) --settings_index; 25981afb1eeSMike Rapoport (Microsoft) thp_write_settings(thp_current_settings()); 26081afb1eeSMike Rapoport (Microsoft) } 26181afb1eeSMike Rapoport (Microsoft) 26281afb1eeSMike Rapoport (Microsoft) void thp_restore_settings(void) 26381afb1eeSMike Rapoport (Microsoft) { 26481afb1eeSMike Rapoport (Microsoft) if (thp_settings_saved) 26581afb1eeSMike Rapoport (Microsoft) thp_write_settings(&saved_settings); 26681afb1eeSMike Rapoport (Microsoft) } 26781afb1eeSMike Rapoport (Microsoft) 268*08e51ae5SMike Rapoport (Microsoft) static void __thp_save_settings(void) 26981afb1eeSMike Rapoport (Microsoft) { 270*08e51ae5SMike Rapoport (Microsoft) if (!thp_available()) 271*08e51ae5SMike Rapoport (Microsoft) return; 27281afb1eeSMike Rapoport (Microsoft) 273*08e51ae5SMike Rapoport (Microsoft) if (thp_settings_saved) 274*08e51ae5SMike Rapoport (Microsoft) return; 27581afb1eeSMike Rapoport (Microsoft) 27681afb1eeSMike Rapoport (Microsoft) thp_read_settings(&saved_settings); 27781afb1eeSMike Rapoport (Microsoft) thp_settings_saved = true; 27881afb1eeSMike Rapoport (Microsoft) } 27981afb1eeSMike Rapoport (Microsoft) 28081afb1eeSMike Rapoport (Microsoft) void thp_set_read_ahead_path(char *path) 28181afb1eeSMike Rapoport (Microsoft) { 28281afb1eeSMike Rapoport (Microsoft) if (!path) { 28381afb1eeSMike Rapoport (Microsoft) dev_queue_read_ahead_path[0] = '\0'; 28481afb1eeSMike Rapoport (Microsoft) return; 28581afb1eeSMike Rapoport (Microsoft) } 28681afb1eeSMike Rapoport (Microsoft) 28781afb1eeSMike Rapoport (Microsoft) strncpy(dev_queue_read_ahead_path, path, 28881afb1eeSMike Rapoport (Microsoft) sizeof(dev_queue_read_ahead_path)); 28981afb1eeSMike Rapoport (Microsoft) dev_queue_read_ahead_path[sizeof(dev_queue_read_ahead_path) - 1] = '\0'; 29081afb1eeSMike Rapoport (Microsoft) } 29181afb1eeSMike Rapoport (Microsoft) 29281afb1eeSMike Rapoport (Microsoft) static unsigned long __thp_supported_orders(bool is_shmem) 29381afb1eeSMike Rapoport (Microsoft) { 29481afb1eeSMike Rapoport (Microsoft) unsigned long orders = 0; 29581afb1eeSMike Rapoport (Microsoft) char path[PATH_MAX]; 29681afb1eeSMike Rapoport (Microsoft) char buf[256]; 29781afb1eeSMike Rapoport (Microsoft) int ret, i; 29881afb1eeSMike Rapoport (Microsoft) char anon_dir[] = "enabled"; 29981afb1eeSMike Rapoport (Microsoft) char shmem_dir[] = "shmem_enabled"; 30081afb1eeSMike Rapoport (Microsoft) 30181afb1eeSMike Rapoport (Microsoft) for (i = 0; i < NR_ORDERS; i++) { 30281afb1eeSMike Rapoport (Microsoft) ret = snprintf(path, PATH_MAX, THP_SYSFS "hugepages-%ukB/%s", 30381afb1eeSMike Rapoport (Microsoft) (getpagesize() >> 10) << i, is_shmem ? shmem_dir : anon_dir); 30481afb1eeSMike Rapoport (Microsoft) if (ret >= PATH_MAX) { 30581afb1eeSMike Rapoport (Microsoft) printf("%s: Pathname is too long\n", __func__); 30681afb1eeSMike Rapoport (Microsoft) exit(EXIT_FAILURE); 30781afb1eeSMike Rapoport (Microsoft) } 30881afb1eeSMike Rapoport (Microsoft) 30981afb1eeSMike Rapoport (Microsoft) ret = read_file(path, buf, sizeof(buf)); 31081afb1eeSMike Rapoport (Microsoft) if (ret) 31181afb1eeSMike Rapoport (Microsoft) orders |= 1UL << i; 31281afb1eeSMike Rapoport (Microsoft) } 31381afb1eeSMike Rapoport (Microsoft) 31481afb1eeSMike Rapoport (Microsoft) return orders; 31581afb1eeSMike Rapoport (Microsoft) } 31681afb1eeSMike Rapoport (Microsoft) 31781afb1eeSMike Rapoport (Microsoft) unsigned long thp_supported_orders(void) 31881afb1eeSMike Rapoport (Microsoft) { 31981afb1eeSMike Rapoport (Microsoft) return __thp_supported_orders(false); 32081afb1eeSMike Rapoport (Microsoft) } 32181afb1eeSMike Rapoport (Microsoft) 32281afb1eeSMike Rapoport (Microsoft) unsigned long thp_shmem_supported_orders(void) 32381afb1eeSMike Rapoport (Microsoft) { 32481afb1eeSMike Rapoport (Microsoft) return __thp_supported_orders(true); 32581afb1eeSMike Rapoport (Microsoft) } 32681afb1eeSMike Rapoport (Microsoft) 32781afb1eeSMike Rapoport (Microsoft) bool thp_available(void) 32881afb1eeSMike Rapoport (Microsoft) { 32981afb1eeSMike Rapoport (Microsoft) if (access(THP_SYSFS, F_OK) != 0) 33081afb1eeSMike Rapoport (Microsoft) return false; 33181afb1eeSMike Rapoport (Microsoft) return true; 33281afb1eeSMike Rapoport (Microsoft) } 33381afb1eeSMike Rapoport (Microsoft) 33481afb1eeSMike Rapoport (Microsoft) bool thp_is_enabled(void) 33581afb1eeSMike Rapoport (Microsoft) { 33681afb1eeSMike Rapoport (Microsoft) if (!thp_available()) 33781afb1eeSMike Rapoport (Microsoft) return false; 33881afb1eeSMike Rapoport (Microsoft) 33981afb1eeSMike Rapoport (Microsoft) int mode = thp_read_string("enabled", thp_enabled_strings); 34081afb1eeSMike Rapoport (Microsoft) 34181afb1eeSMike Rapoport (Microsoft) /* THP is considered enabled if it's either "always" or "madvise" */ 34281afb1eeSMike Rapoport (Microsoft) return mode == 1 || mode == 3; 34381afb1eeSMike Rapoport (Microsoft) } 3441bdc1698SMike Rapoport (Microsoft) 345*08e51ae5SMike Rapoport (Microsoft) #define HUGETLB_MAX_NR_PAGESIZES 10 346*08e51ae5SMike Rapoport (Microsoft) struct hugetlb_settings { 347*08e51ae5SMike Rapoport (Microsoft) unsigned long nr_hugepages[HUGETLB_MAX_NR_PAGESIZES]; 348*08e51ae5SMike Rapoport (Microsoft) unsigned long sizes[HUGETLB_MAX_NR_PAGESIZES]; 349*08e51ae5SMike Rapoport (Microsoft) unsigned long default_size; 350*08e51ae5SMike Rapoport (Microsoft) int nr_sizes; 351*08e51ae5SMike Rapoport (Microsoft) }; 352*08e51ae5SMike Rapoport (Microsoft) 353*08e51ae5SMike Rapoport (Microsoft) static struct hugetlb_settings hugetlb_saved_settings; 354*08e51ae5SMike Rapoport (Microsoft) static bool hugetlb_settings_saved; 355*08e51ae5SMike Rapoport (Microsoft) 3560020a1f5SMike Rapoport (Microsoft) int detect_hugetlb_page_sizes(unsigned long sizes[], int max) 3571bdc1698SMike Rapoport (Microsoft) { 358*08e51ae5SMike Rapoport (Microsoft) static struct hugetlb_settings *settings = &hugetlb_saved_settings; 359*08e51ae5SMike Rapoport (Microsoft) DIR *dir; 3601bdc1698SMike Rapoport (Microsoft) int count = 0; 3611bdc1698SMike Rapoport (Microsoft) 362*08e51ae5SMike Rapoport (Microsoft) if (settings->nr_sizes) { 363*08e51ae5SMike Rapoport (Microsoft) if (settings->nr_sizes < max) 364*08e51ae5SMike Rapoport (Microsoft) max = settings->nr_sizes; 365*08e51ae5SMike Rapoport (Microsoft) for (count = 0; count < max; count++) 366*08e51ae5SMike Rapoport (Microsoft) sizes[count] = settings->sizes[count]; 367*08e51ae5SMike Rapoport (Microsoft) return count; 368*08e51ae5SMike Rapoport (Microsoft) } 369*08e51ae5SMike Rapoport (Microsoft) 370*08e51ae5SMike Rapoport (Microsoft) dir = opendir("/sys/kernel/mm/hugepages/"); 3711bdc1698SMike Rapoport (Microsoft) if (!dir) 3721bdc1698SMike Rapoport (Microsoft) return 0; 3731bdc1698SMike Rapoport (Microsoft) 3741bdc1698SMike Rapoport (Microsoft) while (count < max) { 3751bdc1698SMike Rapoport (Microsoft) struct dirent *entry = readdir(dir); 3761bdc1698SMike Rapoport (Microsoft) size_t kb; 3771bdc1698SMike Rapoport (Microsoft) 3781bdc1698SMike Rapoport (Microsoft) if (!entry) 3791bdc1698SMike Rapoport (Microsoft) break; 3801bdc1698SMike Rapoport (Microsoft) if (entry->d_type != DT_DIR) 3811bdc1698SMike Rapoport (Microsoft) continue; 3821bdc1698SMike Rapoport (Microsoft) if (sscanf(entry->d_name, "hugepages-%zukB", &kb) != 1) 3831bdc1698SMike Rapoport (Microsoft) continue; 3841bdc1698SMike Rapoport (Microsoft) sizes[count++] = kb * 1024; 3851bdc1698SMike Rapoport (Microsoft) ksft_print_msg("[INFO] detected hugetlb page size: %zu KiB\n", 3861bdc1698SMike Rapoport (Microsoft) kb); 3871bdc1698SMike Rapoport (Microsoft) } 3881bdc1698SMike Rapoport (Microsoft) closedir(dir); 3891bdc1698SMike Rapoport (Microsoft) return count; 3901bdc1698SMike Rapoport (Microsoft) } 3911bdc1698SMike Rapoport (Microsoft) 3921bdc1698SMike Rapoport (Microsoft) unsigned long default_huge_page_size(void) 3931bdc1698SMike Rapoport (Microsoft) { 394*08e51ae5SMike Rapoport (Microsoft) static struct hugetlb_settings *settings = &hugetlb_saved_settings; 3951bdc1698SMike Rapoport (Microsoft) unsigned long hps = 0; 3961bdc1698SMike Rapoport (Microsoft) char *line = NULL; 3971bdc1698SMike Rapoport (Microsoft) size_t linelen = 0; 398*08e51ae5SMike Rapoport (Microsoft) FILE *f; 3991bdc1698SMike Rapoport (Microsoft) 400*08e51ae5SMike Rapoport (Microsoft) if (settings->default_size) 401*08e51ae5SMike Rapoport (Microsoft) return settings->default_size; 402*08e51ae5SMike Rapoport (Microsoft) 403*08e51ae5SMike Rapoport (Microsoft) f = fopen("/proc/meminfo", "r"); 4041bdc1698SMike Rapoport (Microsoft) if (!f) 4051bdc1698SMike Rapoport (Microsoft) return 0; 4061bdc1698SMike Rapoport (Microsoft) while (getline(&line, &linelen, f) > 0) { 4071bdc1698SMike Rapoport (Microsoft) if (sscanf(line, "Hugepagesize: %lu kB", &hps) == 1) { 4081bdc1698SMike Rapoport (Microsoft) hps <<= 10; 4091bdc1698SMike Rapoport (Microsoft) break; 4101bdc1698SMike Rapoport (Microsoft) } 4111bdc1698SMike Rapoport (Microsoft) } 4121bdc1698SMike Rapoport (Microsoft) 4131bdc1698SMike Rapoport (Microsoft) free(line); 4141bdc1698SMike Rapoport (Microsoft) fclose(f); 4151bdc1698SMike Rapoport (Microsoft) return hps; 4161bdc1698SMike Rapoport (Microsoft) } 4171bdc1698SMike Rapoport (Microsoft) 41827477b28SMike Rapoport (Microsoft) static void hugetlb_sysfs_path(char *buf, size_t buflen, 41927477b28SMike Rapoport (Microsoft) unsigned long size, const char *attr) 42027477b28SMike Rapoport (Microsoft) { 42127477b28SMike Rapoport (Microsoft) snprintf(buf, buflen, "/sys/kernel/mm/hugepages/hugepages-%lukB/%s", 42227477b28SMike Rapoport (Microsoft) size / 1024, attr); 42327477b28SMike Rapoport (Microsoft) } 42427477b28SMike Rapoport (Microsoft) 42527477b28SMike Rapoport (Microsoft) unsigned long hugetlb_nr_pages(unsigned long size) 42627477b28SMike Rapoport (Microsoft) { 42727477b28SMike Rapoport (Microsoft) char path[PATH_MAX]; 42827477b28SMike Rapoport (Microsoft) 42927477b28SMike Rapoport (Microsoft) hugetlb_sysfs_path(path, sizeof(path), size, "nr_hugepages"); 43027477b28SMike Rapoport (Microsoft) 43127477b28SMike Rapoport (Microsoft) return read_num(path); 43227477b28SMike Rapoport (Microsoft) } 43327477b28SMike Rapoport (Microsoft) 43427477b28SMike Rapoport (Microsoft) void hugetlb_set_nr_pages(unsigned long size, unsigned long nr) 43527477b28SMike Rapoport (Microsoft) { 43627477b28SMike Rapoport (Microsoft) char path[PATH_MAX]; 43727477b28SMike Rapoport (Microsoft) 43827477b28SMike Rapoport (Microsoft) hugetlb_sysfs_path(path, sizeof(path), size, "nr_hugepages"); 43927477b28SMike Rapoport (Microsoft) 44027477b28SMike Rapoport (Microsoft) write_num(path, nr); 44127477b28SMike Rapoport (Microsoft) } 442024ec9a7SMike Rapoport (Microsoft) 443024ec9a7SMike Rapoport (Microsoft) unsigned long hugetlb_free_pages(unsigned long size) 444024ec9a7SMike Rapoport (Microsoft) { 445024ec9a7SMike Rapoport (Microsoft) char path[PATH_MAX]; 446024ec9a7SMike Rapoport (Microsoft) 447024ec9a7SMike Rapoport (Microsoft) hugetlb_sysfs_path(path, sizeof(path), size, "free_hugepages"); 448024ec9a7SMike Rapoport (Microsoft) 449024ec9a7SMike Rapoport (Microsoft) return read_num(path); 450024ec9a7SMike Rapoport (Microsoft) } 451*08e51ae5SMike Rapoport (Microsoft) 452*08e51ae5SMike Rapoport (Microsoft) static bool __hugetlb_setup(unsigned long size, unsigned long nr) 453*08e51ae5SMike Rapoport (Microsoft) { 454*08e51ae5SMike Rapoport (Microsoft) unsigned long free = hugetlb_free_pages(size); 455*08e51ae5SMike Rapoport (Microsoft) unsigned long total = hugetlb_nr_pages(size); 456*08e51ae5SMike Rapoport (Microsoft) 457*08e51ae5SMike Rapoport (Microsoft) if (free >= nr) 458*08e51ae5SMike Rapoport (Microsoft) return true; 459*08e51ae5SMike Rapoport (Microsoft) 460*08e51ae5SMike Rapoport (Microsoft) hugetlb_set_nr_pages(size, total + (nr - free)); 461*08e51ae5SMike Rapoport (Microsoft) 462*08e51ae5SMike Rapoport (Microsoft) return hugetlb_free_pages(size) >= nr; 463*08e51ae5SMike Rapoport (Microsoft) } 464*08e51ae5SMike Rapoport (Microsoft) 465*08e51ae5SMike Rapoport (Microsoft) bool hugetlb_setup_default(unsigned long nr) 466*08e51ae5SMike Rapoport (Microsoft) { 467*08e51ae5SMike Rapoport (Microsoft) unsigned long size; 468*08e51ae5SMike Rapoport (Microsoft) 469*08e51ae5SMike Rapoport (Microsoft) hugetlb_save_settings(); 470*08e51ae5SMike Rapoport (Microsoft) size = default_huge_page_size(); 471*08e51ae5SMike Rapoport (Microsoft) if (!size) 472*08e51ae5SMike Rapoport (Microsoft) return false; 473*08e51ae5SMike Rapoport (Microsoft) 474*08e51ae5SMike Rapoport (Microsoft) return __hugetlb_setup(size, nr); 475*08e51ae5SMike Rapoport (Microsoft) } 476*08e51ae5SMike Rapoport (Microsoft) 477*08e51ae5SMike Rapoport (Microsoft) bool hugetlb_setup_default_exact(unsigned long nr) 478*08e51ae5SMike Rapoport (Microsoft) { 479*08e51ae5SMike Rapoport (Microsoft) unsigned long size; 480*08e51ae5SMike Rapoport (Microsoft) 481*08e51ae5SMike Rapoport (Microsoft) hugetlb_save_settings(); 482*08e51ae5SMike Rapoport (Microsoft) size = default_huge_page_size(); 483*08e51ae5SMike Rapoport (Microsoft) if (!size) 484*08e51ae5SMike Rapoport (Microsoft) return false; 485*08e51ae5SMike Rapoport (Microsoft) 486*08e51ae5SMike Rapoport (Microsoft) hugetlb_set_nr_pages(size, nr); 487*08e51ae5SMike Rapoport (Microsoft) 488*08e51ae5SMike Rapoport (Microsoft) return hugetlb_free_pages(size) == nr; 489*08e51ae5SMike Rapoport (Microsoft) } 490*08e51ae5SMike Rapoport (Microsoft) 491*08e51ae5SMike Rapoport (Microsoft) unsigned long hugetlb_setup(unsigned long nr, unsigned long sizes[], 492*08e51ae5SMike Rapoport (Microsoft) int max) 493*08e51ae5SMike Rapoport (Microsoft) { 494*08e51ae5SMike Rapoport (Microsoft) unsigned long enabled[10]; 495*08e51ae5SMike Rapoport (Microsoft) int nr_sizes = 0; 496*08e51ae5SMike Rapoport (Microsoft) int nr_enabled; 497*08e51ae5SMike Rapoport (Microsoft) 498*08e51ae5SMike Rapoport (Microsoft) hugetlb_save_settings(); 499*08e51ae5SMike Rapoport (Microsoft) 500*08e51ae5SMike Rapoport (Microsoft) nr_enabled = detect_hugetlb_page_sizes(enabled, ARRAY_SIZE(enabled)); 501*08e51ae5SMike Rapoport (Microsoft) if (!nr_enabled) 502*08e51ae5SMike Rapoport (Microsoft) return 0; 503*08e51ae5SMike Rapoport (Microsoft) 504*08e51ae5SMike Rapoport (Microsoft) if (nr_enabled > max) { 505*08e51ae5SMike Rapoport (Microsoft) ksft_print_msg("detected %d huge page sizes, will only test %d\n", nr_enabled, max); 506*08e51ae5SMike Rapoport (Microsoft) nr_enabled = max; 507*08e51ae5SMike Rapoport (Microsoft) } 508*08e51ae5SMike Rapoport (Microsoft) 509*08e51ae5SMike Rapoport (Microsoft) /* request nr HugeTLB pages of every size. */ 510*08e51ae5SMike Rapoport (Microsoft) for (int i = 0; i < nr_enabled; i++) { 511*08e51ae5SMike Rapoport (Microsoft) if (!__hugetlb_setup(enabled[i], nr)) 512*08e51ae5SMike Rapoport (Microsoft) continue; 513*08e51ae5SMike Rapoport (Microsoft) sizes[nr_sizes++] = enabled[i]; 514*08e51ae5SMike Rapoport (Microsoft) } 515*08e51ae5SMike Rapoport (Microsoft) 516*08e51ae5SMike Rapoport (Microsoft) return nr_sizes; 517*08e51ae5SMike Rapoport (Microsoft) } 518*08e51ae5SMike Rapoport (Microsoft) 519*08e51ae5SMike Rapoport (Microsoft) static void __hugetlb_save_settings(void) 520*08e51ae5SMike Rapoport (Microsoft) { 521*08e51ae5SMike Rapoport (Microsoft) struct hugetlb_settings *settings = &hugetlb_saved_settings; 522*08e51ae5SMike Rapoport (Microsoft) int nr_sizes; 523*08e51ae5SMike Rapoport (Microsoft) 524*08e51ae5SMike Rapoport (Microsoft) if (hugetlb_settings_saved) 525*08e51ae5SMike Rapoport (Microsoft) return; 526*08e51ae5SMike Rapoport (Microsoft) 527*08e51ae5SMike Rapoport (Microsoft) settings->default_size = default_huge_page_size(); 528*08e51ae5SMike Rapoport (Microsoft) if (!settings->default_size) 529*08e51ae5SMike Rapoport (Microsoft) return; 530*08e51ae5SMike Rapoport (Microsoft) 531*08e51ae5SMike Rapoport (Microsoft) nr_sizes = detect_hugetlb_page_sizes(settings->sizes, 532*08e51ae5SMike Rapoport (Microsoft) HUGETLB_MAX_NR_PAGESIZES); 533*08e51ae5SMike Rapoport (Microsoft) if (!nr_sizes) { 534*08e51ae5SMike Rapoport (Microsoft) settings->default_size = 0; 535*08e51ae5SMike Rapoport (Microsoft) return; 536*08e51ae5SMike Rapoport (Microsoft) } 537*08e51ae5SMike Rapoport (Microsoft) 538*08e51ae5SMike Rapoport (Microsoft) for (int i = 0; i < nr_sizes; i++) { 539*08e51ae5SMike Rapoport (Microsoft) unsigned long sz = settings->sizes[i]; 540*08e51ae5SMike Rapoport (Microsoft) 541*08e51ae5SMike Rapoport (Microsoft) if (!sz) 542*08e51ae5SMike Rapoport (Microsoft) continue; 543*08e51ae5SMike Rapoport (Microsoft) settings->nr_hugepages[i] = hugetlb_nr_pages(sz); 544*08e51ae5SMike Rapoport (Microsoft) } 545*08e51ae5SMike Rapoport (Microsoft) 546*08e51ae5SMike Rapoport (Microsoft) settings->nr_sizes = nr_sizes; 547*08e51ae5SMike Rapoport (Microsoft) hugetlb_settings_saved = true; 548*08e51ae5SMike Rapoport (Microsoft) } 549*08e51ae5SMike Rapoport (Microsoft) 550*08e51ae5SMike Rapoport (Microsoft) void hugetlb_restore_settings(void) 551*08e51ae5SMike Rapoport (Microsoft) { 552*08e51ae5SMike Rapoport (Microsoft) struct hugetlb_settings *settings = &hugetlb_saved_settings; 553*08e51ae5SMike Rapoport (Microsoft) 554*08e51ae5SMike Rapoport (Microsoft) if (!hugetlb_settings_saved || !settings->default_size) 555*08e51ae5SMike Rapoport (Microsoft) return; 556*08e51ae5SMike Rapoport (Microsoft) 557*08e51ae5SMike Rapoport (Microsoft) for (int i = 0; i < HUGETLB_MAX_NR_PAGESIZES; i++) { 558*08e51ae5SMike Rapoport (Microsoft) unsigned long sz = settings->sizes[i]; 559*08e51ae5SMike Rapoport (Microsoft) 560*08e51ae5SMike Rapoport (Microsoft) if (!sz) 561*08e51ae5SMike Rapoport (Microsoft) continue; 562*08e51ae5SMike Rapoport (Microsoft) 563*08e51ae5SMike Rapoport (Microsoft) hugetlb_set_nr_pages(sz, settings->nr_hugepages[i]); 564*08e51ae5SMike Rapoport (Microsoft) } 565*08e51ae5SMike Rapoport (Microsoft) } 566*08e51ae5SMike Rapoport (Microsoft) 567*08e51ae5SMike Rapoport (Microsoft) static void hugepage_restore_settings_atexit(void) 568*08e51ae5SMike Rapoport (Microsoft) { 569*08e51ae5SMike Rapoport (Microsoft) if (thp_settings_saved) 570*08e51ae5SMike Rapoport (Microsoft) thp_restore_settings(); 571*08e51ae5SMike Rapoport (Microsoft) if (hugetlb_settings_saved) 572*08e51ae5SMike Rapoport (Microsoft) hugetlb_restore_settings(); 573*08e51ae5SMike Rapoport (Microsoft) } 574*08e51ae5SMike Rapoport (Microsoft) 575*08e51ae5SMike Rapoport (Microsoft) static void hugepage_restore_settings_sighandler(int sig) 576*08e51ae5SMike Rapoport (Microsoft) { 577*08e51ae5SMike Rapoport (Microsoft) /* exit() will invoke the hugepage_restore_settings_atexit handler. */ 578*08e51ae5SMike Rapoport (Microsoft) exit(KSFT_FAIL); 579*08e51ae5SMike Rapoport (Microsoft) } 580*08e51ae5SMike Rapoport (Microsoft) 581*08e51ae5SMike Rapoport (Microsoft) void hugepage_save_settings(bool thp, bool hugetlb) 582*08e51ae5SMike Rapoport (Microsoft) { 583*08e51ae5SMike Rapoport (Microsoft) if (!thp && !hugetlb) 584*08e51ae5SMike Rapoport (Microsoft) return; 585*08e51ae5SMike Rapoport (Microsoft) 586*08e51ae5SMike Rapoport (Microsoft) if (thp) 587*08e51ae5SMike Rapoport (Microsoft) __thp_save_settings(); 588*08e51ae5SMike Rapoport (Microsoft) if (hugetlb) 589*08e51ae5SMike Rapoport (Microsoft) __hugetlb_save_settings(); 590*08e51ae5SMike Rapoport (Microsoft) 591*08e51ae5SMike Rapoport (Microsoft) /* 592*08e51ae5SMike Rapoport (Microsoft) * setup exit hooks to make sure THP and HugeTLB settings are 593*08e51ae5SMike Rapoport (Microsoft) * restored on graceful and error exits and signals 594*08e51ae5SMike Rapoport (Microsoft) */ 595*08e51ae5SMike Rapoport (Microsoft) atexit(hugepage_restore_settings_atexit); 596*08e51ae5SMike Rapoport (Microsoft) signal(SIGTERM, hugepage_restore_settings_sighandler); 597*08e51ae5SMike Rapoport (Microsoft) signal(SIGINT, hugepage_restore_settings_sighandler); 598*08e51ae5SMike Rapoport (Microsoft) signal(SIGHUP, hugepage_restore_settings_sighandler); 599*08e51ae5SMike Rapoport (Microsoft) signal(SIGQUIT, hugepage_restore_settings_sighandler); 600*08e51ae5SMike Rapoport (Microsoft) } 601