From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5511043C05F; Mon, 11 May 2026 16:31:53 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778517113; cv=none; b=iZluLgRn5/oWpcLU+8AlC2Ibq1Z8y0WrxTllVTApOn7fy1HyEa5AYMkT8PHl4zAECe+pjF9ysSJxNmjoCwoSU6BuNQWn0l9WmddD/Swmy8LmGskV1EeAC8mEUNldLlya+6SDyh8W1wpp4LWV9g/Dy1xuIUwhu0xVxeJxQwA0fSQ= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778517113; c=relaxed/simple; bh=CnmardlqPvE7LgOIfn5JKBi/nFUBjeb2JAZaSRqTuWE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=R0Gtr3Z2sNj1lXRdOSbQQxJSW97CET8fNEftbDKsoZ8AUJ9Td/DrlLPN7HEEZGDpHr2D4OJmx+hIGSU9tZ+chHwnVYWo5rgJelj+gBN/tbxnqHvtbeb+hy/dUeAgEkXXfqcuB6tCFc7JKjHYmgwTTbHisZR8vJQ/cjrhX1p60l0= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=HqEENZyA; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="HqEENZyA" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 13D03C2BCC9; Mon, 11 May 2026 16:31:46 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1778517113; bh=CnmardlqPvE7LgOIfn5JKBi/nFUBjeb2JAZaSRqTuWE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=HqEENZyAuLwkRNYG0uKKcfP95/KqKl/OvoZJjFYv/qO+8Vj94jOMRDKwKOfwZBTOR 1BTOHLU8dN/u5/OdyBX5vIeCMBGi1sKx9fMQaLX1olSBXpaVWg9vK9bCDxuIA87PQo 7lIO8lvY1C85jyNM582acvHchRDopt0mOu0pAcaKV3Yg75Sw09Q6BUfK+hd4sz6fOv PVnGyflETzXNBHvxsW3eEGd4rW1RqK3VawCGDO/BMC4/XY9p3I8P9C6mBvSgXHSF/n /E9q6BLDQ7iLM6WlD18dyGm8PJRCYosJNYg2sqRzAZYbjv1yyviZqwGwAQkZNGDQ2l UL3vbrnnOv04w== From: Mike Rapoport To: Andrew Morton , David Hildenbrand Cc: Baolin Wang , Barry Song , Dev Jain , Donet Tom , Jason Gunthorpe , John Hubbard , "Liam R. Howlett" , Lance Yang , Li Wang , Leon Romanovsky , Lorenzo Stoakes , Luiz Capitulino , Mark Brown , Michal Hocko , Mike Rapoport , Nico Pache , Peter Xu , Ryan Roberts , Sarthak Sharma , Shuah Khan , Suren Baghdasaryan , Vlastimil Babka , Zi Yan , linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-mm@kvack.org Subject: [PATCH v4 28/55] selftests/mm: hugepage_settings: add APIs for HugeTLB setup and teardown Date: Mon, 11 May 2026 19:28:12 +0300 Message-ID: <20260511162840.375890-29-rppt@kernel.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260511162840.375890-1-rppt@kernel.org> References: <20260511162840.375890-1-rppt@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit From: "Mike Rapoport (Microsoft)" A lot of tests require free HugeTLB pages. Some need just a few default huge pages, some need a certain amount of memory available as HugeTLB, and some just skip lots of tests if huge pages of all supported sizes are not available. This all resulted in a huge mess in run_vmtests.sh that sets up some huge pages, adjusts them later and restores some of the settings if the stars align. Add APIs that allow saving the state of HugeTLB and setting up the desired amount of HugeTLB pages. Saving the state also registers atexit() callback and signal handler that will ensure restoration of HugeTLB state. Since many tests use both HugeTLB and THP, the atexit() callbacks and signal handler are restoring both. For kselftest_harness tests that run fixture setups and test in child processes add a constructor that will save and restore settings in the main process. Tested-by: Luiz Capitulino Tested-by: Sarthak Sharma Signed-off-by: Mike Rapoport (Microsoft) --- .../testing/selftests/mm/hugepage_settings.c | 205 ++++++++++++++++-- .../testing/selftests/mm/hugepage_settings.h | 31 ++- 2 files changed, 213 insertions(+), 23 deletions(-) diff --git a/tools/testing/selftests/mm/hugepage_settings.c b/tools/testing/selftests/mm/hugepage_settings.c index 3c944d28d14a..01a557f372d1 100644 --- a/tools/testing/selftests/mm/hugepage_settings.c +++ b/tools/testing/selftests/mm/hugepage_settings.c @@ -306,31 +306,16 @@ void thp_restore_settings(void) 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) @@ -398,11 +383,32 @@ bool thp_is_enabled(void) 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; @@ -426,11 +432,16 @@ int detect_hugetlb_page_sizes(unsigned long sizes[], int max) 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) { @@ -478,3 +489,153 @@ unsigned long hugetlb_free_pages(unsigned long size) 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); +} diff --git a/tools/testing/selftests/mm/hugepage_settings.h b/tools/testing/selftests/mm/hugepage_settings.h index 436f4ce02984..c07722b7f102 100644 --- a/tools/testing/selftests/mm/hugepage_settings.h +++ b/tools/testing/selftests/mm/hugepage_settings.h @@ -6,6 +6,8 @@ #include #include +void hugepage_save_settings(bool thp, bool hugetlb); + /* Transparent Huge Pages (THP) */ enum thp_enabled { @@ -79,7 +81,11 @@ struct thp_settings *thp_current_settings(void); 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); @@ -97,6 +103,13 @@ unsigned long hugetlb_nr_pages(unsigned long size); 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(); @@ -127,4 +140,20 @@ static inline unsigned long hugetlb_free_default_pages(void) 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__ */ -- 2.53.0