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 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
TEST(invalid_flag)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
TEST(unaligned_address)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
TEST(null_address)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
TEST(invalid_clockid)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