xref: /linux/tools/testing/selftests/futex/functional/futex_numa_mpol.c (revision 0fc8f6200d2313278fbf4539bbab74677c685531)
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 #ifdef LIBNUMA_VER_SUFFICIENT
14 #include <numa.h>
15 #include <numaif.h>
16 #endif
17 
18 #include <linux/futex.h>
19 #include <sys/mman.h>
20 
21 #include "futextest.h"
22 #include "futex2test.h"
23 #include "kselftest_harness.h"
24 
25 #define MAX_THREADS	64
26 
27 static pthread_barrier_t barrier_main;
28 static pthread_t threads[MAX_THREADS];
29 
30 struct thread_args {
31 	void *futex_ptr;
32 	unsigned int flags;
33 	int result;
34 };
35 
36 static struct thread_args thread_args[MAX_THREADS];
37 
38 #ifndef FUTEX_NO_NODE
39 #define FUTEX_NO_NODE (-1)
40 #endif
41 
42 #ifndef FUTEX2_MPOL
43 #define FUTEX2_MPOL	0x08
44 #endif
45 
46 static void *thread_lock_fn(void *arg)
47 {
48 	struct thread_args *args = arg;
49 	int ret;
50 
51 	pthread_barrier_wait(&barrier_main);
52 	ret = futex2_wait(args->futex_ptr, 0, args->flags, NULL, 0);
53 	args->result = ret;
54 	return NULL;
55 }
56 
57 static void create_max_threads(void *futex_ptr)
58 {
59 	int i, ret;
60 
61 	for (i = 0; i < MAX_THREADS; i++) {
62 		thread_args[i].futex_ptr = futex_ptr;
63 		thread_args[i].flags = FUTEX2_SIZE_U32 | FUTEX_PRIVATE_FLAG | FUTEX2_NUMA;
64 		thread_args[i].result = 0;
65 		ret = pthread_create(&threads[i], NULL, thread_lock_fn, &thread_args[i]);
66 		if (ret)
67 			ksft_exit_fail_msg("pthread_create failed\n");
68 	}
69 }
70 
71 static void join_max_threads(void)
72 {
73 	int i, ret;
74 
75 	for (i = 0; i < MAX_THREADS; i++) {
76 		ret = pthread_join(threads[i], NULL);
77 		if (ret)
78 			ksft_exit_fail_msg("pthread_join failed for thread %d\n", i);
79 	}
80 }
81 
82 static void __test_futex(void *futex_ptr, int err_value, unsigned int futex_flags)
83 {
84 	int to_wake, ret, i, need_exit = 0;
85 
86 	pthread_barrier_init(&barrier_main, NULL, MAX_THREADS + 1);
87 	create_max_threads(futex_ptr);
88 	pthread_barrier_wait(&barrier_main);
89 	to_wake = MAX_THREADS;
90 
91 	do {
92 		ret = futex2_wake(futex_ptr, to_wake, futex_flags);
93 
94 		if (err_value) {
95 			if (ret >= 0)
96 				ksft_exit_fail_msg("futex2_wake(%d, 0x%x) should fail, but didn't\n",
97 						   to_wake, futex_flags);
98 
99 			if (errno != err_value)
100 				ksft_exit_fail_msg("futex2_wake(%d, 0x%x) expected error was %d, but returned %d (%s)\n",
101 						   to_wake, futex_flags, err_value, errno, strerror(errno));
102 
103 			break;
104 		}
105 		if (ret < 0) {
106 			ksft_exit_fail_msg("Failed futex2_wake(%d, 0x%x): %m\n",
107 					   to_wake, futex_flags);
108 		}
109 		if (!ret)
110 			usleep(50);
111 		to_wake -= ret;
112 
113 	} while (to_wake);
114 	join_max_threads();
115 
116 	for (i = 0; i < MAX_THREADS; i++) {
117 		if (err_value && thread_args[i].result != -1) {
118 			ksft_print_msg("Thread %d should fail but succeeded (%d)\n",
119 				       i, thread_args[i].result);
120 			need_exit = 1;
121 		}
122 		if (!err_value && thread_args[i].result != 0) {
123 			ksft_print_msg("Thread %d failed (%d)\n", i, thread_args[i].result);
124 			need_exit = 1;
125 		}
126 	}
127 	if (need_exit)
128 		ksft_exit_fail_msg("Aborting due to earlier errors.\n");
129 }
130 
131 static void test_futex(void *futex_ptr, int err_value)
132 {
133 	__test_futex(futex_ptr, err_value, FUTEX2_SIZE_U32 | FUTEX_PRIVATE_FLAG | FUTEX2_NUMA);
134 }
135 
136 TEST(futex_numa_mpol)
137 {
138 	struct futex32_numa *futex_numa;
139 	void *futex_ptr;
140 	int mem_size;
141 
142 	mem_size = sysconf(_SC_PAGE_SIZE);
143 	futex_ptr = mmap(NULL, mem_size * 2, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
144 	if (futex_ptr == MAP_FAILED)
145 		ksft_exit_fail_msg("mmap() for %d bytes failed\n", mem_size);
146 
147 	/* Create an invalid memory region for the "Memory out of range" test */
148 	mprotect(futex_ptr + mem_size, mem_size, PROT_NONE);
149 
150 	futex_numa = futex_ptr;
151 
152 	ksft_print_msg("Regular test\n");
153 	futex_numa->futex = 0;
154 	futex_numa->numa = FUTEX_NO_NODE;
155 	test_futex(futex_ptr, 0);
156 
157 	if (futex_numa->numa == FUTEX_NO_NODE)
158 		ksft_exit_fail_msg("NUMA node is left uninitialized\n");
159 
160 	/* FUTEX2_NUMA futex must be 8-byte aligned */
161 	ksft_print_msg("Mis-aligned futex\n");
162 	test_futex(futex_ptr + mem_size - 4, EINVAL);
163 
164 	ksft_print_msg("Memory out of range\n");
165 	test_futex(futex_ptr + mem_size, EFAULT);
166 
167 	futex_numa->numa = FUTEX_NO_NODE;
168 	mprotect(futex_ptr, mem_size, PROT_READ);
169 	ksft_print_msg("Memory, RO\n");
170 	test_futex(futex_ptr, EFAULT);
171 
172 	mprotect(futex_ptr, mem_size, PROT_NONE);
173 	ksft_print_msg("Memory, no access\n");
174 	test_futex(futex_ptr, EFAULT);
175 
176 	mprotect(futex_ptr, mem_size, PROT_READ | PROT_WRITE);
177 	ksft_print_msg("Memory back to RW\n");
178 	test_futex(futex_ptr, 0);
179 
180 	ksft_test_result_pass("futex2 memory boundary tests passed\n");
181 
182 	/* MPOL test. Does not work as expected */
183 #ifdef LIBNUMA_VER_SUFFICIENT
184 	for (int i = 0; i < 4; i++) {
185 		unsigned long nodemask;
186 		int ret;
187 
188 		nodemask = 1 << i;
189 		ret = mbind(futex_ptr, mem_size, MPOL_BIND, &nodemask,
190 			    sizeof(nodemask) * 8, 0);
191 		if (ret == 0) {
192 			ret = numa_set_mempolicy_home_node(futex_ptr, mem_size, i, 0);
193 			if (ret != 0)
194 				ksft_exit_fail_msg("Failed to set home node: %m, %d\n", errno);
195 
196 			ksft_print_msg("Node %d test\n", i);
197 			futex_numa->futex = 0;
198 			futex_numa->numa = FUTEX_NO_NODE;
199 
200 			ret = futex2_wake(futex_ptr, 0, FUTEX2_SIZE_U32 | FUTEX_PRIVATE_FLAG | FUTEX2_NUMA | FUTEX2_MPOL);
201 			if (ret < 0)
202 				ksft_test_result_fail("Failed to wake 0 with MPOL: %m\n");
203 			if (futex_numa->numa != i) {
204 				ksft_exit_fail_msg("Returned NUMA node is %d expected %d\n",
205 						   futex_numa->numa, i);
206 			}
207 		}
208 	}
209 	ksft_test_result_pass("futex2 MPOL hints test passed\n");
210 #else
211 	ksft_test_result_skip("futex2 MPOL hints test requires libnuma 2.0.18+\n");
212 #endif
213 	munmap(futex_ptr, mem_size * 2);
214 }
215 
216 TEST_HARNESS_MAIN
217