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
25static struct futex_waitv waitv[NR_FUTEXES];
26u_int32_t futexes[NR_FUTEXES] = {0};
27
28void 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
37void *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
60int 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