1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (C) 2025 Sebastian Andrzej Siewior <bigeasy@linutronix.de>
4 */
5
6 #define _GNU_SOURCE
7
8 #include <errno.h>
9 #include <pthread.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <unistd.h>
13 #include <numa.h>
14 #include <numaif.h>
15
16 #include <linux/futex.h>
17 #include <sys/mman.h>
18
19 #include "logging.h"
20 #include "futextest.h"
21 #include "futex2test.h"
22
23 #define MAX_THREADS 64
24
25 static pthread_barrier_t barrier_main;
26 static pthread_t threads[MAX_THREADS];
27
28 struct thread_args {
29 void *futex_ptr;
30 unsigned int flags;
31 int result;
32 };
33
34 static struct thread_args thread_args[MAX_THREADS];
35
36 #ifndef FUTEX_NO_NODE
37 #define FUTEX_NO_NODE (-1)
38 #endif
39
40 #ifndef FUTEX2_MPOL
41 #define FUTEX2_MPOL 0x08
42 #endif
43
thread_lock_fn(void * arg)44 static void *thread_lock_fn(void *arg)
45 {
46 struct thread_args *args = arg;
47 int ret;
48
49 pthread_barrier_wait(&barrier_main);
50 ret = futex2_wait(args->futex_ptr, 0, args->flags, NULL, 0);
51 args->result = ret;
52 return NULL;
53 }
54
create_max_threads(void * futex_ptr)55 static void create_max_threads(void *futex_ptr)
56 {
57 int i, ret;
58
59 for (i = 0; i < MAX_THREADS; i++) {
60 thread_args[i].futex_ptr = futex_ptr;
61 thread_args[i].flags = FUTEX2_SIZE_U32 | FUTEX_PRIVATE_FLAG | FUTEX2_NUMA;
62 thread_args[i].result = 0;
63 ret = pthread_create(&threads[i], NULL, thread_lock_fn, &thread_args[i]);
64 if (ret)
65 ksft_exit_fail_msg("pthread_create failed\n");
66 }
67 }
68
join_max_threads(void)69 static void join_max_threads(void)
70 {
71 int i, ret;
72
73 for (i = 0; i < MAX_THREADS; i++) {
74 ret = pthread_join(threads[i], NULL);
75 if (ret)
76 ksft_exit_fail_msg("pthread_join failed for thread %d\n", i);
77 }
78 }
79
__test_futex(void * futex_ptr,int must_fail,unsigned int futex_flags)80 static void __test_futex(void *futex_ptr, int must_fail, unsigned int futex_flags)
81 {
82 int to_wake, ret, i, need_exit = 0;
83
84 pthread_barrier_init(&barrier_main, NULL, MAX_THREADS + 1);
85 create_max_threads(futex_ptr);
86 pthread_barrier_wait(&barrier_main);
87 to_wake = MAX_THREADS;
88
89 do {
90 ret = futex2_wake(futex_ptr, to_wake, futex_flags);
91 if (must_fail) {
92 if (ret < 0)
93 break;
94 ksft_exit_fail_msg("futex2_wake(%d, 0x%x) should fail, but didn't\n",
95 to_wake, futex_flags);
96 }
97 if (ret < 0) {
98 ksft_exit_fail_msg("Failed futex2_wake(%d, 0x%x): %m\n",
99 to_wake, futex_flags);
100 }
101 if (!ret)
102 usleep(50);
103 to_wake -= ret;
104
105 } while (to_wake);
106 join_max_threads();
107
108 for (i = 0; i < MAX_THREADS; i++) {
109 if (must_fail && thread_args[i].result != -1) {
110 ksft_print_msg("Thread %d should fail but succeeded (%d)\n",
111 i, thread_args[i].result);
112 need_exit = 1;
113 }
114 if (!must_fail && thread_args[i].result != 0) {
115 ksft_print_msg("Thread %d failed (%d)\n", i, thread_args[i].result);
116 need_exit = 1;
117 }
118 }
119 if (need_exit)
120 ksft_exit_fail_msg("Aborting due to earlier errors.\n");
121 }
122
test_futex(void * futex_ptr,int must_fail)123 static void test_futex(void *futex_ptr, int must_fail)
124 {
125 __test_futex(futex_ptr, must_fail, FUTEX2_SIZE_U32 | FUTEX_PRIVATE_FLAG | FUTEX2_NUMA);
126 }
127
test_futex_mpol(void * futex_ptr,int must_fail)128 static void test_futex_mpol(void *futex_ptr, int must_fail)
129 {
130 __test_futex(futex_ptr, must_fail, FUTEX2_SIZE_U32 | FUTEX_PRIVATE_FLAG | FUTEX2_NUMA | FUTEX2_MPOL);
131 }
132
usage(char * prog)133 static void usage(char *prog)
134 {
135 printf("Usage: %s\n", prog);
136 printf(" -c Use color\n");
137 printf(" -h Display this help message\n");
138 printf(" -v L Verbosity level: %d=QUIET %d=CRITICAL %d=INFO\n",
139 VQUIET, VCRITICAL, VINFO);
140 }
141
main(int argc,char * argv[])142 int main(int argc, char *argv[])
143 {
144 struct futex32_numa *futex_numa;
145 int mem_size, i;
146 void *futex_ptr;
147 int c;
148
149 while ((c = getopt(argc, argv, "chv:")) != -1) {
150 switch (c) {
151 case 'c':
152 log_color(1);
153 break;
154 case 'h':
155 usage(basename(argv[0]));
156 exit(0);
157 break;
158 case 'v':
159 log_verbosity(atoi(optarg));
160 break;
161 default:
162 usage(basename(argv[0]));
163 exit(1);
164 }
165 }
166
167 ksft_print_header();
168 ksft_set_plan(1);
169
170 mem_size = sysconf(_SC_PAGE_SIZE);
171 futex_ptr = mmap(NULL, mem_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
172 if (futex_ptr == MAP_FAILED)
173 ksft_exit_fail_msg("mmap() for %d bytes failed\n", mem_size);
174
175 futex_numa = futex_ptr;
176
177 ksft_print_msg("Regular test\n");
178 futex_numa->futex = 0;
179 futex_numa->numa = FUTEX_NO_NODE;
180 test_futex(futex_ptr, 0);
181
182 if (futex_numa->numa == FUTEX_NO_NODE)
183 ksft_exit_fail_msg("NUMA node is left uninitialized\n");
184
185 ksft_print_msg("Memory too small\n");
186 test_futex(futex_ptr + mem_size - 4, 1);
187
188 ksft_print_msg("Memory out of range\n");
189 test_futex(futex_ptr + mem_size, 1);
190
191 futex_numa->numa = FUTEX_NO_NODE;
192 mprotect(futex_ptr, mem_size, PROT_READ);
193 ksft_print_msg("Memory, RO\n");
194 test_futex(futex_ptr, 1);
195
196 mprotect(futex_ptr, mem_size, PROT_NONE);
197 ksft_print_msg("Memory, no access\n");
198 test_futex(futex_ptr, 1);
199
200 mprotect(futex_ptr, mem_size, PROT_READ | PROT_WRITE);
201 ksft_print_msg("Memory back to RW\n");
202 test_futex(futex_ptr, 0);
203
204 /* MPOL test. Does not work as expected */
205 for (i = 0; i < 4; i++) {
206 unsigned long nodemask;
207 int ret;
208
209 nodemask = 1 << i;
210 ret = mbind(futex_ptr, mem_size, MPOL_BIND, &nodemask,
211 sizeof(nodemask) * 8, 0);
212 if (ret == 0) {
213 ret = numa_set_mempolicy_home_node(futex_ptr, mem_size, i, 0);
214 if (ret != 0)
215 ksft_exit_fail_msg("Failed to set home node: %m, %d\n", errno);
216
217 ksft_print_msg("Node %d test\n", i);
218 futex_numa->futex = 0;
219 futex_numa->numa = FUTEX_NO_NODE;
220
221 ret = futex2_wake(futex_ptr, 0, FUTEX2_SIZE_U32 | FUTEX_PRIVATE_FLAG | FUTEX2_NUMA | FUTEX2_MPOL);
222 if (ret < 0)
223 ksft_test_result_fail("Failed to wake 0 with MPOL: %m\n");
224 if (0)
225 test_futex_mpol(futex_numa, 0);
226 if (futex_numa->numa != i) {
227 ksft_exit_fail_msg("Returned NUMA node is %d expected %d\n",
228 futex_numa->numa, i);
229 }
230 }
231 }
232 ksft_test_result_pass("NUMA MPOL tests passed\n");
233 ksft_finished();
234 return 0;
235 }
236