xref: /linux/tools/testing/selftests/mm/hugetlb_fault_after_madv.c (revision 7f71507851fc7764b36a3221839607d3a45c2025)
1 // SPDX-License-Identifier: GPL-2.0
2 #include <pthread.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <sys/mman.h>
6 #include <sys/types.h>
7 #include <unistd.h>
8 #include <setjmp.h>
9 #include <signal.h>
10 
11 #include "vm_util.h"
12 #include "../kselftest.h"
13 
14 #define INLOOP_ITER 100
15 
16 static char *huge_ptr;
17 static size_t huge_page_size;
18 
19 static sigjmp_buf sigbuf;
20 static bool sigbus_triggered;
21 
22 static void signal_handler(int signal)
23 {
24 	if (signal == SIGBUS) {
25 		sigbus_triggered = true;
26 		siglongjmp(sigbuf, 1);
27 	}
28 }
29 
30 /* Touch the memory while it is being madvised() */
31 void *touch(void *unused)
32 {
33 	char *ptr = (char *)huge_ptr;
34 
35 	if (sigsetjmp(sigbuf, 1))
36 		return NULL;
37 
38 	for (int i = 0; i < INLOOP_ITER; i++)
39 		ptr[0] = '.';
40 
41 	return NULL;
42 }
43 
44 void *madv(void *unused)
45 {
46 	usleep(rand() % 10);
47 
48 	for (int i = 0; i < INLOOP_ITER; i++)
49 		madvise(huge_ptr, huge_page_size, MADV_DONTNEED);
50 
51 	return NULL;
52 }
53 
54 int main(void)
55 {
56 	unsigned long free_hugepages;
57 	pthread_t thread1, thread2;
58 	/*
59 	 * On kernel 6.4, we are able to reproduce the problem with ~1000
60 	 * interactions
61 	 */
62 	int max = 10000;
63 	int err;
64 
65 	ksft_print_header();
66 	ksft_set_plan(1);
67 
68 	srand(getpid());
69 
70 	if (signal(SIGBUS, signal_handler) == SIG_ERR)
71 		ksft_exit_skip("Could not register signal handler.");
72 
73 	huge_page_size = default_huge_page_size();
74 	if (!huge_page_size)
75 		ksft_exit_skip("Could not detect default hugetlb page size.");
76 
77 	ksft_print_msg("[INFO] detected default hugetlb page size: %zu KiB\n",
78 		       huge_page_size / 1024);
79 
80 	free_hugepages = get_free_hugepages();
81 	if (free_hugepages != 1) {
82 		ksft_exit_skip("This test needs one and only one page to execute. Got %lu\n",
83 			       free_hugepages);
84 	}
85 
86 	while (max--) {
87 		huge_ptr = mmap(NULL, huge_page_size, PROT_READ | PROT_WRITE,
88 				MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB,
89 				-1, 0);
90 
91 		if ((unsigned long)huge_ptr == -1)
92 			ksft_exit_skip("Failed to allocated huge page\n");
93 
94 		pthread_create(&thread1, NULL, madv, NULL);
95 		pthread_create(&thread2, NULL, touch, NULL);
96 
97 		pthread_join(thread1, NULL);
98 		pthread_join(thread2, NULL);
99 		munmap(huge_ptr, huge_page_size);
100 	}
101 
102 	ksft_test_result(!sigbus_triggered, "SIGBUS behavior\n");
103 
104 	err = ksft_get_fail_cnt();
105 	if (err)
106 		ksft_exit_fail_msg("%d out of %d tests failed\n",
107 				   err, ksft_test_num());
108 	ksft_exit_pass();
109 }
110