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