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 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 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 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 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 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