1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * futex_waitv() test by André Almeida <andrealmeid@collabora.com> 4 * 5 * Copyright 2021 Collabora Ltd. 6 */ 7 8 #include <errno.h> 9 #include <error.h> 10 #include <getopt.h> 11 #include <stdio.h> 12 #include <stdlib.h> 13 #include <string.h> 14 #include <time.h> 15 #include <pthread.h> 16 #include <stdint.h> 17 #include <sys/shm.h> 18 19 #include "futextest.h" 20 #include "futex2test.h" 21 #include "kselftest_harness.h" 22 23 #define WAKE_WAIT_US 10000 24 #define NR_FUTEXES 30 25 static struct futex_waitv waitv[NR_FUTEXES]; 26 u_int32_t futexes[NR_FUTEXES] = {0}; 27 28 void *waiterfn(void *arg) 29 { 30 struct timespec to; 31 int res; 32 33 /* setting absolute timeout for futex2 */ 34 if (clock_gettime(CLOCK_MONOTONIC, &to)) 35 ksft_exit_fail_msg("gettime64 failed\n"); 36 37 to.tv_sec++; 38 39 res = futex_waitv(waitv, NR_FUTEXES, 0, &to, CLOCK_MONOTONIC); 40 if (res < 0) { 41 ksft_test_result_fail("futex_waitv returned: %d %s\n", 42 errno, strerror(errno)); 43 } else if (res != NR_FUTEXES - 1) { 44 ksft_test_result_fail("futex_waitv returned: %d, expecting %d\n", 45 res, NR_FUTEXES - 1); 46 } 47 48 return NULL; 49 } 50 51 TEST(private_waitv) 52 { 53 pthread_t waiter; 54 int res, i; 55 56 for (i = 0; i < NR_FUTEXES; i++) { 57 waitv[i].uaddr = (uintptr_t)&futexes[i]; 58 waitv[i].flags = FUTEX_32 | FUTEX_PRIVATE_FLAG; 59 waitv[i].val = 0; 60 waitv[i].__reserved = 0; 61 } 62 63 /* Private waitv */ 64 if (pthread_create(&waiter, NULL, waiterfn, NULL)) 65 ksft_exit_fail_msg("pthread_create failed\n"); 66 67 usleep(WAKE_WAIT_US); 68 69 res = futex_wake(u64_to_ptr(waitv[NR_FUTEXES - 1].uaddr), 1, FUTEX_PRIVATE_FLAG); 70 if (res != 1) { 71 ksft_test_result_fail("futex_wake private returned: %d %s\n", 72 res ? errno : res, 73 res ? strerror(errno) : ""); 74 } else { 75 ksft_test_result_pass("futex_waitv private\n"); 76 } 77 } 78 79 TEST(shared_waitv) 80 { 81 pthread_t waiter; 82 int res, i; 83 84 /* Shared waitv */ 85 for (i = 0; i < NR_FUTEXES; i++) { 86 int shm_id = shmget(IPC_PRIVATE, 4096, IPC_CREAT | 0666); 87 88 if (shm_id < 0) { 89 if (errno == ENOSYS) 90 ksft_exit_skip("shmget syscall not supported\n"); 91 perror("shmget"); 92 exit(1); 93 } 94 95 unsigned int *shared_data = shmat(shm_id, NULL, 0); 96 97 *shared_data = 0; 98 waitv[i].uaddr = (uintptr_t)shared_data; 99 waitv[i].flags = FUTEX_32; 100 waitv[i].val = 0; 101 waitv[i].__reserved = 0; 102 } 103 104 if (pthread_create(&waiter, NULL, waiterfn, NULL)) 105 ksft_exit_fail_msg("pthread_create failed\n"); 106 107 usleep(WAKE_WAIT_US); 108 109 res = futex_wake(u64_to_ptr(waitv[NR_FUTEXES - 1].uaddr), 1, 0); 110 if (res != 1) { 111 ksft_test_result_fail("futex_wake shared returned: %d %s\n", 112 res ? errno : res, 113 res ? strerror(errno) : ""); 114 } else { 115 ksft_test_result_pass("futex_waitv shared\n"); 116 } 117 118 for (i = 0; i < NR_FUTEXES; i++) 119 shmdt(u64_to_ptr(waitv[i].uaddr)); 120 } 121 122 TEST(invalid_flag) 123 { 124 struct timespec to; 125 int res; 126 127 /* Testing a waiter without FUTEX_32 flag */ 128 waitv[0].flags = FUTEX_PRIVATE_FLAG; 129 130 if (clock_gettime(CLOCK_MONOTONIC, &to)) 131 ksft_exit_fail_msg("gettime64 failed\n"); 132 133 to.tv_sec++; 134 135 res = futex_waitv(waitv, NR_FUTEXES, 0, &to, CLOCK_MONOTONIC); 136 if (res == EINVAL) { 137 ksft_test_result_fail("futex_waitv private returned: %d %s\n", 138 res ? errno : res, 139 res ? strerror(errno) : ""); 140 } else { 141 ksft_test_result_pass("futex_waitv without FUTEX_32\n"); 142 } 143 } 144 145 TEST(unaligned_address) 146 { 147 struct timespec to; 148 int res; 149 150 /* Testing a waiter with an unaligned address */ 151 waitv[0].flags = FUTEX_PRIVATE_FLAG | FUTEX_32; 152 waitv[0].uaddr = 1; 153 154 if (clock_gettime(CLOCK_MONOTONIC, &to)) 155 ksft_exit_fail_msg("gettime64 failed\n"); 156 157 to.tv_sec++; 158 159 res = futex_waitv(waitv, NR_FUTEXES, 0, &to, CLOCK_MONOTONIC); 160 if (res == EINVAL) { 161 ksft_test_result_fail("futex_wake private returned: %d %s\n", 162 res ? errno : res, 163 res ? strerror(errno) : ""); 164 } else { 165 ksft_test_result_pass("futex_waitv with an unaligned address\n"); 166 } 167 } 168 169 TEST(null_address) 170 { 171 struct timespec to; 172 int res; 173 174 /* Testing a NULL address for waiters.uaddr */ 175 waitv[0].uaddr = 0x00000000; 176 177 if (clock_gettime(CLOCK_MONOTONIC, &to)) 178 ksft_exit_fail_msg("gettime64 failed\n"); 179 180 to.tv_sec++; 181 182 res = futex_waitv(waitv, NR_FUTEXES, 0, &to, CLOCK_MONOTONIC); 183 if (res == EINVAL) { 184 ksft_test_result_fail("futex_waitv private returned: %d %s\n", 185 res ? errno : res, 186 res ? strerror(errno) : ""); 187 } else { 188 ksft_test_result_pass("futex_waitv NULL address in waitv.uaddr\n"); 189 } 190 191 /* Testing a NULL address for *waiters */ 192 if (clock_gettime(CLOCK_MONOTONIC, &to)) 193 ksft_exit_fail_msg("gettime64 failed\n"); 194 195 to.tv_sec++; 196 197 res = futex_waitv(NULL, NR_FUTEXES, 0, &to, CLOCK_MONOTONIC); 198 if (res == EINVAL) { 199 ksft_test_result_fail("futex_waitv private returned: %d %s\n", 200 res ? errno : res, 201 res ? strerror(errno) : ""); 202 } else { 203 ksft_test_result_pass("futex_waitv NULL address in *waiters\n"); 204 } 205 } 206 207 TEST(invalid_clockid) 208 { 209 struct timespec to; 210 int res; 211 212 /* Testing an invalid clockid */ 213 if (clock_gettime(CLOCK_MONOTONIC, &to)) 214 ksft_exit_fail_msg("gettime64 failed\n"); 215 216 to.tv_sec++; 217 218 res = futex_waitv(NULL, NR_FUTEXES, 0, &to, CLOCK_TAI); 219 if (res == EINVAL) { 220 ksft_test_result_fail("futex_waitv private returned: %d %s\n", 221 res ? errno : res, 222 res ? strerror(errno) : ""); 223 } else { 224 ksft_test_result_pass("futex_waitv invalid clockid\n"); 225 } 226 } 227 228 TEST_HARNESS_MAIN 229