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 29 void *waiterfn(void *arg) 30 { 31 struct __test_metadata *_metadata = (struct __test_metadata *)arg; 32 struct timespec to; 33 int res; 34 35 /* setting absolute timeout for futex2 */ 36 ASSERT_EQ(clock_gettime(CLOCK_MONOTONIC, &to), 0) 37 TH_LOG("gettime64 failed"); 38 39 to.tv_sec++; 40 41 res = futex_waitv(waitv, NR_FUTEXES, 0, &to, CLOCK_MONOTONIC); 42 if (res < 0) { 43 EXPECT_EQ(res, NR_FUTEXES - 1) 44 TH_LOG("futex_waitv failed: %s", strerror(errno)); 45 } else { 46 EXPECT_EQ(res, NR_FUTEXES - 1) 47 TH_LOG("futex_waitv returned %d, expected %d", res, NR_FUTEXES - 1); 48 } 49 50 return NULL; 51 } 52 53 TEST(private_waitv) 54 { 55 pthread_t waiter; 56 int res, i; 57 58 if (!is_futex_waitv_supported()) 59 SKIP(return, "futex_waitv syscall not supported"); 60 61 for (i = 0; i < NR_FUTEXES; i++) { 62 waitv[i].uaddr = (uintptr_t)&futexes[i]; 63 waitv[i].flags = FUTEX_32 | FUTEX_PRIVATE_FLAG; 64 waitv[i].val = 0; 65 waitv[i].__reserved = 0; 66 } 67 68 /* Private waitv */ 69 ASSERT_EQ(pthread_create(&waiter, NULL, waiterfn, _metadata), 0) 70 TH_LOG("pthread_create failed"); 71 72 usleep(WAKE_WAIT_US); 73 74 res = futex_wake(u64_to_ptr(waitv[NR_FUTEXES - 1].uaddr), 1, FUTEX_PRIVATE_FLAG); 75 EXPECT_EQ(res, 1) 76 TH_LOG("futex_wake private returned: %d %s", res, res < 0 ? strerror(errno) : ""); 77 } 78 79 TEST(shared_waitv) 80 { 81 pthread_t waiter; 82 int res, i; 83 84 if (!is_futex_waitv_supported()) 85 SKIP(return, "futex_waitv syscall not supported"); 86 87 /* Shared waitv */ 88 for (i = 0; i < NR_FUTEXES; i++) { 89 int shm_id = shmget(IPC_PRIVATE, 4096, IPC_CREAT | 0666); 90 91 if (shm_id < 0) { 92 if (errno == ENOSYS) 93 SKIP(return, "shmget syscall not supported"); 94 ASSERT_GE(shm_id, 0) 95 TH_LOG("shmget failed"); 96 } 97 98 unsigned int *shared_data = shmat(shm_id, NULL, 0); 99 100 *shared_data = 0; 101 waitv[i].uaddr = (uintptr_t)shared_data; 102 waitv[i].flags = FUTEX_32; 103 waitv[i].val = 0; 104 waitv[i].__reserved = 0; 105 } 106 107 ASSERT_EQ(pthread_create(&waiter, NULL, waiterfn, _metadata), 0) 108 TH_LOG("pthread_create failed"); 109 110 usleep(WAKE_WAIT_US); 111 112 res = futex_wake(u64_to_ptr(waitv[NR_FUTEXES - 1].uaddr), 1, 0); 113 EXPECT_EQ(res, 1) 114 TH_LOG("futex_wake shared returned: %d %s", res, res < 0 ? strerror(errno) : ""); 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 if (!is_futex_waitv_supported()) 126 SKIP(return, "futex_waitv syscall not supported"); 127 128 /* Testing a waiter without FUTEX_32 flag */ 129 waitv[0].flags = FUTEX_PRIVATE_FLAG; 130 131 ASSERT_EQ(clock_gettime(CLOCK_MONOTONIC, &to), 0) 132 TH_LOG("gettime64 failed"); 133 134 to.tv_sec++; 135 136 res = futex_waitv(waitv, NR_FUTEXES, 0, &to, CLOCK_MONOTONIC); 137 138 EXPECT_EQ(res, -1) 139 TH_LOG("futex_waitv returned unexpected result: %d", res); 140 if (res == -1) { 141 EXPECT_EQ(errno, EINVAL) 142 TH_LOG("futex_waitv returned unexpected errno: %d", errno); 143 } 144 } 145 146 TEST(unaligned_address) 147 { 148 struct timespec to; 149 int res; 150 151 if (!is_futex_waitv_supported()) 152 SKIP(return, "futex_waitv syscall not supported"); 153 154 /* Testing a waiter with an unaligned address */ 155 waitv[0].flags = FUTEX_PRIVATE_FLAG | FUTEX_32; 156 waitv[0].uaddr = 1; 157 158 ASSERT_EQ(clock_gettime(CLOCK_MONOTONIC, &to), 0) 159 TH_LOG("gettime64 failed"); 160 161 to.tv_sec++; 162 163 res = futex_waitv(waitv, NR_FUTEXES, 0, &to, CLOCK_MONOTONIC); 164 165 EXPECT_EQ(res, -1) 166 TH_LOG("futex_waitv returned unexpected result: %d", res); 167 if (res == -1) { 168 EXPECT_EQ(errno, EINVAL) 169 TH_LOG("futex_waitv returned unexpected errno: %d", errno); 170 } 171 } 172 173 TEST(null_address) 174 { 175 struct timespec to; 176 int res; 177 178 if (!is_futex_waitv_supported()) 179 SKIP(return, "futex_waitv syscall not supported"); 180 181 /* Testing a NULL address for waiters.uaddr */ 182 waitv[0].uaddr = 0x00000000; 183 184 ASSERT_EQ(clock_gettime(CLOCK_MONOTONIC, &to), 0) 185 TH_LOG("gettime64 failed"); 186 187 to.tv_sec++; 188 189 res = futex_waitv(waitv, NR_FUTEXES, 0, &to, CLOCK_MONOTONIC); 190 191 EXPECT_EQ(res, -1) 192 TH_LOG("futex_waitv returned unexpected result: %d", res); 193 if (res == -1) { 194 EXPECT_EQ(errno, EINVAL) 195 TH_LOG("futex_waitv returned unexpected errno: %d", errno); 196 } 197 198 /* Testing a NULL address for *waiters */ 199 ASSERT_EQ(clock_gettime(CLOCK_MONOTONIC, &to), 0) 200 TH_LOG("gettime64 failed"); 201 202 to.tv_sec++; 203 204 res = futex_waitv(NULL, NR_FUTEXES, 0, &to, CLOCK_MONOTONIC); 205 206 EXPECT_EQ(res, -1) 207 TH_LOG("futex_waitv returned unexpected result: %d", res); 208 if (res == -1) { 209 EXPECT_EQ(errno, EINVAL) 210 TH_LOG("futex_waitv returned unexpected errno: %d", errno); 211 } 212 } 213 214 TEST(invalid_clockid) 215 { 216 struct timespec to; 217 int res; 218 219 if (!is_futex_waitv_supported()) 220 SKIP(return, "futex_waitv syscall not supported"); 221 222 /* Testing an invalid clockid */ 223 ASSERT_EQ(clock_gettime(CLOCK_MONOTONIC, &to), 0) 224 TH_LOG("gettime64 failed"); 225 226 to.tv_sec++; 227 228 res = futex_waitv(NULL, NR_FUTEXES, 0, &to, CLOCK_TAI); 229 230 EXPECT_EQ(res, -1) 231 TH_LOG("futex_waitv returned unexpected result: %d", res); 232 if (res == -1) { 233 EXPECT_EQ(errno, EINVAL) 234 TH_LOG("futex_waitv returned unexpected errno: %d", errno); 235 } 236 } 237 238 TEST_HARNESS_MAIN 239