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 if (res == EINVAL) { 138 EXPECT_TRUE(0) { 139 TH_LOG("futex_waitv private returned: %d %s", 140 res ? errno : res, res ? strerror(errno) : ""); 141 } 142 } 143 } 144 145 TEST(unaligned_address) 146 { 147 struct timespec to; 148 int res; 149 150 if (!is_futex_waitv_supported()) 151 SKIP(return, "futex_waitv syscall not supported"); 152 153 /* Testing a waiter with an unaligned address */ 154 waitv[0].flags = FUTEX_PRIVATE_FLAG | FUTEX_32; 155 waitv[0].uaddr = 1; 156 157 ASSERT_EQ(clock_gettime(CLOCK_MONOTONIC, &to), 0) 158 TH_LOG("gettime64 failed"); 159 160 to.tv_sec++; 161 162 res = futex_waitv(waitv, NR_FUTEXES, 0, &to, CLOCK_MONOTONIC); 163 if (res == EINVAL) { 164 EXPECT_TRUE(0) { 165 TH_LOG("futex_wake private returned: %d %s", 166 res ? errno : res, res ? strerror(errno) : ""); 167 } 168 } 169 } 170 171 TEST(null_address) 172 { 173 struct timespec to; 174 int res; 175 176 if (!is_futex_waitv_supported()) 177 SKIP(return, "futex_waitv syscall not supported"); 178 179 /* Testing a NULL address for waiters.uaddr */ 180 waitv[0].uaddr = 0x00000000; 181 182 ASSERT_EQ(clock_gettime(CLOCK_MONOTONIC, &to), 0) 183 TH_LOG("gettime64 failed"); 184 185 to.tv_sec++; 186 187 res = futex_waitv(waitv, NR_FUTEXES, 0, &to, CLOCK_MONOTONIC); 188 if (res == EINVAL) { 189 EXPECT_TRUE(0) { 190 TH_LOG("futex_waitv private returned: %d %s", 191 res ? errno : res, res ? strerror(errno) : ""); 192 } 193 } 194 195 /* Testing a NULL address for *waiters */ 196 ASSERT_EQ(clock_gettime(CLOCK_MONOTONIC, &to), 0) 197 TH_LOG("gettime64 failed"); 198 199 to.tv_sec++; 200 201 res = futex_waitv(NULL, NR_FUTEXES, 0, &to, CLOCK_MONOTONIC); 202 if (res == EINVAL) { 203 EXPECT_TRUE(0) { 204 TH_LOG("futex_waitv private returned: %d %s", 205 res ? errno : res, res ? strerror(errno) : ""); 206 } 207 } 208 } 209 210 TEST(invalid_clockid) 211 { 212 struct timespec to; 213 int res; 214 215 if (!is_futex_waitv_supported()) 216 SKIP(return, "futex_waitv syscall not supported"); 217 218 /* Testing an invalid clockid */ 219 ASSERT_EQ(clock_gettime(CLOCK_MONOTONIC, &to), 0) 220 TH_LOG("gettime64 failed"); 221 222 to.tv_sec++; 223 224 res = futex_waitv(NULL, NR_FUTEXES, 0, &to, CLOCK_TAI); 225 if (res == EINVAL) { 226 EXPECT_TRUE(0) { 227 TH_LOG("futex_waitv private returned: %d %s", 228 res ? errno : res, res ? strerror(errno) : ""); 229 } 230 } 231 } 232 233 TEST_HARNESS_MAIN 234