Skip to content

Commit 3163369

Browse files
Sebastian Andrzej SiewiorPeter Zijlstra
authored andcommitted
selftests/futex: Add futex_numa_mpol
Test the basic functionality for the NUMA and MPOL flags: - FUTEX2_NUMA should take the NUMA node which is after the uaddr and use it. - Only update the node if FUTEX_NO_NODE was set by the user - FUTEX2_MPOL should use the memory based on the policy. I attempted to set the node with mbind() and then use this with MPOL but this fails and futex falls back to the default node for the current CPU. Signed-off-by: Sebastian Andrzej Siewior <[email protected]> Signed-off-by: Peter Zijlstra (Intel) <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent cda95fa commit 3163369

File tree

5 files changed

+290
-1
lines changed

5 files changed

+290
-1
lines changed

tools/testing/selftests/futex/functional/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# SPDX-License-Identifier: GPL-2.0-only
2+
futex_numa_mpol
23
futex_priv_hash
34
futex_requeue
45
futex_requeue_pi

tools/testing/selftests/futex/functional/Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# SPDX-License-Identifier: GPL-2.0
22
INCLUDES := -I../include -I../../ $(KHDR_INCLUDES)
33
CFLAGS := $(CFLAGS) -g -O2 -Wall -pthread $(INCLUDES) $(KHDR_INCLUDES)
4-
LDLIBS := -lpthread -lrt
4+
LDLIBS := -lpthread -lrt -lnuma
55

66
LOCAL_HDRS := \
77
../include/futextest.h \
@@ -18,6 +18,7 @@ TEST_GEN_PROGS := \
1818
futex_wait \
1919
futex_requeue \
2020
futex_priv_hash \
21+
futex_numa_mpol \
2122
futex_waitv
2223

2324
TEST_PROGS := run.sh
Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
/*
3+
* Copyright (C) 2025 Sebastian Andrzej Siewior <[email protected]>
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+
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+
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+
error("pthread_create failed\n", errno);
66+
exit(1);
67+
}
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+
error("pthread_join failed for thread %d\n", errno, i);
79+
exit(1);
80+
}
81+
}
82+
}
83+
84+
static void __test_futex(void *futex_ptr, int must_fail, unsigned int futex_flags)
85+
{
86+
int to_wake, ret, i, need_exit = 0;
87+
88+
pthread_barrier_init(&barrier_main, NULL, MAX_THREADS + 1);
89+
create_max_threads(futex_ptr);
90+
pthread_barrier_wait(&barrier_main);
91+
to_wake = MAX_THREADS;
92+
93+
do {
94+
ret = futex2_wake(futex_ptr, to_wake, futex_flags);
95+
if (must_fail) {
96+
if (ret < 0)
97+
break;
98+
fail("Should fail, but didn't\n");
99+
exit(1);
100+
}
101+
if (ret < 0) {
102+
error("Failed futex2_wake(%d)\n", errno, to_wake);
103+
exit(1);
104+
}
105+
if (!ret)
106+
usleep(50);
107+
to_wake -= ret;
108+
109+
} while (to_wake);
110+
join_max_threads();
111+
112+
for (i = 0; i < MAX_THREADS; i++) {
113+
if (must_fail && thread_args[i].result != -1) {
114+
fail("Thread %d should fail but succeeded (%d)\n", i, thread_args[i].result);
115+
need_exit = 1;
116+
}
117+
if (!must_fail && thread_args[i].result != 0) {
118+
fail("Thread %d failed (%d)\n", i, thread_args[i].result);
119+
need_exit = 1;
120+
}
121+
}
122+
if (need_exit)
123+
exit(1);
124+
}
125+
126+
static void test_futex(void *futex_ptr, int must_fail)
127+
{
128+
__test_futex(futex_ptr, must_fail, FUTEX2_SIZE_U32 | FUTEX_PRIVATE_FLAG | FUTEX2_NUMA);
129+
}
130+
131+
static void test_futex_mpol(void *futex_ptr, int must_fail)
132+
{
133+
__test_futex(futex_ptr, must_fail, FUTEX2_SIZE_U32 | FUTEX_PRIVATE_FLAG | FUTEX2_NUMA | FUTEX2_MPOL);
134+
}
135+
136+
static void usage(char *prog)
137+
{
138+
printf("Usage: %s\n", prog);
139+
printf(" -c Use color\n");
140+
printf(" -h Display this help message\n");
141+
printf(" -v L Verbosity level: %d=QUIET %d=CRITICAL %d=INFO\n",
142+
VQUIET, VCRITICAL, VINFO);
143+
}
144+
145+
int main(int argc, char *argv[])
146+
{
147+
struct futex32_numa *futex_numa;
148+
int mem_size, i;
149+
void *futex_ptr;
150+
char c;
151+
152+
while ((c = getopt(argc, argv, "chv:")) != -1) {
153+
switch (c) {
154+
case 'c':
155+
log_color(1);
156+
break;
157+
case 'h':
158+
usage(basename(argv[0]));
159+
exit(0);
160+
break;
161+
case 'v':
162+
log_verbosity(atoi(optarg));
163+
break;
164+
default:
165+
usage(basename(argv[0]));
166+
exit(1);
167+
}
168+
}
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+
error("mmap() for %d bytes failed\n", errno, mem_size);
174+
return 1;
175+
}
176+
futex_numa = futex_ptr;
177+
178+
info("Regular test\n");
179+
futex_numa->futex = 0;
180+
futex_numa->numa = FUTEX_NO_NODE;
181+
test_futex(futex_ptr, 0);
182+
183+
if (futex_numa->numa == FUTEX_NO_NODE) {
184+
fail("NUMA node is left unitiliazed\n");
185+
return 1;
186+
}
187+
188+
info("Memory too small\n");
189+
test_futex(futex_ptr + mem_size - 4, 1);
190+
191+
info("Memory out of range\n");
192+
test_futex(futex_ptr + mem_size, 1);
193+
194+
futex_numa->numa = FUTEX_NO_NODE;
195+
mprotect(futex_ptr, mem_size, PROT_READ);
196+
info("Memory, RO\n");
197+
test_futex(futex_ptr, 1);
198+
199+
mprotect(futex_ptr, mem_size, PROT_NONE);
200+
info("Memory, no access\n");
201+
test_futex(futex_ptr, 1);
202+
203+
mprotect(futex_ptr, mem_size, PROT_READ | PROT_WRITE);
204+
info("Memory back to RW\n");
205+
test_futex(futex_ptr, 0);
206+
207+
/* MPOL test. Does not work as expected */
208+
for (i = 0; i < 4; i++) {
209+
unsigned long nodemask;
210+
int ret;
211+
212+
nodemask = 1 << i;
213+
ret = mbind(futex_ptr, mem_size, MPOL_BIND, &nodemask,
214+
sizeof(nodemask) * 8, 0);
215+
if (ret == 0) {
216+
info("Node %d test\n", i);
217+
futex_numa->futex = 0;
218+
futex_numa->numa = FUTEX_NO_NODE;
219+
220+
ret = futex2_wake(futex_ptr, 0, FUTEX2_SIZE_U32 | FUTEX_PRIVATE_FLAG | FUTEX2_NUMA | FUTEX2_MPOL);
221+
if (ret < 0)
222+
error("Failed to wake 0 with MPOL.\n", errno);
223+
if (0)
224+
test_futex_mpol(futex_numa, 0);
225+
if (futex_numa->numa != i) {
226+
fail("Returned NUMA node is %d expected %d\n",
227+
futex_numa->numa, i);
228+
}
229+
}
230+
}
231+
return 0;
232+
}

