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 #include "futextest.h"
19 #include "futex2test.h"
20 #include "logging.h"
21
22 #define TEST_NAME "futex-wait"
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
usage(char * prog)28 void usage(char *prog)
29 {
30 printf("Usage: %s\n", prog);
31 printf(" -c Use color\n");
32 printf(" -h Display this help message\n");
33 printf(" -v L Verbosity level: %d=QUIET %d=CRITICAL %d=INFO\n",
34 VQUIET, VCRITICAL, VINFO);
35 }
36
waiterfn(void * arg)37 void *waiterfn(void *arg)
38 {
39 struct timespec to;
40 int res;
41
42 /* setting absolute timeout for futex2 */
43 if (clock_gettime(CLOCK_MONOTONIC, &to))
44 error("gettime64 failed\n", errno);
45
46 to.tv_sec++;
47
48 res = futex_waitv(waitv, NR_FUTEXES, 0, &to, CLOCK_MONOTONIC);
49 if (res < 0) {
50 ksft_test_result_fail("futex_waitv returned: %d %s\n",
51 errno, strerror(errno));
52 } else if (res != NR_FUTEXES - 1) {
53 ksft_test_result_fail("futex_waitv returned: %d, expecting %d\n",
54 res, NR_FUTEXES - 1);
55 }
56
57 return NULL;
58 }
59
main(int argc,char * argv[])60 int main(int argc, char *argv[])
61 {
62 pthread_t waiter;
63 int res, ret = RET_PASS;
64 struct timespec to;
65 int c, i;
66
67 while ((c = getopt(argc, argv, "cht:v:")) != -1) {
68 switch (c) {
69 case 'c':
70 log_color(1);
71 break;
72 case 'h':
73 usage(basename(argv[0]));
74 exit(0);
75 case 'v':
76 log_verbosity(atoi(optarg));
77 break;
78 default:
79 usage(basename(argv[0]));
80 exit(1);
81 }
82 }
83
84 ksft_print_header();
85 ksft_set_plan(7);
86 ksft_print_msg("%s: Test FUTEX_WAITV\n",
87 basename(argv[0]));
88
89 for (i = 0; i < NR_FUTEXES; i++) {
90 waitv[i].uaddr = (uintptr_t)&futexes[i];
91 waitv[i].flags = FUTEX_32 | FUTEX_PRIVATE_FLAG;
92 waitv[i].val = 0;
93 waitv[i].__reserved = 0;
94 }
95
96 /* Private waitv */
97 if (pthread_create(&waiter, NULL, waiterfn, NULL))
98 error("pthread_create failed\n", errno);
99
100 usleep(WAKE_WAIT_US);
101
102 res = futex_wake(u64_to_ptr(waitv[NR_FUTEXES - 1].uaddr), 1, FUTEX_PRIVATE_FLAG);
103 if (res != 1) {
104 ksft_test_result_fail("futex_wake private returned: %d %s\n",
105 res ? errno : res,
106 res ? strerror(errno) : "");
107 ret = RET_FAIL;
108 } else {
109 ksft_test_result_pass("futex_waitv private\n");
110 }
111
112 /* Shared waitv */
113 for (i = 0; i < NR_FUTEXES; i++) {
114 int shm_id = shmget(IPC_PRIVATE, 4096, IPC_CREAT | 0666);
115
116 if (shm_id < 0) {
117 perror("shmget");
118 exit(1);
119 }
120
121 unsigned int *shared_data = shmat(shm_id, NULL, 0);
122
123 *shared_data = 0;
124 waitv[i].uaddr = (uintptr_t)shared_data;
125 waitv[i].flags = FUTEX_32;
126 waitv[i].val = 0;
127 waitv[i].__reserved = 0;
128 }
129
130 if (pthread_create(&waiter, NULL, waiterfn, NULL))
131 error("pthread_create failed\n", errno);
132
133 usleep(WAKE_WAIT_US);
134
135 res = futex_wake(u64_to_ptr(waitv[NR_FUTEXES - 1].uaddr), 1, 0);
136 if (res != 1) {
137 ksft_test_result_fail("futex_wake shared returned: %d %s\n",
138 res ? errno : res,
139 res ? strerror(errno) : "");
140 ret = RET_FAIL;
141 } else {
142 ksft_test_result_pass("futex_waitv shared\n");
143 }
144
145 for (i = 0; i < NR_FUTEXES; i++)
146 shmdt(u64_to_ptr(waitv[i].uaddr));
147
148 /* Testing a waiter without FUTEX_32 flag */
149 waitv[0].flags = FUTEX_PRIVATE_FLAG;
150
151 if (clock_gettime(CLOCK_MONOTONIC, &to))
152 error("gettime64 failed\n", errno);
153
154 to.tv_sec++;
155
156 res = futex_waitv(waitv, NR_FUTEXES, 0, &to, CLOCK_MONOTONIC);
157 if (res == EINVAL) {
158 ksft_test_result_fail("futex_waitv private returned: %d %s\n",
159 res ? errno : res,
160 res ? strerror(errno) : "");
161 ret = RET_FAIL;
162 } else {
163 ksft_test_result_pass("futex_waitv without FUTEX_32\n");
164 }
165
166 /* Testing a waiter with an unaligned address */
167 waitv[0].flags = FUTEX_PRIVATE_FLAG | FUTEX_32;
168 waitv[0].uaddr = 1;
169
170 if (clock_gettime(CLOCK_MONOTONIC, &to))
171 error("gettime64 failed\n", errno);
172
173 to.tv_sec++;
174
175 res = futex_waitv(waitv, NR_FUTEXES, 0, &to, CLOCK_MONOTONIC);
176 if (res == EINVAL) {
177 ksft_test_result_fail("futex_wake private returned: %d %s\n",
178 res ? errno : res,
179 res ? strerror(errno) : "");
180 ret = RET_FAIL;
181 } else {
182 ksft_test_result_pass("futex_waitv with an unaligned address\n");
183 }
184
185 /* Testing a NULL address for waiters.uaddr */
186 waitv[0].uaddr = 0x00000000;
187
188 if (clock_gettime(CLOCK_MONOTONIC, &to))
189 error("gettime64 failed\n", errno);
190
191 to.tv_sec++;
192
193 res = futex_waitv(waitv, NR_FUTEXES, 0, &to, CLOCK_MONOTONIC);
194 if (res == EINVAL) {
195 ksft_test_result_fail("futex_waitv private returned: %d %s\n",
196 res ? errno : res,
197 res ? strerror(errno) : "");
198 ret = RET_FAIL;
199 } else {
200 ksft_test_result_pass("futex_waitv NULL address in waitv.uaddr\n");
201 }
202
203 /* Testing a NULL address for *waiters */
204 if (clock_gettime(CLOCK_MONOTONIC, &to))
205 error("gettime64 failed\n", errno);
206
207 to.tv_sec++;
208
209 res = futex_waitv(NULL, NR_FUTEXES, 0, &to, CLOCK_MONOTONIC);
210 if (res == EINVAL) {
211 ksft_test_result_fail("futex_waitv private returned: %d %s\n",
212 res ? errno : res,
213 res ? strerror(errno) : "");
214 ret = RET_FAIL;
215 } else {
216 ksft_test_result_pass("futex_waitv NULL address in *waiters\n");
217 }
218
219 /* Testing an invalid clockid */
220 if (clock_gettime(CLOCK_MONOTONIC, &to))
221 error("gettime64 failed\n", errno);
222
223 to.tv_sec++;
224
225 res = futex_waitv(NULL, NR_FUTEXES, 0, &to, CLOCK_TAI);
226 if (res == EINVAL) {
227 ksft_test_result_fail("futex_waitv private returned: %d %s\n",
228 res ? errno : res,
229 res ? strerror(errno) : "");
230 ret = RET_FAIL;
231 } else {
232 ksft_test_result_pass("futex_waitv invalid clockid\n");
233 }
234
235 ksft_print_cnts();
236 return ret;
237 }
238