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