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 perror("shmget"); 90 exit(1); 91 } 92 93 unsigned int *shared_data = shmat(shm_id, NULL, 0); 94 95 *shared_data = 0; 96 waitv[i].uaddr = (uintptr_t)shared_data; 97 waitv[i].flags = FUTEX_32; 98 waitv[i].val = 0; 99 waitv[i].__reserved = 0; 100 } 101 102 if (pthread_create(&waiter, NULL, waiterfn, NULL)) 103 ksft_exit_fail_msg("pthread_create failed\n"); 104 105 usleep(WAKE_WAIT_US); 106 107 res = futex_wake(u64_to_ptr(waitv[NR_FUTEXES - 1].uaddr), 1, 0); 108 if (res != 1) { 109 ksft_test_result_fail("futex_wake shared returned: %d %s\n", 110 res ? errno : res, 111 res ? strerror(errno) : ""); 112 } else { 113 ksft_test_result_pass("futex_waitv shared\n"); 114 } 115 116 for (i = 0; i < NR_FUTEXES; i++) 117 shmdt(u64_to_ptr(waitv[i].uaddr)); 118 } 119 120 TEST(invalid_flag) 121 { 122 struct timespec to; 123 int res; 124 125 /* Testing a waiter without FUTEX_32 flag */ 126 waitv[0].flags = FUTEX_PRIVATE_FLAG; 127 128 if (clock_gettime(CLOCK_MONOTONIC, &to)) 129 ksft_exit_fail_msg("gettime64 failed\n"); 130 131 to.tv_sec++; 132 133 res = futex_waitv(waitv, NR_FUTEXES, 0, &to, CLOCK_MONOTONIC); 134 if (res == EINVAL) { 135 ksft_test_result_fail("futex_waitv private returned: %d %s\n", 136 res ? errno : res, 137 res ? strerror(errno) : ""); 138 } else { 139 ksft_test_result_pass("futex_waitv without FUTEX_32\n"); 140 } 141 } 142 143 TEST(unaligned_address) 144 { 145 struct timespec to; 146 int res; 147 148 /* Testing a waiter with an unaligned address */ 149 waitv[0].flags = FUTEX_PRIVATE_FLAG | FUTEX_32; 150 waitv[0].uaddr = 1; 151 152 if (clock_gettime(CLOCK_MONOTONIC, &to)) 153 ksft_exit_fail_msg("gettime64 failed\n"); 154 155 to.tv_sec++; 156 157 res = futex_waitv(waitv, NR_FUTEXES, 0, &to, CLOCK_MONOTONIC); 158 if (res == EINVAL) { 159 ksft_test_result_fail("futex_wake private returned: %d %s\n", 160 res ? errno : res, 161 res ? strerror(errno) : ""); 162 } else { 163 ksft_test_result_pass("futex_waitv with an unaligned address\n"); 164 } 165 } 166 167 TEST(null_address) 168 { 169 struct timespec to; 170 int res; 171 172 /* Testing a NULL address for waiters.uaddr */ 173 waitv[0].uaddr = 0x00000000; 174 175 if (clock_gettime(CLOCK_MONOTONIC, &to)) 176 ksft_exit_fail_msg("gettime64 failed\n"); 177 178 to.tv_sec++; 179 180 res = futex_waitv(waitv, NR_FUTEXES, 0, &to, CLOCK_MONOTONIC); 181 if (res == EINVAL) { 182 ksft_test_result_fail("futex_waitv private returned: %d %s\n", 183 res ? errno : res, 184 res ? strerror(errno) : ""); 185 } else { 186 ksft_test_result_pass("futex_waitv NULL address in waitv.uaddr\n"); 187 } 188 189 /* Testing a NULL address for *waiters */ 190 if (clock_gettime(CLOCK_MONOTONIC, &to)) 191 ksft_exit_fail_msg("gettime64 failed\n"); 192 193 to.tv_sec++; 194 195 res = futex_waitv(NULL, NR_FUTEXES, 0, &to, CLOCK_MONOTONIC); 196 if (res == EINVAL) { 197 ksft_test_result_fail("futex_waitv private returned: %d %s\n", 198 res ? errno : res, 199 res ? strerror(errno) : ""); 200 } else { 201 ksft_test_result_pass("futex_waitv NULL address in *waiters\n"); 202 } 203 } 204 205 TEST(invalid_clockid) 206 { 207 struct timespec to; 208 int res; 209 210 /* Testing an invalid clockid */ 211 if (clock_gettime(CLOCK_MONOTONIC, &to)) 212 ksft_exit_fail_msg("gettime64 failed\n"); 213 214 to.tv_sec++; 215 216 res = futex_waitv(NULL, NR_FUTEXES, 0, &to, CLOCK_TAI); 217 if (res == EINVAL) { 218 ksft_test_result_fail("futex_waitv private returned: %d %s\n", 219 res ? errno : res, 220 res ? strerror(errno) : ""); 221 } else { 222 ksft_test_result_pass("futex_waitv invalid clockid\n"); 223 } 224 } 225 226 TEST_HARNESS_MAIN 227