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
waiterfn(void * arg)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
TEST(private_waitv)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
TEST(shared_waitv)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
TEST(invalid_flag)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
TEST(unaligned_address)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
TEST(null_address)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
TEST(invalid_clockid)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