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 ksft_exit_fail_msg("pthread_create failed\n"); 66 } 67 } 68 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 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 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 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 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 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