// SPDX-License-Identifier: GPL-2.0 #include #include #include #include #include #include #include #include #include "vm_util.h" #include "../kselftest.h" #define INLOOP_ITER 100 static char *huge_ptr; static size_t huge_page_size; static sigjmp_buf sigbuf; static bool sigbus_triggered; static void signal_handler(int signal) { if (signal == SIGBUS) { sigbus_triggered = true; siglongjmp(sigbuf, 1); } } /* Touch the memory while it is being madvised() */ void *touch(void *unused) { char *ptr = (char *)huge_ptr; if (sigsetjmp(sigbuf, 1)) return NULL; for (int i = 0; i < INLOOP_ITER; i++) ptr[0] = '.'; return NULL; } void *madv(void *unused) { usleep(rand() % 10); for (int i = 0; i < INLOOP_ITER; i++) madvise(huge_ptr, huge_page_size, MADV_DONTNEED); return NULL; } int main(void) { unsigned long free_hugepages; pthread_t thread1, thread2; /* * On kernel 6.4, we are able to reproduce the problem with ~1000 * interactions */ int max = 10000; int err; ksft_print_header(); ksft_set_plan(1); srand(getpid()); if (signal(SIGBUS, signal_handler) == SIG_ERR) ksft_exit_skip("Could not register signal handler."); huge_page_size = default_huge_page_size(); if (!huge_page_size) ksft_exit_skip("Could not detect default hugetlb page size."); ksft_print_msg("[INFO] detected default hugetlb page size: %zu KiB\n", huge_page_size / 1024); free_hugepages = get_free_hugepages(); if (free_hugepages != 1) { ksft_exit_skip("This test needs one and only one page to execute. Got %lu\n", free_hugepages); } while (max--) { huge_ptr = mmap(NULL, huge_page_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, -1, 0); if ((unsigned long)huge_ptr == -1) ksft_exit_skip("Failed to allocated huge page\n"); pthread_create(&thread1, NULL, madv, NULL); pthread_create(&thread2, NULL, touch, NULL); pthread_join(thread1, NULL); pthread_join(thread2, NULL); munmap(huge_ptr, huge_page_size); } ksft_test_result(!sigbus_triggered, "SIGBUS behavior\n"); err = ksft_get_fail_cnt(); if (err) ksft_exit_fail_msg("%d out of %d tests failed\n", err, ksft_test_num()); ksft_exit_pass(); }