1f08c3bdfSopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
2f08c3bdfSopenharmony_ci/*
3f08c3bdfSopenharmony_ci * Copyright © International Business Machines  Corp., 2009
4f08c3bdfSopenharmony_ci * Copyright (C) 2015 Cyril Hrubis <chrubis@suse.cz>
5f08c3bdfSopenharmony_ci *
6f08c3bdfSopenharmony_ci * DESCRIPTION
7f08c3bdfSopenharmony_ci *      Glibc independent futex library for testing kernel functionality.
8f08c3bdfSopenharmony_ci *
9f08c3bdfSopenharmony_ci * AUTHOR
10f08c3bdfSopenharmony_ci *      Darren Hart <dvhltc@us.ibm.com>
11f08c3bdfSopenharmony_ci */
12f08c3bdfSopenharmony_ci
13f08c3bdfSopenharmony_ci#ifndef FUTEXTEST_H
14f08c3bdfSopenharmony_ci#define FUTEXTEST_H
15f08c3bdfSopenharmony_ci
16f08c3bdfSopenharmony_ci#include <unistd.h>
17f08c3bdfSopenharmony_ci#include <sys/syscall.h>
18f08c3bdfSopenharmony_ci#include <sys/types.h>
19f08c3bdfSopenharmony_ci#include "lapi/futex.h"
20f08c3bdfSopenharmony_ci#include "tst_timer.h"
21f08c3bdfSopenharmony_ci
22f08c3bdfSopenharmony_ci#define FUTEX_INITIALIZER 0
23f08c3bdfSopenharmony_ci
24f08c3bdfSopenharmony_cienum futex_fn_type {
25f08c3bdfSopenharmony_ci	FUTEX_FN_FUTEX,
26f08c3bdfSopenharmony_ci	FUTEX_FN_FUTEX64,
27f08c3bdfSopenharmony_ci};
28f08c3bdfSopenharmony_ci
29f08c3bdfSopenharmony_cistruct futex_test_variants {
30f08c3bdfSopenharmony_ci	enum futex_fn_type fntype;
31f08c3bdfSopenharmony_ci	enum tst_ts_type tstype;
32f08c3bdfSopenharmony_ci	int (*gettime)(clockid_t clk_id, void *ts);
33f08c3bdfSopenharmony_ci	char *desc;
34f08c3bdfSopenharmony_ci};
35f08c3bdfSopenharmony_ci
36f08c3bdfSopenharmony_cistatic inline void futex_supported_by_kernel(enum futex_fn_type fntype)
37f08c3bdfSopenharmony_ci{
38f08c3bdfSopenharmony_ci	if (fntype != FUTEX_FN_FUTEX64)
39f08c3bdfSopenharmony_ci		return;
40f08c3bdfSopenharmony_ci
41f08c3bdfSopenharmony_ci	/* Check if the syscall is implemented on the platform */
42f08c3bdfSopenharmony_ci	TEST(sys_futex_time64(NULL, 0, 0, NULL, NULL, 0));
43f08c3bdfSopenharmony_ci	if (TST_RET == -1 && TST_ERR == ENOSYS)
44f08c3bdfSopenharmony_ci		tst_brk(TCONF, "Test not supported on kernel/platform");
45f08c3bdfSopenharmony_ci}
46f08c3bdfSopenharmony_ci
47f08c3bdfSopenharmony_ci/**
48f08c3bdfSopenharmony_ci * futex_syscall() - futex syscall wrapper
49f08c3bdfSopenharmony_ci * @fntype:	Futex function type
50f08c3bdfSopenharmony_ci * @uaddr:	address of first futex
51f08c3bdfSopenharmony_ci * @op:		futex op code
52f08c3bdfSopenharmony_ci * @val:	typically expected value of uaddr, but varies by op
53f08c3bdfSopenharmony_ci * @timeout:	typically an absolute struct tst_ts (except where noted
54f08c3bdfSopenharmony_ci *		otherwise). Overloaded by some ops
55f08c3bdfSopenharmony_ci * @uaddr2:	address of second futex for some ops\
56f08c3bdfSopenharmony_ci * @val3:	varies by op
57f08c3bdfSopenharmony_ci * @opflags:	flags to be bitwise OR'd with op, such as FUTEX_PRIVATE_FLAG
58f08c3bdfSopenharmony_ci *
59f08c3bdfSopenharmony_ci * futex_syscall() is used by all the following futex op wrappers. It can also be
60f08c3bdfSopenharmony_ci * used for misuse and abuse testing. Generally, the specific op wrappers
61f08c3bdfSopenharmony_ci * should be used instead. It is a macro instead of an static inline function as
62f08c3bdfSopenharmony_ci * some of the types over overloaded (timeout is used for nr_requeue for
63f08c3bdfSopenharmony_ci * example).
64f08c3bdfSopenharmony_ci *
65f08c3bdfSopenharmony_ci * These argument descriptions are the defaults for all
66f08c3bdfSopenharmony_ci * like-named arguments in the following wrappers except where noted below.
67f08c3bdfSopenharmony_ci */
68f08c3bdfSopenharmony_cistatic inline int futex_syscall(enum futex_fn_type fntype, futex_t *uaddr,
69f08c3bdfSopenharmony_ci				int futex_op, futex_t val, void *timeout,
70f08c3bdfSopenharmony_ci				futex_t *uaddr2, int val3, int opflags)
71f08c3bdfSopenharmony_ci{
72f08c3bdfSopenharmony_ci	int (*func)(int *uaddr, int futex_op, int val, void *to, int *uaddr2, int val3);
73f08c3bdfSopenharmony_ci
74f08c3bdfSopenharmony_ci	if (fntype == FUTEX_FN_FUTEX)
75f08c3bdfSopenharmony_ci		func = sys_futex;
76f08c3bdfSopenharmony_ci	else
77f08c3bdfSopenharmony_ci		func = sys_futex_time64;
78f08c3bdfSopenharmony_ci
79f08c3bdfSopenharmony_ci	return func((int *)uaddr, futex_op | opflags, val, timeout, (int *)uaddr2, val3);
80f08c3bdfSopenharmony_ci}
81f08c3bdfSopenharmony_ci
82f08c3bdfSopenharmony_ci/**
83f08c3bdfSopenharmony_ci * futex_wait() - block on uaddr with optional timeout
84f08c3bdfSopenharmony_ci * @timeout:	relative timeout
85f08c3bdfSopenharmony_ci */
86f08c3bdfSopenharmony_cistatic inline int
87f08c3bdfSopenharmony_cifutex_wait(enum futex_fn_type fntype, futex_t *uaddr, futex_t val,
88f08c3bdfSopenharmony_ci	   struct tst_ts *timeout, int opflags)
89f08c3bdfSopenharmony_ci{
90f08c3bdfSopenharmony_ci	return futex_syscall(fntype, uaddr, FUTEX_WAIT, val,
91f08c3bdfSopenharmony_ci			     tst_ts_get(timeout), NULL, 0, opflags);
92f08c3bdfSopenharmony_ci}
93f08c3bdfSopenharmony_ci
94f08c3bdfSopenharmony_ci/**
95f08c3bdfSopenharmony_ci * futex_wake() - wake one or more tasks blocked on uaddr
96f08c3bdfSopenharmony_ci * @nr_wake:	wake up to this many tasks
97f08c3bdfSopenharmony_ci */
98f08c3bdfSopenharmony_cistatic inline int
99f08c3bdfSopenharmony_cifutex_wake(enum futex_fn_type fntype, futex_t *uaddr, int nr_wake, int opflags)
100f08c3bdfSopenharmony_ci{
101f08c3bdfSopenharmony_ci	return futex_syscall(fntype, uaddr, FUTEX_WAKE, nr_wake, NULL, NULL, 0,
102f08c3bdfSopenharmony_ci			     opflags);
103f08c3bdfSopenharmony_ci}
104f08c3bdfSopenharmony_ci
105f08c3bdfSopenharmony_ci/**
106f08c3bdfSopenharmony_ci * futex_wait_bitset() - block on uaddr with bitset
107f08c3bdfSopenharmony_ci * @bitset:	bitset to be used with futex_wake_bitset
108f08c3bdfSopenharmony_ci */
109f08c3bdfSopenharmony_cistatic inline int
110f08c3bdfSopenharmony_cifutex_wait_bitset(enum futex_fn_type fntype, futex_t *uaddr, futex_t val,
111f08c3bdfSopenharmony_ci		  struct tst_ts *timeout, u_int32_t bitset, int opflags)
112f08c3bdfSopenharmony_ci{
113f08c3bdfSopenharmony_ci	return futex_syscall(fntype, uaddr, FUTEX_WAIT_BITSET, val,
114f08c3bdfSopenharmony_ci			     tst_ts_get(timeout), NULL, bitset, opflags);
115f08c3bdfSopenharmony_ci}
116f08c3bdfSopenharmony_ci
117f08c3bdfSopenharmony_ci/**
118f08c3bdfSopenharmony_ci * futex_wake_bitset() - wake one or more tasks blocked on uaddr with bitset
119f08c3bdfSopenharmony_ci * @bitset:	bitset to compare with that used in futex_wait_bitset
120f08c3bdfSopenharmony_ci */
121f08c3bdfSopenharmony_cistatic inline int
122f08c3bdfSopenharmony_cifutex_wake_bitset(enum futex_fn_type fntype, futex_t *uaddr, int nr_wake,
123f08c3bdfSopenharmony_ci		  u_int32_t bitset, int opflags)
124f08c3bdfSopenharmony_ci{
125f08c3bdfSopenharmony_ci	return futex_syscall(fntype, uaddr, FUTEX_WAKE_BITSET, nr_wake, NULL,
126f08c3bdfSopenharmony_ci			     NULL, bitset, opflags);
127f08c3bdfSopenharmony_ci}
128f08c3bdfSopenharmony_ci
129f08c3bdfSopenharmony_ci/**
130f08c3bdfSopenharmony_ci * futex_lock_pi() - block on uaddr as a PI mutex
131f08c3bdfSopenharmony_ci * @detect:	whether (1) or not (0) to perform deadlock detection
132f08c3bdfSopenharmony_ci */
133f08c3bdfSopenharmony_cistatic inline int
134f08c3bdfSopenharmony_cifutex_lock_pi(enum futex_fn_type fntype, futex_t *uaddr, struct tst_ts *timeout,
135f08c3bdfSopenharmony_ci	      int detect, int opflags)
136f08c3bdfSopenharmony_ci{
137f08c3bdfSopenharmony_ci	return futex_syscall(fntype, uaddr, FUTEX_LOCK_PI, detect,
138f08c3bdfSopenharmony_ci			     tst_ts_get(timeout), NULL, 0, opflags);
139f08c3bdfSopenharmony_ci}
140f08c3bdfSopenharmony_ci
141f08c3bdfSopenharmony_ci/**
142f08c3bdfSopenharmony_ci * futex_unlock_pi() - release uaddr as a PI mutex, waking the top waiter
143f08c3bdfSopenharmony_ci */
144f08c3bdfSopenharmony_cistatic inline int
145f08c3bdfSopenharmony_cifutex_unlock_pi(enum futex_fn_type fntype, futex_t *uaddr, int opflags)
146f08c3bdfSopenharmony_ci{
147f08c3bdfSopenharmony_ci	return futex_syscall(fntype, uaddr, FUTEX_UNLOCK_PI, 0, NULL, NULL, 0,
148f08c3bdfSopenharmony_ci			     opflags); }
149f08c3bdfSopenharmony_ci
150f08c3bdfSopenharmony_ci/**
151f08c3bdfSopenharmony_ci * futex_wake_op() - FIXME: COME UP WITH A GOOD ONE LINE DESCRIPTION
152f08c3bdfSopenharmony_ci */
153f08c3bdfSopenharmony_cistatic inline int
154f08c3bdfSopenharmony_cifutex_wake_op(enum futex_fn_type fntype, futex_t *uaddr, futex_t *uaddr2,
155f08c3bdfSopenharmony_ci	      int nr_wake, int nr_wake2, int wake_op, int opflags)
156f08c3bdfSopenharmony_ci{
157f08c3bdfSopenharmony_ci	return futex_syscall(fntype, uaddr, FUTEX_WAKE_OP, nr_wake,
158f08c3bdfSopenharmony_ci			     (void *)((unsigned long)nr_wake2), uaddr2, wake_op,
159f08c3bdfSopenharmony_ci			     opflags);
160f08c3bdfSopenharmony_ci}
161f08c3bdfSopenharmony_ci
162f08c3bdfSopenharmony_ci/**
163f08c3bdfSopenharmony_ci * futex_requeue() - requeue without expected value comparison, deprecated
164f08c3bdfSopenharmony_ci * @nr_wake:	wake up to this many tasks
165f08c3bdfSopenharmony_ci * @nr_requeue:	requeue up to this many tasks
166f08c3bdfSopenharmony_ci *
167f08c3bdfSopenharmony_ci * Due to its inherently racy implementation, futex_requeue() is deprecated in
168f08c3bdfSopenharmony_ci * favor of futex_cmp_requeue().
169f08c3bdfSopenharmony_ci */
170f08c3bdfSopenharmony_cistatic inline int
171f08c3bdfSopenharmony_cifutex_requeue(enum futex_fn_type fntype, futex_t *uaddr, futex_t *uaddr2,
172f08c3bdfSopenharmony_ci	      int nr_wake, int nr_requeue, int opflags)
173f08c3bdfSopenharmony_ci{
174f08c3bdfSopenharmony_ci	return futex_syscall(fntype, uaddr, FUTEX_REQUEUE, nr_wake,
175f08c3bdfSopenharmony_ci			     (void *)((unsigned long)nr_requeue), uaddr2, 0,
176f08c3bdfSopenharmony_ci			     opflags);
177f08c3bdfSopenharmony_ci}
178f08c3bdfSopenharmony_ci
179f08c3bdfSopenharmony_ci/**
180f08c3bdfSopenharmony_ci * futex_cmp_requeue() - requeue tasks from uaddr to uaddr2
181f08c3bdfSopenharmony_ci * @nr_wake:	wake up to this many tasks
182f08c3bdfSopenharmony_ci * @nr_requeue:	requeue up to this many tasks
183f08c3bdfSopenharmony_ci */
184f08c3bdfSopenharmony_cistatic inline int
185f08c3bdfSopenharmony_cifutex_cmp_requeue(enum futex_fn_type fntype, futex_t *uaddr, futex_t val,
186f08c3bdfSopenharmony_ci		  futex_t *uaddr2, int nr_wake, int nr_requeue, int opflags)
187f08c3bdfSopenharmony_ci{
188f08c3bdfSopenharmony_ci	return futex_syscall(fntype, uaddr, FUTEX_CMP_REQUEUE, nr_wake,
189f08c3bdfSopenharmony_ci			     (void *)((unsigned long)nr_requeue), uaddr2, val,
190f08c3bdfSopenharmony_ci			     opflags);
191f08c3bdfSopenharmony_ci}
192f08c3bdfSopenharmony_ci
193f08c3bdfSopenharmony_ci/**
194f08c3bdfSopenharmony_ci * futex_wait_requeue_pi() - block on uaddr and prepare to requeue to uaddr2
195f08c3bdfSopenharmony_ci * @uaddr:	non-PI futex source
196f08c3bdfSopenharmony_ci * @uaddr2:	PI futex target
197f08c3bdfSopenharmony_ci *
198f08c3bdfSopenharmony_ci * This is the first half of the requeue_pi mechanism. It shall always be
199f08c3bdfSopenharmony_ci * paired with futex_cmp_requeue_pi().
200f08c3bdfSopenharmony_ci */
201f08c3bdfSopenharmony_cistatic inline int
202f08c3bdfSopenharmony_cifutex_wait_requeue_pi(enum futex_fn_type fntype, futex_t *uaddr, futex_t val,
203f08c3bdfSopenharmony_ci		      futex_t *uaddr2, struct tst_ts *timeout, int opflags)
204f08c3bdfSopenharmony_ci{
205f08c3bdfSopenharmony_ci	return futex_syscall(fntype, uaddr, FUTEX_WAIT_REQUEUE_PI, val,
206f08c3bdfSopenharmony_ci			     tst_ts_get(timeout), uaddr2, 0, opflags);
207f08c3bdfSopenharmony_ci}
208f08c3bdfSopenharmony_ci
209f08c3bdfSopenharmony_ci/**
210f08c3bdfSopenharmony_ci * futex_cmp_requeue_pi() - requeue tasks from uaddr to uaddr2 (PI aware)
211f08c3bdfSopenharmony_ci * @uaddr:	non-PI futex source
212f08c3bdfSopenharmony_ci * @uaddr2:	PI futex target
213f08c3bdfSopenharmony_ci * @nr_wake:	wake up to this many tasks
214f08c3bdfSopenharmony_ci * @nr_requeue:	requeue up to this many tasks
215f08c3bdfSopenharmony_ci */
216f08c3bdfSopenharmony_cistatic inline int
217f08c3bdfSopenharmony_cifutex_cmp_requeue_pi(enum futex_fn_type fntype, futex_t *uaddr, futex_t val,
218f08c3bdfSopenharmony_ci		     futex_t *uaddr2, int nr_wake, int nr_requeue, int opflags)
219f08c3bdfSopenharmony_ci{
220f08c3bdfSopenharmony_ci	return futex_syscall(fntype, uaddr, FUTEX_CMP_REQUEUE_PI, nr_wake,
221f08c3bdfSopenharmony_ci			     (void *)((unsigned long)nr_requeue), uaddr2, val,
222f08c3bdfSopenharmony_ci			     opflags);
223f08c3bdfSopenharmony_ci}
224f08c3bdfSopenharmony_ci
225f08c3bdfSopenharmony_ci/**
226f08c3bdfSopenharmony_ci * futex_cmpxchg() - atomic compare and exchange
227f08c3bdfSopenharmony_ci * @uaddr:	The address of the futex to be modified
228f08c3bdfSopenharmony_ci * @oldval:	The expected value of the futex
229f08c3bdfSopenharmony_ci * @newval:	The new value to try and assign the futex
230f08c3bdfSopenharmony_ci *
231f08c3bdfSopenharmony_ci * Implement cmpxchg using gcc atomic builtins.
232f08c3bdfSopenharmony_ci * http://gcc.gnu.org/onlinedocs/gcc-4.1.0/gcc/Atomic-Builtins.html
233f08c3bdfSopenharmony_ci *
234f08c3bdfSopenharmony_ci * Return the old futex value.
235f08c3bdfSopenharmony_ci */
236f08c3bdfSopenharmony_cistatic inline u_int32_t
237f08c3bdfSopenharmony_cifutex_cmpxchg(futex_t *uaddr, u_int32_t oldval, u_int32_t newval)
238f08c3bdfSopenharmony_ci{
239f08c3bdfSopenharmony_ci	return __sync_val_compare_and_swap(uaddr, oldval, newval);
240f08c3bdfSopenharmony_ci}
241f08c3bdfSopenharmony_ci
242f08c3bdfSopenharmony_ci/**
243f08c3bdfSopenharmony_ci * futex_dec() - atomic decrement of the futex value
244f08c3bdfSopenharmony_ci * @uaddr:	The address of the futex to be modified
245f08c3bdfSopenharmony_ci *
246f08c3bdfSopenharmony_ci * Return the new futex value.
247f08c3bdfSopenharmony_ci */
248f08c3bdfSopenharmony_cistatic inline u_int32_t
249f08c3bdfSopenharmony_cifutex_dec(futex_t *uaddr)
250f08c3bdfSopenharmony_ci{
251f08c3bdfSopenharmony_ci	return __sync_sub_and_fetch(uaddr, 1);
252f08c3bdfSopenharmony_ci}
253f08c3bdfSopenharmony_ci
254f08c3bdfSopenharmony_ci/**
255f08c3bdfSopenharmony_ci * futex_inc() - atomic increment of the futex value
256f08c3bdfSopenharmony_ci * @uaddr:	the address of the futex to be modified
257f08c3bdfSopenharmony_ci *
258f08c3bdfSopenharmony_ci * Return the new futex value.
259f08c3bdfSopenharmony_ci */
260f08c3bdfSopenharmony_cistatic inline u_int32_t
261f08c3bdfSopenharmony_cifutex_inc(futex_t *uaddr)
262f08c3bdfSopenharmony_ci{
263f08c3bdfSopenharmony_ci	return __sync_add_and_fetch(uaddr, 1);
264f08c3bdfSopenharmony_ci}
265f08c3bdfSopenharmony_ci
266f08c3bdfSopenharmony_ci/**
267f08c3bdfSopenharmony_ci * futex_set() - atomic decrement of the futex value
268f08c3bdfSopenharmony_ci * @uaddr:	the address of the futex to be modified
269f08c3bdfSopenharmony_ci * @newval:	New value for the atomic_t
270f08c3bdfSopenharmony_ci *
271f08c3bdfSopenharmony_ci * Return the new futex value.
272f08c3bdfSopenharmony_ci */
273f08c3bdfSopenharmony_cistatic inline u_int32_t
274f08c3bdfSopenharmony_cifutex_set(futex_t *uaddr, u_int32_t newval)
275f08c3bdfSopenharmony_ci{
276f08c3bdfSopenharmony_ci	*uaddr = newval;
277f08c3bdfSopenharmony_ci	return newval;
278f08c3bdfSopenharmony_ci}
279f08c3bdfSopenharmony_ci
280f08c3bdfSopenharmony_ci/**
281f08c3bdfSopenharmony_ci * futex_waked_someone() - ECHCK func for TST_RETRY_FUNC
282f08c3bdfSopenharmony_ci * @ret:	return value of futex_wake
283f08c3bdfSopenharmony_ci *
284f08c3bdfSopenharmony_ci * Return value drives TST_RETRY_FUNC macro.
285f08c3bdfSopenharmony_ci */
286f08c3bdfSopenharmony_cistatic inline int
287f08c3bdfSopenharmony_cifutex_waked_someone(int ret)
288f08c3bdfSopenharmony_ci{
289f08c3bdfSopenharmony_ci	if (ret < 0)
290f08c3bdfSopenharmony_ci		tst_brk(TBROK | TERRNO, "futex_wake returned: %d", ret);
291f08c3bdfSopenharmony_ci
292f08c3bdfSopenharmony_ci	return ret;
293f08c3bdfSopenharmony_ci}
294f08c3bdfSopenharmony_ci
295f08c3bdfSopenharmony_ci#endif /* _FUTEXTEST_H */
296