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 | |
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 | |
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 | |
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(prog: basename(argv[0])); |
74 | exit(0); |
75 | case 'v': |
76 | log_verbosity(atoi(optarg)); |
77 | break; |
78 | default: |
79 | usage(prog: 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 | |