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 "futextest.h"
20 #include "futex2test.h"
21 #include "../../kselftest_harness.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 err_value,unsigned int futex_flags)80 static void __test_futex(void *futex_ptr, int err_value, 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
92 if (err_value) {
93 if (ret >= 0)
94 ksft_exit_fail_msg("futex2_wake(%d, 0x%x) should fail, but didn't\n",
95 to_wake, futex_flags);
96
97 if (errno != err_value)
98 ksft_exit_fail_msg("futex2_wake(%d, 0x%x) expected error was %d, but returned %d (%s)\n",
99 to_wake, futex_flags, err_value, errno, strerror(errno));
100
101 break;
102 }
103 if (ret < 0) {
104 ksft_exit_fail_msg("Failed futex2_wake(%d, 0x%x): %m\n",
105 to_wake, futex_flags);
106 }
107 if (!ret)
108 usleep(50);
109 to_wake -= ret;
110
111 } while (to_wake);
112 join_max_threads();
113
114 for (i = 0; i < MAX_THREADS; i++) {
115 if (err_value && thread_args[i].result != -1) {
116 ksft_print_msg("Thread %d should fail but succeeded (%d)\n",
117 i, thread_args[i].result);
118 need_exit = 1;
119 }
120 if (!err_value && thread_args[i].result != 0) {
121 ksft_print_msg("Thread %d failed (%d)\n", i, thread_args[i].result);
122 need_exit = 1;
123 }
124 }
125 if (need_exit)
126 ksft_exit_fail_msg("Aborting due to earlier errors.\n");
127 }
128
test_futex(void * futex_ptr,int err_value)129 static void test_futex(void *futex_ptr, int err_value)
130 {
131 __test_futex(futex_ptr, err_value, FUTEX2_SIZE_U32 | FUTEX_PRIVATE_FLAG | FUTEX2_NUMA);
132 }
133
test_futex_mpol(void * futex_ptr,int err_value)134 static void test_futex_mpol(void *futex_ptr, int err_value)
135 {
136 __test_futex(futex_ptr, err_value, FUTEX2_SIZE_U32 | FUTEX_PRIVATE_FLAG | FUTEX2_NUMA | FUTEX2_MPOL);
137 }
138
TEST(futex_numa_mpol)139 TEST(futex_numa_mpol)
140 {
141 struct futex32_numa *futex_numa;
142 void *futex_ptr;
143 int mem_size;
144
145 mem_size = sysconf(_SC_PAGE_SIZE);
146 futex_ptr = mmap(NULL, mem_size * 2, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
147 if (futex_ptr == MAP_FAILED)
148 ksft_exit_fail_msg("mmap() for %d bytes failed\n", mem_size);
149
150 /* Create an invalid memory region for the "Memory out of range" test */
151 mprotect(futex_ptr + mem_size, mem_size, PROT_NONE);
152
153 futex_numa = futex_ptr;
154
155 ksft_print_msg("Regular test\n");
156 futex_numa->futex = 0;
157 futex_numa->numa = FUTEX_NO_NODE;
158 test_futex(futex_ptr, 0);
159
160 if (futex_numa->numa == FUTEX_NO_NODE)
161 ksft_exit_fail_msg("NUMA node is left uninitialized\n");
162
163 /* FUTEX2_NUMA futex must be 8-byte aligned */
164 ksft_print_msg("Mis-aligned futex\n");
165 test_futex(futex_ptr + mem_size - 4, EINVAL);
166
167 ksft_print_msg("Memory out of range\n");
168 test_futex(futex_ptr + mem_size, EFAULT);
169
170 futex_numa->numa = FUTEX_NO_NODE;
171 mprotect(futex_ptr, mem_size, PROT_READ);
172 ksft_print_msg("Memory, RO\n");
173 test_futex(futex_ptr, EFAULT);
174
175 mprotect(futex_ptr, mem_size, PROT_NONE);
176 ksft_print_msg("Memory, no access\n");
177 test_futex(futex_ptr, EFAULT);
178
179 mprotect(futex_ptr, mem_size, PROT_READ | PROT_WRITE);
180 ksft_print_msg("Memory back to RW\n");
181 test_futex(futex_ptr, 0);
182
183 ksft_test_result_pass("futex2 memory boundary tests passed\n");
184
185 /* MPOL test. Does not work as expected */
186 #ifdef LIBNUMA_VER_SUFFICIENT
187 for (int i = 0; i < 4; i++) {
188 unsigned long nodemask;
189 int ret;
190
191 nodemask = 1 << i;
192 ret = mbind(futex_ptr, mem_size, MPOL_BIND, &nodemask,
193 sizeof(nodemask) * 8, 0);
194 if (ret == 0) {
195 ret = numa_set_mempolicy_home_node(futex_ptr, mem_size, i, 0);
196 if (ret != 0)
197 ksft_exit_fail_msg("Failed to set home node: %m, %d\n", errno);
198
199 ksft_print_msg("Node %d test\n", i);
200 futex_numa->futex = 0;
201 futex_numa->numa = FUTEX_NO_NODE;
202
203 ret = futex2_wake(futex_ptr, 0, FUTEX2_SIZE_U32 | FUTEX_PRIVATE_FLAG | FUTEX2_NUMA | FUTEX2_MPOL);
204 if (ret < 0)
205 ksft_test_result_fail("Failed to wake 0 with MPOL: %m\n");
206 if (futex_numa->numa != i) {
207 ksft_exit_fail_msg("Returned NUMA node is %d expected %d\n",
208 futex_numa->numa, i);
209 }
210 }
211 }
212 ksft_test_result_pass("futex2 MPOL hints test passed\n");
213 #else
214 ksft_test_result_skip("futex2 MPOL hints test requires libnuma 2.0.16+\n");
215 #endif
216 munmap(futex_ptr, mem_size * 2);
217 }
218
219 TEST_HARNESS_MAIN
220