tools/testing/selftests/futex/functional/run.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,3 +86,6 @@ echo
8686
echo
8787
./futex_priv_hash $COLOR
8888
./futex_priv_hash -g $COLOR
89+
90+
echo
91+
./futex_numa_mpol $COLOR

tools/testing/selftests/futex/include/futex2test.h

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,43 @@ struct futex_waitv {
1818
};
1919
#endif
2020

21+
#ifndef __NR_futex_wake
22+
#define __NR_futex_wake 454
23+
#endif
24+
25+
#ifndef __NR_futex_wait
26+
#define __NR_futex_wait 455
27+
#endif
28+
2129
#ifndef FUTEX2_SIZE_U32
2230
#define FUTEX2_SIZE_U32 0x02
2331
#endif
2432

33+
#ifndef FUTEX2_NUMA
34+
#define FUTEX2_NUMA 0x04
35+
#endif
36+
37+
#ifndef FUTEX2_MPOL
38+
#define FUTEX2_MPOL 0x08
39+
#endif
40+
41+
#ifndef FUTEX2_PRIVATE
42+
#define FUTEX2_PRIVATE FUTEX_PRIVATE_FLAG
43+
#endif
44+
45+
#ifndef FUTEX2_NO_NODE
46+
#define FUTEX_NO_NODE (-1)
47+
#endif
48+
2549
#ifndef FUTEX_32
2650
#define FUTEX_32 FUTEX2_SIZE_U32
2751
#endif
2852

53+
struct futex32_numa {
54+
futex_t futex;
55+
futex_t numa;
56+
};
57+
2958
/**
3059
* futex_waitv - Wait at multiple futexes, wake on any
3160
* @waiters: Array of waiters
@@ -38,3 +67,26 @@ static inline int futex_waitv(volatile struct futex_waitv *waiters, unsigned lon
3867
{
3968
return syscall(__NR_futex_waitv, waiters, nr_waiters, flags, timo, clockid);
4069
}
70+
71+
/*
72+
* futex_wait() - block on uaddr with optional timeout
73+
* @val: Expected value
74+
* @flags: FUTEX2 flags
75+
* @timeout: Relative timeout
76+
* @clockid: Clock id for the timeout
77+
*/
78+
static inline int futex2_wait(void *uaddr, long val, unsigned int flags,
79+
struct timespec *timeout, clockid_t clockid)
80+
{
81+
return syscall(__NR_futex_wait, uaddr, val, ~0U, flags, timeout, clockid);
82+
}
83+
84+
/*
85+
* futex2_wake() - Wake a number of futexes
86+
* @nr: Number of threads to wake at most
87+
* @flags: FUTEX2 flags
88+
*/
89+
static inline int futex2_wake(void *uaddr, int nr, unsigned int flags)
90+
{
91+
return syscall(__NR_futex_wake, uaddr, ~0U, nr, flags);
92+
}

0 commit comments

Comments
 (0)