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 82CAD37DEBB; Mon, 6 Apr 2026 14:19:42 +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=1775485182; cv=none; b=snJ3oQXGJQtU3Ys+TxlToh1snTnPDdVN4WBZFTILZUVvxWMvDsq31LZj0oB91aq9esqggPfErMbXSGqTDUs+XmXHBjxGWzIzK5UaYmh+zf1hfPamNZr6T/1kuNrziLT4vUNTX/Ml3IbzLFQdC50dCqzUsT3ksnxVZfolYGKXp1U= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775485182; c=relaxed/simple; bh=CSRWIbnqZ2+IUD8e7KEy7LOUD0pEzBaiEkzcv/mT4lU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=GS+T6z4A+zLtNRVZk8t2cfU0nEKeBgOeKzvDi1HiR+ooH/02MLTNiojWwRIX/QC2CHm2prtEZX/h8h477hIKKut2UUItHWXbBf2YQuKI4sQJccz1HHzrAqcsCts/q0qPaZ0piHbRl5YJdoEiN6/BxAwlKMRTtmokDE6/PysjRVM= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=WkkzDwRF; 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="WkkzDwRF" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 09315C4CEF7; Mon, 6 Apr 2026 14:19:36 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1775485182; bh=CSRWIbnqZ2+IUD8e7KEy7LOUD0pEzBaiEkzcv/mT4lU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=WkkzDwRFh22fX0emoHfuxUNPut6dxYmqhLmgjOy4hBGeBJLq8J2htWFvJlocgrmpd 71lH2/zYqfL4U+/u4tJ4vi/n1leSLbw4uNd32Dn8kfmxFU1tjGDWaKzB/6R/OBzM2E Dap6EDkqFQzKCf4paMgOcse5gtbtjmbUEYt/I3APkwuTdNX+Rvs26R20pA7u456bzW TGdk4F9BOLisA6ZJEHelNZtP55sWJ7JHlcaWyzQDVV783ziK89JHIWuf/7FHJw2kQ9 2yX31+BbsSP1dcD9aJClS+aR+yxs9UNT4thnfq7nnYNiGBdrujppI6l1z5ttljvcd0 ZSGAXoKOl/t6w== From: Mike Rapoport To: Andrew Morton , David Hildenbrand Cc: Baolin Wang , Barry Song , Dev Jain , Jason Gunthorpe , John Hubbard , "Liam R. Howlett" , Lance Yang , Leon Romanovsky , Lorenzo Stoakes , Mark Brown , Michal Hocko , Mike Rapoport , Nico Pache , Peter Xu , Ryan Roberts , Shuah Khan , Suren Baghdasaryan , Vlastimil Babka , Zi Yan , linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-mm@kvack.org Subject: [PATCH 20/53] selftests/mm: add atexit() and signal handlers to thp_settings Date: Mon, 6 Apr 2026 17:17:02 +0300 Message-ID: <20260406141735.2179309-21-rppt@kernel.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260406141735.2179309-1-rppt@kernel.org> References: <20260406141735.2179309-1-rppt@kernel.org> Precedence: bulk X-Mailing-List: linux-kselftest@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit From: "Mike Rapoport (Microsoft)" khugepaged registers atexit() and signal handlers that ensure that THP settings are restored regardless of how the test exited. Make these handlers available for all users of thp_settings. The call to thp_save_settings() installs thp_restore_settings as the atexit() callback and makes sure that signals that kill a process would still call exit() and atexit() callback. Update child process in tests using thp_settings to use _exit() instead of exit() to avoid altering THP settings in the middle of a test. Signed-off-by: Mike Rapoport (Microsoft) --- tools/testing/selftests/mm/cow.c | 21 +++++------ tools/testing/selftests/mm/khugepaged.c | 40 +++------------------ tools/testing/selftests/mm/thp_settings.c | 28 ++++++++++++++- tools/testing/selftests/mm/uffd-wp-mremap.c | 4 --- 4 files changed, 39 insertions(+), 54 deletions(-) diff --git a/tools/testing/selftests/mm/cow.c b/tools/testing/selftests/mm/cow.c index d9c69c04b67d..6abdcb30aba8 100644 --- a/tools/testing/selftests/mm/cow.c +++ b/tools/testing/selftests/mm/cow.c @@ -202,7 +202,7 @@ static void do_test_cow_in_parent(char *mem, size_t size, bool do_mprotect, log_test_result(KSFT_FAIL); goto close_comm_pipes; } else if (!ret) { - exit(fn(mem, size, &comm_pipes)); + _exit(fn(mem, size, &comm_pipes)); } while (read(comm_pipes.child_ready[0], &buf, 1) != 1) @@ -333,7 +333,7 @@ static void do_test_vmsplice_in_parent(char *mem, size_t size, ; /* Modify page content in the child. */ memset(mem, 0xff, size); - exit(0); + _exit(0); } if (!before_fork) { @@ -480,7 +480,7 @@ static void do_test_iouring(char *mem, size_t size, bool use_fork) write(comm_pipes.child_ready[1], "0", 1); while (read(comm_pipes.parent_ready[0], &buf, 1) != 1) ; - exit(0); + _exit(0); } while (read(comm_pipes.child_ready[0], &buf, 1) != 1) @@ -645,7 +645,7 @@ static void do_test_ro_pin(char *mem, size_t size, enum ro_pin_test test, write(comm_pipes.child_ready[1], "0", 1); while (read(comm_pipes.parent_ready[0], &buf, 1) != 1) ; - exit(0); + _exit(0); } /* Wait until our child is ready. */ @@ -956,7 +956,7 @@ static void do_run_with_thp(test_fn fn, enum thp_run thp_run, size_t thpsize) log_test_result(KSFT_FAIL); goto munmap; } else if (!ret) { - exit(0); + _exit(0); } wait(&ret); /* Allow for sharing all pages again. */ @@ -1347,13 +1347,13 @@ static void do_test_anon_thp_collapse(char *mem, size_t size, switch (test) { case ANON_THP_COLLAPSE_UNSHARED: case ANON_THP_COLLAPSE_FULLY_SHARED: - exit(child_memcmp_fn(mem, size, &comm_pipes)); + _exit(child_memcmp_fn(mem, size, &comm_pipes)); break; case ANON_THP_COLLAPSE_LOWER_SHARED: - exit(child_memcmp_fn(mem, size / 2, &comm_pipes)); + _exit(child_memcmp_fn(mem, size / 2, &comm_pipes)); break; case ANON_THP_COLLAPSE_UPPER_SHARED: - exit(child_memcmp_fn(mem + size / 2, size / 2, + _exit(child_memcmp_fn(mem + size / 2, size / 2, &comm_pipes)); break; default: @@ -1911,10 +1911,5 @@ int main(int argc, char **argv) run_anon_thp_test_cases(); run_non_anon_test_cases(); - if (pmdsize) { - /* Only if THP is supported. */ - thp_restore_settings(); - } - ksft_finished(); } diff --git a/tools/testing/selftests/mm/khugepaged.c b/tools/testing/selftests/mm/khugepaged.c index ba0a9e14e600..fac11b7e7443 100644 --- a/tools/testing/selftests/mm/khugepaged.c +++ b/tools/testing/selftests/mm/khugepaged.c @@ -72,7 +72,6 @@ struct file_info { }; static struct file_info finfo; -static bool skip_settings_restore; static int exit_status; static void success(const char *msg) @@ -92,25 +91,6 @@ static void skip(const char *msg) exit_status = KSFT_SKIP; } -static void restore_settings_atexit(void) -{ - if (skip_settings_restore) - return; - - ksft_print_msg("Restore THP and khugepaged settings..."); - thp_restore_settings(); - success("OK"); - - skip_settings_restore = true; - ksft_finished(); -} - -static void restore_settings(int sig) -{ - /* exit() will invoke the restore_settings_atexit handler. */ - exit(sig ? KSFT_FAIL : exit_status); -} - static void save_settings(void) { ksft_print_msg("Save THP and khugepaged settings..."); @@ -119,12 +99,6 @@ static void save_settings(void) thp_save_settings(); success("OK"); - - atexit(restore_settings_atexit); - signal(SIGTERM, restore_settings); - signal(SIGINT, restore_settings); - signal(SIGHUP, restore_settings); - signal(SIGQUIT, restore_settings); } static void get_finfo(const char *dir) @@ -840,8 +814,6 @@ static void collapse_fork(struct collapse_context *c, struct mem_ops *ops) ksft_print_msg("Share small page over fork()..."); if (!fork()) { /* Do not touch settings on child exit */ - skip_settings_restore = true; - if (ops->check_huge(p, 0)) success("OK"); else @@ -853,7 +825,7 @@ static void collapse_fork(struct collapse_context *c, struct mem_ops *ops) validate_memory(p, 0, page_size); ops->cleanup_area(p, hpage_pmd_size); - exit(exit_status); + _exit(exit_status); } wait(&wstatus); @@ -878,8 +850,6 @@ static void collapse_fork_compound(struct collapse_context *c, struct mem_ops *o ksft_print_msg("Share huge page over fork()..."); if (!fork()) { /* Do not touch settings on child exit */ - skip_settings_restore = true; - if (ops->check_huge(p, 1)) success("OK"); else @@ -902,7 +872,7 @@ static void collapse_fork_compound(struct collapse_context *c, struct mem_ops *o validate_memory(p, 0, hpage_pmd_size); ops->cleanup_area(p, hpage_pmd_size); - exit(exit_status); + _exit(exit_status); } wait(&wstatus); @@ -928,8 +898,6 @@ static void collapse_max_ptes_shared(struct collapse_context *c, struct mem_ops ksft_print_msg("Share huge page over fork()..."); if (!fork()) { /* Do not touch settings on child exit */ - skip_settings_restore = true; - if (ops->check_huge(p, 1)) success("OK"); else @@ -962,7 +930,7 @@ static void collapse_max_ptes_shared(struct collapse_context *c, struct mem_ops validate_memory(p, 0, hpage_pmd_size); ops->cleanup_area(p, hpage_pmd_size); - exit(exit_status); + _exit(exit_status); } wait(&wstatus); @@ -1258,5 +1226,5 @@ int main(int argc, char **argv) t->fn(t->ctx, t->ops); } - restore_settings(0); + ksft_finished(); } diff --git a/tools/testing/selftests/mm/thp_settings.c b/tools/testing/selftests/mm/thp_settings.c index e748ebfb3d4e..f38ba8a27b30 100644 --- a/tools/testing/selftests/mm/thp_settings.c +++ b/tools/testing/selftests/mm/thp_settings.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #include #include +#include #include #include #include @@ -15,6 +16,7 @@ static struct thp_settings settings_stack[MAX_SETTINGS_DEPTH]; static int settings_index; static struct thp_settings saved_settings; static char dev_queue_read_ahead_path[PATH_MAX]; +static bool thp_settings_saved; static const char * const thp_enabled_strings[] = { "never", @@ -298,12 +300,36 @@ void thp_pop_settings(void) void thp_restore_settings(void) { - thp_write_settings(&saved_settings); + if (thp_settings_saved) + thp_write_settings(&saved_settings); +} + +static void thp_restore_settings_atexit(void) +{ + thp_restore_settings(); +} + +static void thp_restore_settings_sighandler(int sig) +{ + /* exit() will invoke the thp_restore_settings_atexit handler. */ + exit(KSFT_FAIL); } void thp_save_settings(void) { thp_read_settings(&saved_settings); + + /* + * 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); + + thp_settings_saved = true; } void thp_set_read_ahead_path(char *path) diff --git a/tools/testing/selftests/mm/uffd-wp-mremap.c b/tools/testing/selftests/mm/uffd-wp-mremap.c index 17186d4a4147..516c35d236e1 100644 --- a/tools/testing/selftests/mm/uffd-wp-mremap.c +++ b/tools/testing/selftests/mm/uffd-wp-mremap.c @@ -368,10 +368,6 @@ int main(int argc, char **argv) tc->swapout, tc->hugetlb); } - /* If THP is supported, restore original THP settings. */ - if (nr_thpsizes) - thp_restore_settings(); - i = ksft_get_fail_cnt(); if (i) ksft_exit_fail_msg("%d out of %d tests failed\n", -- 2.53.0