thp_write_settings(&saved_settings);
}
-static void thp_restore_settings_atexit(void)
+static void __thp_save_settings(void)
{
- thp_restore_settings();
-}
+ if (!thp_available())
+ return;
-static void thp_restore_settings_sighandler(int sig)
-{
- /* exit() will invoke the thp_restore_settings_atexit handler. */
- exit(KSFT_FAIL);
-}
+ if (thp_settings_saved)
+ return;
-void thp_save_settings(void)
-{
thp_read_settings(&saved_settings);
thp_settings_saved = true;
-
- /*
- * setup exit hooks to make sure THP settings are restored on graceful
- * and error exits and signals
- */
- atexit(thp_restore_settings_atexit);
- signal(SIGTERM, thp_restore_settings_sighandler);
- signal(SIGINT, thp_restore_settings_sighandler);
- signal(SIGHUP, thp_restore_settings_sighandler);
- signal(SIGQUIT, thp_restore_settings_sighandler);
}
void thp_set_read_ahead_path(char *path)
return mode == 1 || mode == 3;
}
+#define HUGETLB_MAX_NR_PAGESIZES 10
+struct hugetlb_settings {
+ unsigned long nr_hugepages[HUGETLB_MAX_NR_PAGESIZES];
+ unsigned long sizes[HUGETLB_MAX_NR_PAGESIZES];
+ unsigned long default_size;
+ int nr_sizes;
+};
+
+static struct hugetlb_settings hugetlb_saved_settings;
+static bool hugetlb_settings_saved;
+
int detect_hugetlb_page_sizes(unsigned long sizes[], int max)
{
- DIR *dir = opendir("/sys/kernel/mm/hugepages/");
+ static struct hugetlb_settings *settings = &hugetlb_saved_settings;
+ DIR *dir;
int count = 0;
+ if (settings->nr_sizes) {
+ if (settings->nr_sizes < max)
+ max = settings->nr_sizes;
+ for (count = 0; count < max; count++)
+ sizes[count] = settings->sizes[count];
+ return count;
+ }
+
+ dir = opendir("/sys/kernel/mm/hugepages/");
if (!dir)
return 0;
unsigned long default_huge_page_size(void)
{
+ static struct hugetlb_settings *settings = &hugetlb_saved_settings;
unsigned long hps = 0;
char *line = NULL;
size_t linelen = 0;
- FILE *f = fopen("/proc/meminfo", "r");
+ FILE *f;
+
+ if (settings->default_size)
+ return settings->default_size;
+ f = fopen("/proc/meminfo", "r");
if (!f)
return 0;
while (getline(&line, &linelen, f) > 0) {
return read_num(path);
}
+
+static bool __hugetlb_setup(unsigned long size, unsigned long nr)
+{
+ unsigned long free = hugetlb_free_pages(size);
+ unsigned long total = hugetlb_nr_pages(size);
+
+ if (free >= nr)
+ return true;
+
+ hugetlb_set_nr_pages(size, total + (nr - free));
+
+ return hugetlb_free_pages(size) >= nr;
+}
+
+bool hugetlb_setup_default(unsigned long nr)
+{
+ unsigned long size;
+
+ hugetlb_save_settings();
+ size = default_huge_page_size();
+ if (!size)
+ return false;
+
+ return __hugetlb_setup(size, nr);
+}
+
+bool hugetlb_setup_default_exact(unsigned long nr)
+{
+ unsigned long size;
+
+ hugetlb_save_settings();
+ size = default_huge_page_size();
+ if (!size)
+ return false;
+
+ hugetlb_set_nr_pages(size, nr);
+
+ return hugetlb_free_pages(size) == nr;
+}
+
+unsigned long hugetlb_setup(unsigned long nr, unsigned long sizes[],
+ int max)
+{
+ unsigned long enabled[10];
+ int nr_sizes = 0;
+ int nr_enabled;
+
+ hugetlb_save_settings();
+
+ nr_enabled = detect_hugetlb_page_sizes(enabled, ARRAY_SIZE(enabled));
+ if (!nr_enabled)
+ return 0;
+
+ if (nr_enabled > max) {
+ ksft_print_msg("detected %d huge page sizes, will only test %d\n", nr_enabled, max);
+ nr_enabled = max;
+ }
+
+ /* request nr HugeTLB pages of every size. */
+ for (int i = 0; i < nr_enabled; i++) {
+ if (!__hugetlb_setup(enabled[i], nr))
+ continue;
+ sizes[nr_sizes++] = enabled[i];
+ }
+
+ return nr_sizes;
+}
+
+static void __hugetlb_save_settings(void)
+{
+ struct hugetlb_settings *settings = &hugetlb_saved_settings;
+ int nr_sizes;
+
+ if (hugetlb_settings_saved)
+ return;
+
+ settings->default_size = default_huge_page_size();
+ if (!settings->default_size)
+ return;
+
+ nr_sizes = detect_hugetlb_page_sizes(settings->sizes,
+ HUGETLB_MAX_NR_PAGESIZES);
+ if (!nr_sizes) {
+ settings->default_size = 0;
+ return;
+ }
+
+ for (int i = 0; i < nr_sizes; i++) {
+ unsigned long sz = settings->sizes[i];
+
+ if (!sz)
+ continue;
+ settings->nr_hugepages[i] = hugetlb_nr_pages(sz);
+ }
+
+ settings->nr_sizes = nr_sizes;
+ hugetlb_settings_saved = true;
+}
+
+void hugetlb_restore_settings(void)
+{
+ struct hugetlb_settings *settings = &hugetlb_saved_settings;
+
+ if (!hugetlb_settings_saved || !settings->default_size)
+ return;
+
+ for (int i = 0; i < HUGETLB_MAX_NR_PAGESIZES; i++) {
+ unsigned long sz = settings->sizes[i];
+
+ if (!sz)
+ continue;
+
+ hugetlb_set_nr_pages(sz, settings->nr_hugepages[i]);
+ }
+}
+
+static void hugepage_restore_settings_atexit(void)
+{
+ if (thp_settings_saved)
+ thp_restore_settings();
+ if (hugetlb_settings_saved)
+ hugetlb_restore_settings();
+}
+
+static void hugepage_restore_settings_sighandler(int sig)
+{
+ /* exit() will invoke the hugepage_restore_settings_atexit handler. */
+ exit(KSFT_FAIL);
+}
+
+void hugepage_save_settings(bool thp, bool hugetlb)
+{
+ if (!thp && !hugetlb)
+ return;
+
+ if (thp)
+ __thp_save_settings();
+ if (hugetlb)
+ __hugetlb_save_settings();
+
+ /*
+ * setup exit hooks to make sure THP and HugeTLB settings are
+ * restored on graceful and error exits and signals
+ */
+ atexit(hugepage_restore_settings_atexit);
+ signal(SIGTERM, hugepage_restore_settings_sighandler);
+ signal(SIGINT, hugepage_restore_settings_sighandler);
+ signal(SIGHUP, hugepage_restore_settings_sighandler);
+ signal(SIGQUIT, hugepage_restore_settings_sighandler);
+}
#include <stddef.h>
#include <stdint.h>
+void hugepage_save_settings(bool thp, bool hugetlb);
+
/* Transparent Huge Pages (THP) */
enum thp_enabled {
void thp_push_settings(struct thp_settings *settings);
void thp_pop_settings(void);
void thp_restore_settings(void);
-void thp_save_settings(void);
+
+static inline void thp_save_settings(void)
+{
+ hugepage_save_settings(/* thp = */ true, /* hugetlb = */ false);
+}
void thp_set_read_ahead_path(char *path);
unsigned long thp_supported_orders(void);
void hugetlb_set_nr_pages(unsigned long size, unsigned long nr);
unsigned long hugetlb_free_pages(unsigned long size);
+static inline void hugetlb_save_settings(void)
+{
+ hugepage_save_settings(/* thp = */ false, /* hugetlb = */ true);
+}
+
+void hugetlb_restore_settings(void);
+
static inline unsigned long hugetlb_nr_default_pages(void)
{
unsigned long size = default_huge_page_size();
return hugetlb_free_pages(size);
}
+static inline bool hugetlb_available(void)
+{
+ return default_huge_page_size() != 0;
+}
+
+bool hugetlb_setup_default(unsigned long nr);
+bool hugetlb_setup_default_exact(unsigned long nr);
+unsigned long hugetlb_setup(unsigned long nr, unsigned long sizes[],
+ int max);
+
+#define HUGETLB_SETUP_DEFAULT_PAGES(nr_pages) \
+static void __attribute__((constructor)) __hugetlb_setup_default(void) \
+{ \
+ hugetlb_setup_default((nr_pages)); \
+}
+
#endif /* __HUGEPAGE_SETTINGS_H__ */