Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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 <bigeasy@linutronix.de>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lore.kernel.org/r/20250416162921.513656-22-bigeasy@linutronix.de

authored by

Sebastian Andrzej Siewior and committed by
Peter Zijlstra
31633694 cda95fae

+290 -1
+1
tools/testing/selftests/futex/functional/.gitignore
··· 1 1 # SPDX-License-Identifier: GPL-2.0-only 2 + futex_numa_mpol 2 3 futex_priv_hash 3 4 futex_requeue 4 5 futex_requeue_pi
+2 -1
tools/testing/selftests/futex/functional/Makefile
··· 1 1 # SPDX-License-Identifier: GPL-2.0 2 2 INCLUDES := -I../include -I../../ $(KHDR_INCLUDES) 3 3 CFLAGS := $(CFLAGS) -g -O2 -Wall -pthread $(INCLUDES) $(KHDR_INCLUDES) 4 - LDLIBS := -lpthread -lrt 4 + LDLIBS := -lpthread -lrt -lnuma 5 5 6 6 LOCAL_HDRS := \ 7 7 ../include/futextest.h \ ··· 18 18 futex_wait \ 19 19 futex_requeue \ 20 20 futex_priv_hash \ 21 + futex_numa_mpol \ 21 22 futex_waitv 22 23 23 24 TEST_PROGS := run.sh
+232
tools/testing/selftests/futex/functional/futex_numa_mpol.c
··· 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 + 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 + }
+3
tools/testing/selftests/futex/functional/run.sh
··· 86 86 echo 87 87 ./futex_priv_hash $COLOR 88 88 ./futex_priv_hash -g $COLOR 89 + 90 + echo 91 + ./futex_numa_mpol $COLOR
+52
tools/testing/selftests/futex/include/futex2test.h
··· 18 18 }; 19 19 #endif 20 20 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 + 21 29 #ifndef FUTEX2_SIZE_U32 22 30 #define FUTEX2_SIZE_U32 0x02 31 + #endif 32 + 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) 23 47 #endif 24 48 25 49 #ifndef FUTEX_32 26 50 #define FUTEX_32 FUTEX2_SIZE_U32 27 51 #endif 52 + 53 + struct futex32_numa { 54 + futex_t futex; 55 + futex_t numa; 56 + }; 28 57 29 58 /** 30 59 * futex_waitv - Wait at multiple futexes, wake on any ··· 66 37 unsigned long flags, struct timespec *timo, clockid_t clockid) 67 38 { 68 39 return syscall(__NR_futex_waitv, waiters, nr_waiters, flags, timo, clockid); 40 + } 41 + 42 + /* 43 + * futex_wait() - block on uaddr with optional timeout 44 + * @val: Expected value 45 + * @flags: FUTEX2 flags 46 + * @timeout: Relative timeout 47 + * @clockid: Clock id for the timeout 48 + */ 49 + static inline int futex2_wait(void *uaddr, long val, unsigned int flags, 50 + struct timespec *timeout, clockid_t clockid) 51 + { 52 + return syscall(__NR_futex_wait, uaddr, val, ~0U, flags, timeout, clockid); 53 + } 54 + 55 + /* 56 + * futex2_wake() - Wake a number of futexes 57 + * @nr: Number of threads to wake at most 58 + * @flags: FUTEX2 flags 59 + */ 60 + static inline int futex2_wake(void *uaddr, int nr, unsigned int flags) 61 + { 62 + return syscall(__NR_futex_wake, uaddr, ~0U, nr, flags); 69 63 }