xref: /freebsd-src/sys/compat/linux/linux_futex.c (revision 3460fab5fced39c7ea597cc7de0ebc3e4c88989a)
1ad2056f2SAlexander Leidinger /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
37f2d13d6SPedro F. Giffuni  *
46e31bed6SDmitry Chagin  * Copyright (c) 2009-2021 Dmitry Chagin <dchagin@FreeBSD.org>
56e31bed6SDmitry Chagin  * Copyright (c) 2008 Roman Divacky
6ad2056f2SAlexander Leidinger  *
7ad2056f2SAlexander Leidinger  * Redistribution and use in source and binary forms, with or without
8ad2056f2SAlexander Leidinger  * modification, are permitted provided that the following conditions
9ad2056f2SAlexander Leidinger  * are met:
10ad2056f2SAlexander Leidinger  * 1. Redistributions of source code must retain the above copyright
11ad2056f2SAlexander Leidinger  *    notice, this list of conditions and the following disclaimer.
12ad2056f2SAlexander Leidinger  * 2. Redistributions in binary form must reproduce the above copyright
13ad2056f2SAlexander Leidinger  *    notice, this list of conditions and the following disclaimer in the
14ad2056f2SAlexander Leidinger  *    documentation and/or other materials provided with the distribution.
15ad2056f2SAlexander Leidinger  *
166e31bed6SDmitry Chagin  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
176e31bed6SDmitry Chagin  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
186e31bed6SDmitry Chagin  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
196e31bed6SDmitry Chagin  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
206e31bed6SDmitry Chagin  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
216e31bed6SDmitry Chagin  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
226e31bed6SDmitry Chagin  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
236e31bed6SDmitry Chagin  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
246e31bed6SDmitry Chagin  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
256e31bed6SDmitry Chagin  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
266e31bed6SDmitry Chagin  * SUCH DAMAGE.
27ad2056f2SAlexander Leidinger  */
28ad2056f2SAlexander Leidinger 
29ad2056f2SAlexander Leidinger #include <sys/param.h>
304732e446SRoman Divacky #include <sys/imgact.h>
31cf8d74e3SDmitry Chagin #include <sys/imgact_elf.h>
32ca8c3e7bSDmitry Chagin #include <sys/ktr.h>
33d8e53d94SDmitry Chagin #include <sys/lock.h>
34ad2056f2SAlexander Leidinger #include <sys/mutex.h>
354732e446SRoman Divacky #include <sys/priv.h>
36731aded8SDmitry Chagin #include <sys/proc.h>
374732e446SRoman Divacky #include <sys/sched.h>
38513eb69eSDmitry Chagin #include <sys/sysent.h>
39d8e53d94SDmitry Chagin #include <sys/vnode.h>
40af29f399SDmitry Chagin #include <sys/umtxvar.h>
41ad2056f2SAlexander Leidinger 
42ad2056f2SAlexander Leidinger #ifdef COMPAT_LINUX32
43ad2056f2SAlexander Leidinger #include <machine/../linux32/linux.h>
44ad2056f2SAlexander Leidinger #include <machine/../linux32/linux32_proto.h>
45ad2056f2SAlexander Leidinger #else
46ad2056f2SAlexander Leidinger #include <machine/../linux/linux.h>
47ad2056f2SAlexander Leidinger #include <machine/../linux/linux_proto.h>
48ad2056f2SAlexander Leidinger #endif
493b8cbbdeSDmitry Chagin #include <compat/linux/linux_emul.h>
5049fa1a74SDmitry Chagin #include <compat/linux/linux_futex.h>
517a718f29SDmitry Chagin #include <compat/linux/linux_misc.h>
52c8a79231SDmitry Chagin #include <compat/linux/linux_time.h>
53ca8c3e7bSDmitry Chagin #include <compat/linux/linux_util.h>
54ad2056f2SAlexander Leidinger 
5521f8f506SJohn Baldwin #define	FUTEX_SHARED	0x8     /* shared futex */
56b04f5d18SDmitry Chagin #define	FUTEX_UNOWNED	0
57ad2056f2SAlexander Leidinger 
580dc38e33SDmitry Chagin #define	GET_SHARED(a)	(a->flags & FUTEX_SHARED) ? AUTO_SHARE : THREAD_SHARE
59ad2056f2SAlexander Leidinger 
6007d10893SDmitry Chagin static int futex_atomic_op(struct thread *, int, uint32_t *, int *);
614f34dc64SDmitry Chagin static int handle_futex_death(struct thread *td, struct linux_emuldata *,
62b59cf25eSDmitry Chagin     uint32_t *, unsigned int, bool);
637e947cccSDmitry Chagin static int fetch_robust_entry(struct linux_robust_list **,
647e947cccSDmitry Chagin     struct linux_robust_list **, unsigned int *);
657e947cccSDmitry Chagin 
662e46d0c3SDmitry Chagin struct linux_futex_args {
672e46d0c3SDmitry Chagin 	uint32_t	*uaddr;
682e46d0c3SDmitry Chagin 	int32_t		op;
691866eef4SDmitry Chagin 	uint32_t	flags;
701866eef4SDmitry Chagin 	bool		clockrt;
712e46d0c3SDmitry Chagin 	uint32_t	val;
722e46d0c3SDmitry Chagin 	struct timespec	*ts;
732e46d0c3SDmitry Chagin 	uint32_t	*uaddr2;
742e46d0c3SDmitry Chagin 	uint32_t	val3;
75cf8d74e3SDmitry Chagin 	bool		val3_compare;
76f8d72f53SDmitry Chagin 	struct timespec	kts;
772e46d0c3SDmitry Chagin };
78f8d72f53SDmitry Chagin 
79dad10770SDmitry Chagin static inline int futex_key_get(const void *, int, int, struct umtx_key *);
800dc38e33SDmitry Chagin static void linux_umtx_abs_timeout_init(struct umtx_abs_timeout *,
810dc38e33SDmitry Chagin 	    struct linux_futex_args *);
822e46d0c3SDmitry Chagin static int linux_futex(struct thread *, struct linux_futex_args *);
83f6b0d275SDmitry Chagin static int linux_futex_wait(struct thread *, struct linux_futex_args *);
8419f7e2c2SDmitry Chagin static int linux_futex_wake(struct thread *, struct linux_futex_args *);
85bb62a919SDmitry Chagin static int linux_futex_requeue(struct thread *, struct linux_futex_args *);
864c361d7aSDmitry Chagin static int linux_futex_wakeop(struct thread *, struct linux_futex_args *);
877a718f29SDmitry Chagin static int linux_futex_lock_pi(struct thread *, bool, struct linux_futex_args *);
887a718f29SDmitry Chagin static int linux_futex_unlock_pi(struct thread *, bool,
897a718f29SDmitry Chagin 	    struct linux_futex_args *);
907a718f29SDmitry Chagin static int futex_wake_pi(struct thread *, uint32_t *, bool);
91f8d72f53SDmitry Chagin 
92dad10770SDmitry Chagin static int
futex_key_get(const void * uaddr,int type,int share,struct umtx_key * key)93dad10770SDmitry Chagin futex_key_get(const void *uaddr, int type, int share, struct umtx_key *key)
94dad10770SDmitry Chagin {
95dad10770SDmitry Chagin 
96dad10770SDmitry Chagin 	/* Check that futex address is a 32bit aligned. */
97dad10770SDmitry Chagin 	if (!__is_aligned(uaddr, sizeof(uint32_t)))
98dad10770SDmitry Chagin 		return (EINVAL);
99dad10770SDmitry Chagin 	return (umtx_key_get(uaddr, type, share, key));
100dad10770SDmitry Chagin }
101dad10770SDmitry Chagin 
1020dc38e33SDmitry Chagin int
futex_wake(struct thread * td,uint32_t * uaddr,int val,bool shared)1030dc38e33SDmitry Chagin futex_wake(struct thread *td, uint32_t *uaddr, int val, bool shared)
104ad2056f2SAlexander Leidinger {
1050dc38e33SDmitry Chagin 	struct linux_futex_args args;
106ad2056f2SAlexander Leidinger 
1070dc38e33SDmitry Chagin 	bzero(&args, sizeof(args));
1080dc38e33SDmitry Chagin 	args.op = LINUX_FUTEX_WAKE;
1090dc38e33SDmitry Chagin 	args.uaddr = uaddr;
1100dc38e33SDmitry Chagin 	args.flags = shared == true ? FUTEX_SHARED : 0;
1110dc38e33SDmitry Chagin 	args.val = val;
1120dc38e33SDmitry Chagin 	args.val3 = FUTEX_BITSET_MATCH_ANY;
11379262bf1SDmitry Chagin 
1140dc38e33SDmitry Chagin 	return (linux_futex_wake(td, &args));
11579262bf1SDmitry Chagin }
11679262bf1SDmitry Chagin 
11779262bf1SDmitry Chagin static int
futex_wake_pi(struct thread * td,uint32_t * uaddr,bool shared)1187a718f29SDmitry Chagin futex_wake_pi(struct thread *td, uint32_t *uaddr, bool shared)
1197a718f29SDmitry Chagin {
1207a718f29SDmitry Chagin 	struct linux_futex_args args;
1217a718f29SDmitry Chagin 
1227a718f29SDmitry Chagin 	bzero(&args, sizeof(args));
1237a718f29SDmitry Chagin 	args.op = LINUX_FUTEX_UNLOCK_PI;
1247a718f29SDmitry Chagin 	args.uaddr = uaddr;
1257a718f29SDmitry Chagin 	args.flags = shared == true ? FUTEX_SHARED : 0;
1267a718f29SDmitry Chagin 
1277a718f29SDmitry Chagin 	return (linux_futex_unlock_pi(td, true, &args));
1287a718f29SDmitry Chagin }
1297a718f29SDmitry Chagin 
1307a718f29SDmitry Chagin static int
futex_atomic_op(struct thread * td,int encoded_op,uint32_t * uaddr,int * res)13107d10893SDmitry Chagin futex_atomic_op(struct thread *td, int encoded_op, uint32_t *uaddr,
13207d10893SDmitry Chagin     int *res)
133ad2056f2SAlexander Leidinger {
134ad2056f2SAlexander Leidinger 	int op = (encoded_op >> 28) & 7;
135ad2056f2SAlexander Leidinger 	int cmp = (encoded_op >> 24) & 15;
136ad2056f2SAlexander Leidinger 	int oparg = (encoded_op << 8) >> 20;
137ad2056f2SAlexander Leidinger 	int cmparg = (encoded_op << 20) >> 20;
138ad2056f2SAlexander Leidinger 	int oldval = 0, ret;
139ad2056f2SAlexander Leidinger 
140ad2056f2SAlexander Leidinger 	if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
141ad2056f2SAlexander Leidinger 		oparg = 1 << oparg;
142ad2056f2SAlexander Leidinger 
143ad2056f2SAlexander Leidinger 	switch (op) {
144ad2056f2SAlexander Leidinger 	case FUTEX_OP_SET:
145ad2056f2SAlexander Leidinger 		ret = futex_xchgl(oparg, uaddr, &oldval);
146ad2056f2SAlexander Leidinger 		break;
147ad2056f2SAlexander Leidinger 	case FUTEX_OP_ADD:
148ad2056f2SAlexander Leidinger 		ret = futex_addl(oparg, uaddr, &oldval);
149ad2056f2SAlexander Leidinger 		break;
150ad2056f2SAlexander Leidinger 	case FUTEX_OP_OR:
151ad2056f2SAlexander Leidinger 		ret = futex_orl(oparg, uaddr, &oldval);
152ad2056f2SAlexander Leidinger 		break;
153ad2056f2SAlexander Leidinger 	case FUTEX_OP_ANDN:
154a328699bSJung-uk Kim 		ret = futex_andl(~oparg, uaddr, &oldval);
155ad2056f2SAlexander Leidinger 		break;
156ad2056f2SAlexander Leidinger 	case FUTEX_OP_XOR:
157ad2056f2SAlexander Leidinger 		ret = futex_xorl(oparg, uaddr, &oldval);
158ad2056f2SAlexander Leidinger 		break;
159ad2056f2SAlexander Leidinger 	default:
16007d10893SDmitry Chagin 		ret = ENOSYS;
1611c182de9SKonstantin Belousov 		break;
162ad2056f2SAlexander Leidinger 	}
163ad2056f2SAlexander Leidinger 
16407d10893SDmitry Chagin 	if (ret != 0)
1651c182de9SKonstantin Belousov 		return (ret);
1661c182de9SKonstantin Belousov 
167ad2056f2SAlexander Leidinger 	switch (cmp) {
168ad2056f2SAlexander Leidinger 	case FUTEX_OP_CMP_EQ:
16907d10893SDmitry Chagin 		*res = (oldval == cmparg);
17019e252baSAlexander Leidinger 		break;
171ad2056f2SAlexander Leidinger 	case FUTEX_OP_CMP_NE:
17207d10893SDmitry Chagin 		*res = (oldval != cmparg);
17319e252baSAlexander Leidinger 		break;
174ad2056f2SAlexander Leidinger 	case FUTEX_OP_CMP_LT:
17507d10893SDmitry Chagin 		*res = (oldval < cmparg);
17619e252baSAlexander Leidinger 		break;
177ad2056f2SAlexander Leidinger 	case FUTEX_OP_CMP_GE:
17807d10893SDmitry Chagin 		*res = (oldval >= cmparg);
17919e252baSAlexander Leidinger 		break;
180ad2056f2SAlexander Leidinger 	case FUTEX_OP_CMP_LE:
18107d10893SDmitry Chagin 		*res = (oldval <= cmparg);
18219e252baSAlexander Leidinger 		break;
183ad2056f2SAlexander Leidinger 	case FUTEX_OP_CMP_GT:
18407d10893SDmitry Chagin 		*res = (oldval > cmparg);
18519e252baSAlexander Leidinger 		break;
186a328699bSJung-uk Kim 	default:
18707d10893SDmitry Chagin 		ret = ENOSYS;
188a328699bSJung-uk Kim 	}
18919e252baSAlexander Leidinger 
19019e252baSAlexander Leidinger 	return (ret);
191a328699bSJung-uk Kim }
1924732e446SRoman Divacky 
1932e46d0c3SDmitry Chagin static int
linux_futex(struct thread * td,struct linux_futex_args * args)1942e46d0c3SDmitry Chagin linux_futex(struct thread *td, struct linux_futex_args *args)
19579262bf1SDmitry Chagin {
196bc273677SDmitry Chagin 	struct linux_pemuldata *pem;
197cf8d74e3SDmitry Chagin 	struct proc *p;
19879262bf1SDmitry Chagin 
19921f8f506SJohn Baldwin 	if (args->op & LINUX_FUTEX_PRIVATE_FLAG) {
2001866eef4SDmitry Chagin 		args->flags = 0;
20121f8f506SJohn Baldwin 		args->op &= ~LINUX_FUTEX_PRIVATE_FLAG;
20221f8f506SJohn Baldwin 	} else
2031866eef4SDmitry Chagin 		args->flags = FUTEX_SHARED;
204be44a97cSAlexander Leidinger 
2051866eef4SDmitry Chagin 	args->clockrt = args->op & LINUX_FUTEX_CLOCK_REALTIME;
206be44a97cSAlexander Leidinger 	args->op = args->op & ~LINUX_FUTEX_CLOCK_REALTIME;
20779262bf1SDmitry Chagin 
208626cbd46SDmitry Chagin 	if (args->clockrt &&
209626cbd46SDmitry Chagin 	    args->op != LINUX_FUTEX_WAIT_BITSET &&
210626cbd46SDmitry Chagin 	    args->op != LINUX_FUTEX_WAIT_REQUEUE_PI &&
211626cbd46SDmitry Chagin 	    args->op != LINUX_FUTEX_LOCK_PI2)
212626cbd46SDmitry Chagin 		return (ENOSYS);
213626cbd46SDmitry Chagin 
21479262bf1SDmitry Chagin 	switch (args->op) {
21579262bf1SDmitry Chagin 	case LINUX_FUTEX_WAIT:
21651637623SDmitry Chagin 		args->val3 = FUTEX_BITSET_MATCH_ANY;
21751637623SDmitry Chagin 		/* FALLTHROUGH */
21879262bf1SDmitry Chagin 
21951637623SDmitry Chagin 	case LINUX_FUTEX_WAIT_BITSET:
22032fd4465SDmitry Chagin 		LINUX_CTR3(sys_futex, "WAIT uaddr %p val 0x%x bitset 0x%x",
22151637623SDmitry Chagin 		    args->uaddr, args->val, args->val3);
22219e252baSAlexander Leidinger 
223f6b0d275SDmitry Chagin 		return (linux_futex_wait(td, args));
22479262bf1SDmitry Chagin 
22579262bf1SDmitry Chagin 	case LINUX_FUTEX_WAKE:
22651637623SDmitry Chagin 		args->val3 = FUTEX_BITSET_MATCH_ANY;
22751637623SDmitry Chagin 		/* FALLTHROUGH */
22879262bf1SDmitry Chagin 
22951637623SDmitry Chagin 	case LINUX_FUTEX_WAKE_BITSET:
23032fd4465SDmitry Chagin 		LINUX_CTR3(sys_futex, "WAKE uaddr %p nrwake 0x%x bitset 0x%x",
23151637623SDmitry Chagin 		    args->uaddr, args->val, args->val3);
232ca8c3e7bSDmitry Chagin 
23319f7e2c2SDmitry Chagin 		return (linux_futex_wake(td, args));
23479262bf1SDmitry Chagin 
235cf8d74e3SDmitry Chagin 	case LINUX_FUTEX_REQUEUE:
236cf8d74e3SDmitry Chagin 		/*
237cf8d74e3SDmitry Chagin 		 * Glibc does not use this operation since version 2.3.3,
238cf8d74e3SDmitry Chagin 		 * as it is racy and replaced by FUTEX_CMP_REQUEUE operation.
239cf8d74e3SDmitry Chagin 		 * Glibc versions prior to 2.3.3 fall back to FUTEX_WAKE when
240cf8d74e3SDmitry Chagin 		 * FUTEX_REQUEUE returned EINVAL.
241cf8d74e3SDmitry Chagin 		 */
242cf8d74e3SDmitry Chagin 		pem = pem_find(td->td_proc);
243cf8d74e3SDmitry Chagin 		if ((pem->flags & LINUX_XDEPR_REQUEUEOP) == 0) {
244cf8d74e3SDmitry Chagin 			linux_msg(td, "unsupported FUTEX_REQUEUE");
245cf8d74e3SDmitry Chagin 			pem->flags |= LINUX_XDEPR_REQUEUEOP;
246cf8d74e3SDmitry Chagin 		}
247cf8d74e3SDmitry Chagin 
248cf8d74e3SDmitry Chagin 		/*
249cf8d74e3SDmitry Chagin 		 * The above is true, however musl libc does make use of the
250cf8d74e3SDmitry Chagin 		 * futex requeue operation, allow operation for brands which
251cf8d74e3SDmitry Chagin 		 * set LINUX_BI_FUTEX_REQUEUE bit of Brandinfo flags.
252cf8d74e3SDmitry Chagin 		 */
253cf8d74e3SDmitry Chagin 		p = td->td_proc;
254cf8d74e3SDmitry Chagin 		Elf_Brandinfo *bi = p->p_elf_brandinfo;
255cf8d74e3SDmitry Chagin 		if (bi == NULL || ((bi->flags & LINUX_BI_FUTEX_REQUEUE)) == 0)
256cf8d74e3SDmitry Chagin 			return (EINVAL);
257cf8d74e3SDmitry Chagin 		args->val3_compare = false;
258cf8d74e3SDmitry Chagin 		/* FALLTHROUGH */
259cf8d74e3SDmitry Chagin 
26079262bf1SDmitry Chagin 	case LINUX_FUTEX_CMP_REQUEUE:
261ca8c3e7bSDmitry Chagin 		LINUX_CTR5(sys_futex, "CMP_REQUEUE uaddr %p "
26232fd4465SDmitry Chagin 		    "nrwake 0x%x uval 0x%x uaddr2 %p nrequeue 0x%x",
263ca8c3e7bSDmitry Chagin 		    args->uaddr, args->val, args->val3, args->uaddr2,
2642e46d0c3SDmitry Chagin 		    args->ts);
265ca8c3e7bSDmitry Chagin 
266bb62a919SDmitry Chagin 		return (linux_futex_requeue(td, args));
26779262bf1SDmitry Chagin 
26879262bf1SDmitry Chagin 	case LINUX_FUTEX_WAKE_OP:
269ca8c3e7bSDmitry Chagin 		LINUX_CTR5(sys_futex, "WAKE_OP "
27032fd4465SDmitry Chagin 		    "uaddr %p nrwake 0x%x uaddr2 %p op 0x%x nrwake2 0x%x",
27132fd4465SDmitry Chagin 		    args->uaddr, args->val, args->uaddr2, args->val3,
2722e46d0c3SDmitry Chagin 		    args->ts);
273ca8c3e7bSDmitry Chagin 
2744c361d7aSDmitry Chagin 		return (linux_futex_wakeop(td, args));
27579262bf1SDmitry Chagin 
27679262bf1SDmitry Chagin 	case LINUX_FUTEX_LOCK_PI:
2777a718f29SDmitry Chagin 		args->clockrt = true;
278a9bb1b1cSDmitry Chagin 		/* FALLTHROUGH */
279a9bb1b1cSDmitry Chagin 
280a9bb1b1cSDmitry Chagin 	case LINUX_FUTEX_LOCK_PI2:
2817a718f29SDmitry Chagin 		LINUX_CTR2(sys_futex, "LOCKPI uaddr %p val 0x%x",
2827a718f29SDmitry Chagin 		    args->uaddr, args->val);
2837a718f29SDmitry Chagin 
2847a718f29SDmitry Chagin 		return (linux_futex_lock_pi(td, false, args));
28579262bf1SDmitry Chagin 
28679262bf1SDmitry Chagin 	case LINUX_FUTEX_UNLOCK_PI:
2877a718f29SDmitry Chagin 		LINUX_CTR1(sys_futex, "UNLOCKPI uaddr %p",
2887a718f29SDmitry Chagin 		    args->uaddr);
2897a718f29SDmitry Chagin 
2907a718f29SDmitry Chagin 		return (linux_futex_unlock_pi(td, false, args));
29179262bf1SDmitry Chagin 
29279262bf1SDmitry Chagin 	case LINUX_FUTEX_TRYLOCK_PI:
2937a718f29SDmitry Chagin 		LINUX_CTR1(sys_futex, "TRYLOCKPI uaddr %p",
2947a718f29SDmitry Chagin 		    args->uaddr);
2957a718f29SDmitry Chagin 
2967a718f29SDmitry Chagin 		return (linux_futex_lock_pi(td, true, args));
29779262bf1SDmitry Chagin 
298d90df8acSDmitry Chagin 	/*
299d90df8acSDmitry Chagin 	 * Current implementation of FUTEX_WAIT_REQUEUE_PI and FUTEX_CMP_REQUEUE_PI
300d90df8acSDmitry Chagin 	 * can't be used anymore to implement conditional variables.
301d90df8acSDmitry Chagin 	 * A detailed explanation can be found here:
302d90df8acSDmitry Chagin 	 *
303d90df8acSDmitry Chagin 	 * https://sourceware.org/bugzilla/show_bug.cgi?id=13165
304d90df8acSDmitry Chagin 	 * and here http://austingroupbugs.net/view.php?id=609
305d90df8acSDmitry Chagin 	 *
306d90df8acSDmitry Chagin 	 * And since commit
307d90df8acSDmitry Chagin 	 * https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=ed19993b5b0d05d62cc883571519a67dae481a14
308d4da6928SDmitry Chagin 	 * glibc does not use them.
309d90df8acSDmitry Chagin 	 */
310be44a97cSAlexander Leidinger 	case LINUX_FUTEX_WAIT_REQUEUE_PI:
311be44a97cSAlexander Leidinger 		/* not yet implemented */
3125dd1d097SDmitry Chagin 		pem = pem_find(td->td_proc);
3135dd1d097SDmitry Chagin 		if ((pem->flags & LINUX_XUNSUP_FUTEXPIOP) == 0) {
314433d61a5SEdward Tomasz Napierala 			linux_msg(td, "unsupported FUTEX_WAIT_REQUEUE_PI");
3155dd1d097SDmitry Chagin 			pem->flags |= LINUX_XUNSUP_FUTEXPIOP;
3165dd1d097SDmitry Chagin 		}
317be44a97cSAlexander Leidinger 		return (ENOSYS);
318be44a97cSAlexander Leidinger 
31978ec1867SDmitry Chagin 	case LINUX_FUTEX_CMP_REQUEUE_PI:
32078ec1867SDmitry Chagin 		/* not yet implemented */
3215dd1d097SDmitry Chagin 		pem = pem_find(td->td_proc);
3225dd1d097SDmitry Chagin 		if ((pem->flags & LINUX_XUNSUP_FUTEXPIOP) == 0) {
323433d61a5SEdward Tomasz Napierala 			linux_msg(td, "unsupported FUTEX_CMP_REQUEUE_PI");
3245dd1d097SDmitry Chagin 			pem->flags |= LINUX_XUNSUP_FUTEXPIOP;
3255dd1d097SDmitry Chagin 		}
32678ec1867SDmitry Chagin 		return (ENOSYS);
32778ec1867SDmitry Chagin 
32879262bf1SDmitry Chagin 	default:
32986e794ebSEdward Tomasz Napierala 		linux_msg(td, "unsupported futex op %d", args->op);
33079262bf1SDmitry Chagin 		return (ENOSYS);
33179262bf1SDmitry Chagin 	}
3324c361d7aSDmitry Chagin }
33379262bf1SDmitry Chagin 
3347a718f29SDmitry Chagin /*
3357a718f29SDmitry Chagin  * pi protocol:
3367a718f29SDmitry Chagin  * - 0 futex word value means unlocked.
3377a718f29SDmitry Chagin  * - TID futex word value means locked.
3387a718f29SDmitry Chagin  * Userspace uses atomic ops to lock/unlock these futexes without entering the
3397a718f29SDmitry Chagin  * kernel. If the lock-acquire fastpath fails, (transition from 0 to TID fails),
3407a718f29SDmitry Chagin  * then FUTEX_LOCK_PI is called.
3417a718f29SDmitry Chagin  * The kernel atomically set FUTEX_WAITERS bit in the futex word value, if no
3427a718f29SDmitry Chagin  * other waiters exists looks up the thread that owns the futex (it has put its
3437a718f29SDmitry Chagin  * own TID into the futex value) and made this thread the owner of the internal
3447a718f29SDmitry Chagin  * pi-aware lock object (mutex). Then the kernel tries to lock the internal lock
3457a718f29SDmitry Chagin  * object, on which it blocks. Once it returns, it has the mutex acquired, and it
3467a718f29SDmitry Chagin  * sets the futex value to its own TID and returns (futex value contains
3477a718f29SDmitry Chagin  * FUTEX_WAITERS|TID).
3487a718f29SDmitry Chagin  * The unlock fastpath would fail (because the FUTEX_WAITERS bit is set) and
3497a718f29SDmitry Chagin  * FUTEX_UNLOCK_PI will be called.
3507a718f29SDmitry Chagin  * If a futex is found to be held at exit time, the kernel sets the OWNER_DIED
3517a718f29SDmitry Chagin  * bit of the futex word and wakes up the next futex waiter (if any), WAITERS
3527a718f29SDmitry Chagin  * bit is preserved (if any).
3537a718f29SDmitry Chagin  * If OWNER_DIED bit is set the kernel sanity checks the futex word value against
3547a718f29SDmitry Chagin  * the internal futex state and if correct, acquire futex.
3557a718f29SDmitry Chagin  */
3567a718f29SDmitry Chagin static int
linux_futex_lock_pi(struct thread * td,bool try,struct linux_futex_args * args)3577a718f29SDmitry Chagin linux_futex_lock_pi(struct thread *td, bool try, struct linux_futex_args *args)
3587a718f29SDmitry Chagin {
3597a718f29SDmitry Chagin 	struct umtx_abs_timeout timo;
3607a718f29SDmitry Chagin 	struct linux_emuldata *em;
3617a718f29SDmitry Chagin 	struct umtx_pi *pi, *new_pi;
3627a718f29SDmitry Chagin 	struct thread *td1;
3637a718f29SDmitry Chagin 	struct umtx_q *uq;
3647a718f29SDmitry Chagin 	int error, rv;
3657a718f29SDmitry Chagin 	uint32_t owner, old_owner;
3667a718f29SDmitry Chagin 
3677a718f29SDmitry Chagin 	em = em_find(td);
3687a718f29SDmitry Chagin 	uq = td->td_umtxq;
369dad10770SDmitry Chagin 	error = futex_key_get(args->uaddr, TYPE_PI_FUTEX, GET_SHARED(args),
3707a718f29SDmitry Chagin 	    &uq->uq_key);
3717a718f29SDmitry Chagin 	if (error != 0)
3727a718f29SDmitry Chagin 		return (error);
3737a718f29SDmitry Chagin 	if (args->ts != NULL)
3747a718f29SDmitry Chagin 		linux_umtx_abs_timeout_init(&timo, args);
3757a718f29SDmitry Chagin 
3767a718f29SDmitry Chagin 	umtxq_lock(&uq->uq_key);
3777a718f29SDmitry Chagin 	pi = umtx_pi_lookup(&uq->uq_key);
3787a718f29SDmitry Chagin 	if (pi == NULL) {
3797a718f29SDmitry Chagin 		new_pi = umtx_pi_alloc(M_NOWAIT);
3807a718f29SDmitry Chagin 		if (new_pi == NULL) {
3817a718f29SDmitry Chagin 			umtxq_unlock(&uq->uq_key);
3827a718f29SDmitry Chagin 			new_pi = umtx_pi_alloc(M_WAITOK);
3837a718f29SDmitry Chagin 			umtxq_lock(&uq->uq_key);
3847a718f29SDmitry Chagin 			pi = umtx_pi_lookup(&uq->uq_key);
3857a718f29SDmitry Chagin 			if (pi != NULL) {
3867a718f29SDmitry Chagin 				umtx_pi_free(new_pi);
3877a718f29SDmitry Chagin 				new_pi = NULL;
3887a718f29SDmitry Chagin 			}
3897a718f29SDmitry Chagin 		}
3907a718f29SDmitry Chagin 		if (new_pi != NULL) {
3917a718f29SDmitry Chagin 			new_pi->pi_key = uq->uq_key;
3927a718f29SDmitry Chagin 			umtx_pi_insert(new_pi);
3937a718f29SDmitry Chagin 			pi = new_pi;
3947a718f29SDmitry Chagin 		}
3957a718f29SDmitry Chagin 	}
3967a718f29SDmitry Chagin 	umtx_pi_ref(pi);
3977a718f29SDmitry Chagin 	umtxq_unlock(&uq->uq_key);
3987a718f29SDmitry Chagin 	for (;;) {
3997a718f29SDmitry Chagin 		/* Try uncontested case first. */
400b04f5d18SDmitry Chagin 		rv = casueword32(args->uaddr, FUTEX_UNOWNED, &owner, em->em_tid);
4017a718f29SDmitry Chagin 		/* The acquire succeeded. */
4027a718f29SDmitry Chagin 		if (rv == 0) {
4037a718f29SDmitry Chagin 			error = 0;
4047a718f29SDmitry Chagin 			break;
4057a718f29SDmitry Chagin 		}
4067a718f29SDmitry Chagin 		if (rv == -1) {
4077a718f29SDmitry Chagin 			error = EFAULT;
4087a718f29SDmitry Chagin 			break;
4097a718f29SDmitry Chagin 		}
4107a718f29SDmitry Chagin 
4117a718f29SDmitry Chagin 		/*
4122cd66206SDmitry Chagin 		 * Nobody owns it, but the acquire failed. This can happen
4132cd66206SDmitry Chagin 		 * with ll/sc atomic.
4142cd66206SDmitry Chagin 		 */
415b04f5d18SDmitry Chagin 		if (owner == FUTEX_UNOWNED) {
4162cd66206SDmitry Chagin 			error = thread_check_susp(td, true);
4172cd66206SDmitry Chagin 			if (error != 0)
4182cd66206SDmitry Chagin 				break;
4192cd66206SDmitry Chagin 			continue;
4202cd66206SDmitry Chagin 		}
4212cd66206SDmitry Chagin 
4222cd66206SDmitry Chagin 		/*
4237a718f29SDmitry Chagin 		 * Avoid overwriting a possible error from sleep due
4247a718f29SDmitry Chagin 		 * to the pending signal with suspension check result.
4257a718f29SDmitry Chagin 		 */
4267a718f29SDmitry Chagin 		if (error == 0) {
4277a718f29SDmitry Chagin 			error = thread_check_susp(td, true);
4287a718f29SDmitry Chagin 			if (error != 0)
4297a718f29SDmitry Chagin 				break;
4307a718f29SDmitry Chagin 		}
4317a718f29SDmitry Chagin 
4327a718f29SDmitry Chagin 		/* The futex word at *uaddr is already locked by the caller. */
4337a718f29SDmitry Chagin 		if ((owner & FUTEX_TID_MASK) == em->em_tid) {
4347a718f29SDmitry Chagin 			error = EDEADLK;
4357a718f29SDmitry Chagin 			break;
4367a718f29SDmitry Chagin 		}
4377a718f29SDmitry Chagin 
4387a718f29SDmitry Chagin 		/*
4397a718f29SDmitry Chagin 		 * Futex owner died, handle_futex_death() set the OWNER_DIED bit
4407a718f29SDmitry Chagin 		 * and clear tid. Try to acquire it.
4417a718f29SDmitry Chagin 		 */
442b04f5d18SDmitry Chagin 		if ((owner & FUTEX_TID_MASK) == FUTEX_UNOWNED) {
4437a718f29SDmitry Chagin 			old_owner = owner;
4447a718f29SDmitry Chagin 			owner = owner & (FUTEX_WAITERS | FUTEX_OWNER_DIED);
4457a718f29SDmitry Chagin 			owner |= em->em_tid;
4467a718f29SDmitry Chagin 			rv = casueword32(args->uaddr, old_owner, &owner, owner);
4477a718f29SDmitry Chagin 			if (rv == -1) {
4487a718f29SDmitry Chagin 				error = EFAULT;
4497a718f29SDmitry Chagin 				break;
4507a718f29SDmitry Chagin 			}
4517a718f29SDmitry Chagin 			if (rv == 1) {
4527a718f29SDmitry Chagin 				if (error == 0) {
4537a718f29SDmitry Chagin 					error = thread_check_susp(td, true);
4547a718f29SDmitry Chagin 					if (error != 0)
4557a718f29SDmitry Chagin 						break;
4567a718f29SDmitry Chagin 				}
4577a718f29SDmitry Chagin 
4587a718f29SDmitry Chagin 				/*
4597a718f29SDmitry Chagin 				 * If this failed the lock could
4607a718f29SDmitry Chagin 				 * changed, restart.
4617a718f29SDmitry Chagin 				 */
4627a718f29SDmitry Chagin 				continue;
4637a718f29SDmitry Chagin 			}
4647a718f29SDmitry Chagin 
4657a718f29SDmitry Chagin 			umtxq_lock(&uq->uq_key);
4667a718f29SDmitry Chagin 			umtxq_busy(&uq->uq_key);
4677a718f29SDmitry Chagin 			error = umtx_pi_claim(pi, td);
4687a718f29SDmitry Chagin 			umtxq_unbusy(&uq->uq_key);
4697a718f29SDmitry Chagin 			umtxq_unlock(&uq->uq_key);
4707a718f29SDmitry Chagin 			if (error != 0) {
4717a718f29SDmitry Chagin 				/*
4727a718f29SDmitry Chagin 				 * Since we're going to return an
4737a718f29SDmitry Chagin 				 * error, restore the futex to its
4747a718f29SDmitry Chagin 				 * previous, unowned state to avoid
4757a718f29SDmitry Chagin 				 * compounding the problem.
4767a718f29SDmitry Chagin 				 */
4777a718f29SDmitry Chagin 				(void)casuword32(args->uaddr, owner, old_owner);
4787a718f29SDmitry Chagin 			}
4797a718f29SDmitry Chagin 			break;
4807a718f29SDmitry Chagin 		}
4817a718f29SDmitry Chagin 
4827a718f29SDmitry Chagin 		/*
4837a718f29SDmitry Chagin 		 * Inconsistent state: OWNER_DIED is set and tid is not 0.
4847a718f29SDmitry Chagin 		 * Linux does some checks of futex state, we return EINVAL,
4857a718f29SDmitry Chagin 		 * as the user space can take care of this.
4867a718f29SDmitry Chagin 		 */
487b04f5d18SDmitry Chagin 		if ((owner & FUTEX_OWNER_DIED) != FUTEX_UNOWNED) {
4887a718f29SDmitry Chagin 			error = EINVAL;
4897a718f29SDmitry Chagin 			break;
4907a718f29SDmitry Chagin 		}
4917a718f29SDmitry Chagin 
4927a718f29SDmitry Chagin 		if (try != 0) {
4937a718f29SDmitry Chagin 			error = EBUSY;
4947a718f29SDmitry Chagin 			break;
4957a718f29SDmitry Chagin 		}
4967a718f29SDmitry Chagin 
4977a718f29SDmitry Chagin 		/*
4987a718f29SDmitry Chagin 		 * If we caught a signal, we have retried and now
4997a718f29SDmitry Chagin 		 * exit immediately.
5007a718f29SDmitry Chagin 		 */
5017a718f29SDmitry Chagin 		if (error != 0)
5027a718f29SDmitry Chagin 			break;
5037a718f29SDmitry Chagin 
5047a718f29SDmitry Chagin 		umtxq_lock(&uq->uq_key);
5057a718f29SDmitry Chagin 		umtxq_busy(&uq->uq_key);
5067a718f29SDmitry Chagin 		umtxq_unlock(&uq->uq_key);
5077a718f29SDmitry Chagin 
5087a718f29SDmitry Chagin 		/*
5097a718f29SDmitry Chagin 		 * Set the contested bit so that a release in user space knows
5107a718f29SDmitry Chagin 		 * to use the system call for unlock. If this fails either some
5117a718f29SDmitry Chagin 		 * one else has acquired the lock or it has been released.
5127a718f29SDmitry Chagin 		 */
5137a718f29SDmitry Chagin 		rv = casueword32(args->uaddr, owner, &owner,
5147a718f29SDmitry Chagin 		    owner | FUTEX_WAITERS);
5157a718f29SDmitry Chagin 		if (rv == -1) {
5167a718f29SDmitry Chagin 			umtxq_unbusy_unlocked(&uq->uq_key);
5177a718f29SDmitry Chagin 			error = EFAULT;
5187a718f29SDmitry Chagin 			break;
5197a718f29SDmitry Chagin 		}
5207a718f29SDmitry Chagin 		if (rv == 1) {
5217a718f29SDmitry Chagin 			umtxq_unbusy_unlocked(&uq->uq_key);
5227a718f29SDmitry Chagin 			error = thread_check_susp(td, true);
5237a718f29SDmitry Chagin 			if (error != 0)
5247a718f29SDmitry Chagin 				break;
5257a718f29SDmitry Chagin 
5267a718f29SDmitry Chagin 			/*
5277a718f29SDmitry Chagin 			 * The lock changed and we need to retry or we
5287a718f29SDmitry Chagin 			 * lost a race to the thread unlocking the umtx.
5297a718f29SDmitry Chagin 			 */
5307a718f29SDmitry Chagin 			continue;
5317a718f29SDmitry Chagin 		}
5327a718f29SDmitry Chagin 
5337a718f29SDmitry Chagin 		/*
5347a718f29SDmitry Chagin 		 * Substitute Linux thread id by native thread id to
5357a718f29SDmitry Chagin 		 * avoid refactoring code of umtxq_sleep_pi().
5367a718f29SDmitry Chagin 		 */
5377a718f29SDmitry Chagin 		td1 = linux_tdfind(td, owner & FUTEX_TID_MASK, -1);
5387a718f29SDmitry Chagin 		if (td1 != NULL) {
5397a718f29SDmitry Chagin 			owner = td1->td_tid;
5407a718f29SDmitry Chagin 			PROC_UNLOCK(td1->td_proc);
5417a718f29SDmitry Chagin 		} else {
5427a718f29SDmitry Chagin 			umtxq_unbusy_unlocked(&uq->uq_key);
5437a718f29SDmitry Chagin 			error = EINVAL;
5447a718f29SDmitry Chagin 			break;
5457a718f29SDmitry Chagin 		}
5467a718f29SDmitry Chagin 
5477a718f29SDmitry Chagin 		umtxq_lock(&uq->uq_key);
5487a718f29SDmitry Chagin 
5497a718f29SDmitry Chagin 		/* We set the contested bit, sleep. */
5507a718f29SDmitry Chagin 		error = umtxq_sleep_pi(uq, pi, owner, "futexp",
5517a718f29SDmitry Chagin 		    args->ts == NULL ? NULL : &timo,
5527a718f29SDmitry Chagin 		    (args->flags & FUTEX_SHARED) != 0);
5537a718f29SDmitry Chagin 		if (error != 0)
5547a718f29SDmitry Chagin 			continue;
5557a718f29SDmitry Chagin 
5567a718f29SDmitry Chagin 		error = thread_check_susp(td, false);
5577a718f29SDmitry Chagin 		if (error != 0)
5587a718f29SDmitry Chagin 			break;
5597a718f29SDmitry Chagin 	}
5607a718f29SDmitry Chagin 
5617a718f29SDmitry Chagin 	umtxq_lock(&uq->uq_key);
5627a718f29SDmitry Chagin 	umtx_pi_unref(pi);
5637a718f29SDmitry Chagin 	umtxq_unlock(&uq->uq_key);
5647a718f29SDmitry Chagin 	umtx_key_release(&uq->uq_key);
5657a718f29SDmitry Chagin 	return (error);
5667a718f29SDmitry Chagin }
5677a718f29SDmitry Chagin 
5687a718f29SDmitry Chagin static int
linux_futex_unlock_pi(struct thread * td,bool rb,struct linux_futex_args * args)5697a718f29SDmitry Chagin linux_futex_unlock_pi(struct thread *td, bool rb, struct linux_futex_args *args)
5707a718f29SDmitry Chagin {
5717a718f29SDmitry Chagin 	struct linux_emuldata *em;
5727a718f29SDmitry Chagin 	struct umtx_key key;
5737a718f29SDmitry Chagin 	uint32_t old, owner, new_owner;
5747a718f29SDmitry Chagin 	int count, error;
5757a718f29SDmitry Chagin 
5767a718f29SDmitry Chagin 	em = em_find(td);
5777a718f29SDmitry Chagin 
5787a718f29SDmitry Chagin 	/*
5797a718f29SDmitry Chagin 	 * Make sure we own this mtx.
5807a718f29SDmitry Chagin 	 */
5817a718f29SDmitry Chagin 	error = fueword32(args->uaddr, &owner);
5827a718f29SDmitry Chagin 	if (error == -1)
5837a718f29SDmitry Chagin 		return (EFAULT);
5847a718f29SDmitry Chagin 	if (!rb && (owner & FUTEX_TID_MASK) != em->em_tid)
5857a718f29SDmitry Chagin 		return (EPERM);
5867a718f29SDmitry Chagin 
587dad10770SDmitry Chagin 	error = futex_key_get(args->uaddr, TYPE_PI_FUTEX, GET_SHARED(args), &key);
5887a718f29SDmitry Chagin 	if (error != 0)
5897a718f29SDmitry Chagin 		return (error);
5907a718f29SDmitry Chagin 	umtxq_lock(&key);
5917a718f29SDmitry Chagin 	umtxq_busy(&key);
5927a718f29SDmitry Chagin 	error = umtx_pi_drop(td, &key, rb, &count);
5937a718f29SDmitry Chagin 	if (error != 0 || rb) {
5947a718f29SDmitry Chagin 		umtxq_unbusy(&key);
5957a718f29SDmitry Chagin 		umtxq_unlock(&key);
5967a718f29SDmitry Chagin 		umtx_key_release(&key);
5977a718f29SDmitry Chagin 		return (error);
5987a718f29SDmitry Chagin 	}
5997a718f29SDmitry Chagin 	umtxq_unlock(&key);
6007a718f29SDmitry Chagin 
6017a718f29SDmitry Chagin 	/*
6027a718f29SDmitry Chagin 	 * When unlocking the futex, it must be marked as unowned if
6037a718f29SDmitry Chagin 	 * there is zero or one thread only waiting for it.
6047a718f29SDmitry Chagin 	 * Otherwise, it must be marked as contested.
6057a718f29SDmitry Chagin 	 */
6067a718f29SDmitry Chagin 	if (count > 1)
6077a718f29SDmitry Chagin 		new_owner = FUTEX_WAITERS;
6087a718f29SDmitry Chagin 	else
609b04f5d18SDmitry Chagin 		new_owner = FUTEX_UNOWNED;
6107a718f29SDmitry Chagin 
6117a718f29SDmitry Chagin again:
6127a718f29SDmitry Chagin 	error = casueword32(args->uaddr, owner, &old, new_owner);
6137a718f29SDmitry Chagin 	if (error == 1) {
6147a718f29SDmitry Chagin 		error = thread_check_susp(td, false);
6157a718f29SDmitry Chagin 		if (error == 0)
6167a718f29SDmitry Chagin 			goto again;
6177a718f29SDmitry Chagin 	}
6187a718f29SDmitry Chagin 	umtxq_unbusy_unlocked(&key);
6197a718f29SDmitry Chagin 	umtx_key_release(&key);
6207a718f29SDmitry Chagin 	if (error == -1)
6217a718f29SDmitry Chagin 		return (EFAULT);
6227a718f29SDmitry Chagin 	if (error == 0 && old != owner)
6237a718f29SDmitry Chagin 		return (EINVAL);
6247a718f29SDmitry Chagin 	return (error);
6257a718f29SDmitry Chagin }
6267a718f29SDmitry Chagin 
6274c361d7aSDmitry Chagin static int
linux_futex_wakeop(struct thread * td,struct linux_futex_args * args)6284c361d7aSDmitry Chagin linux_futex_wakeop(struct thread *td, struct linux_futex_args *args)
6294c361d7aSDmitry Chagin {
6300dc38e33SDmitry Chagin 	struct umtx_key key, key2;
6314c361d7aSDmitry Chagin 	int nrwake, op_ret, ret;
6320dc38e33SDmitry Chagin 	int error, count;
6334c361d7aSDmitry Chagin 
6344c361d7aSDmitry Chagin 	if (args->uaddr == args->uaddr2)
6354c361d7aSDmitry Chagin 		return (EINVAL);
6364c361d7aSDmitry Chagin 
637dad10770SDmitry Chagin 	error = futex_key_get(args->uaddr, TYPE_FUTEX, GET_SHARED(args), &key);
6384c361d7aSDmitry Chagin 	if (error != 0)
63979262bf1SDmitry Chagin 		return (error);
640dad10770SDmitry Chagin 	error = futex_key_get(args->uaddr2, TYPE_FUTEX, GET_SHARED(args), &key2);
6414c361d7aSDmitry Chagin 	if (error != 0) {
6420dc38e33SDmitry Chagin 		umtx_key_release(&key);
6434c361d7aSDmitry Chagin 		return (error);
6444c361d7aSDmitry Chagin 	}
6450dc38e33SDmitry Chagin 	umtxq_lock(&key);
6460dc38e33SDmitry Chagin 	umtxq_busy(&key);
6470dc38e33SDmitry Chagin 	umtxq_unlock(&key);
64807d10893SDmitry Chagin 	error = futex_atomic_op(td, args->val3, args->uaddr2, &op_ret);
6490dc38e33SDmitry Chagin 	umtxq_lock(&key);
6500dc38e33SDmitry Chagin 	umtxq_unbusy(&key);
6510dc38e33SDmitry Chagin 	if (error != 0)
6520dc38e33SDmitry Chagin 		goto out;
6530dc38e33SDmitry Chagin 	ret = umtxq_signal_mask(&key, args->val, args->val3);
6540dc38e33SDmitry Chagin 	if (op_ret > 0) {
6550dc38e33SDmitry Chagin 		nrwake = (int)(unsigned long)args->ts;
6560dc38e33SDmitry Chagin 		umtxq_lock(&key2);
6570dc38e33SDmitry Chagin 		count = umtxq_count(&key2);
6580dc38e33SDmitry Chagin 		if (count > 0)
6590dc38e33SDmitry Chagin 			ret += umtxq_signal_mask(&key2, nrwake, args->val3);
6600dc38e33SDmitry Chagin 		else
6610dc38e33SDmitry Chagin 			ret += umtxq_signal_mask(&key, nrwake, args->val3);
6620dc38e33SDmitry Chagin 		umtxq_unlock(&key2);
6630dc38e33SDmitry Chagin 	}
6644c361d7aSDmitry Chagin 	td->td_retval[0] = ret;
6650dc38e33SDmitry Chagin out:
6660dc38e33SDmitry Chagin 	umtxq_unlock(&key);
6670dc38e33SDmitry Chagin 	umtx_key_release(&key2);
6680dc38e33SDmitry Chagin 	umtx_key_release(&key);
6690dc38e33SDmitry Chagin 	return (error);
67079262bf1SDmitry Chagin }
67179262bf1SDmitry Chagin 
672f6b0d275SDmitry Chagin static int
linux_futex_requeue(struct thread * td,struct linux_futex_args * args)673bb62a919SDmitry Chagin linux_futex_requeue(struct thread *td, struct linux_futex_args *args)
674bb62a919SDmitry Chagin {
675bb62a919SDmitry Chagin 	int nrwake, nrrequeue;
6760dc38e33SDmitry Chagin 	struct umtx_key key, key2;
677bb62a919SDmitry Chagin 	int error;
6780dc38e33SDmitry Chagin 	uint32_t uval;
679bb62a919SDmitry Chagin 
680bb62a919SDmitry Chagin 	/*
681bb62a919SDmitry Chagin 	 * Linux allows this, we would not, it is an incorrect
682bb62a919SDmitry Chagin 	 * usage of declared ABI, so return EINVAL.
683bb62a919SDmitry Chagin 	 */
6840dc38e33SDmitry Chagin 	if (args->uaddr == args->uaddr2)
685bb62a919SDmitry Chagin 		return (EINVAL);
686bb62a919SDmitry Chagin 
687bb62a919SDmitry Chagin 	nrrequeue = (int)(unsigned long)args->ts;
688bb62a919SDmitry Chagin 	nrwake = args->val;
689bb62a919SDmitry Chagin 	/*
690bb62a919SDmitry Chagin 	 * Sanity check to prevent signed integer overflow,
691bb62a919SDmitry Chagin 	 * see Linux CVE-2018-6927
692bb62a919SDmitry Chagin 	 */
693bb62a919SDmitry Chagin 	if (nrwake < 0 || nrrequeue < 0)
694bb62a919SDmitry Chagin 		return (EINVAL);
695bb62a919SDmitry Chagin 
696dad10770SDmitry Chagin 	error = futex_key_get(args->uaddr, TYPE_FUTEX, GET_SHARED(args), &key);
697bb62a919SDmitry Chagin 	if (error != 0)
698bb62a919SDmitry Chagin 		return (error);
699dad10770SDmitry Chagin 	error = futex_key_get(args->uaddr2, TYPE_FUTEX, GET_SHARED(args), &key2);
700bb62a919SDmitry Chagin 	if (error != 0) {
7010dc38e33SDmitry Chagin 		umtx_key_release(&key);
702bb62a919SDmitry Chagin 		return (error);
703bb62a919SDmitry Chagin 	}
7040dc38e33SDmitry Chagin 	umtxq_lock(&key);
7050dc38e33SDmitry Chagin 	umtxq_busy(&key);
7060dc38e33SDmitry Chagin 	umtxq_unlock(&key);
7070dc38e33SDmitry Chagin 	error = fueword32(args->uaddr, &uval);
7080dc38e33SDmitry Chagin 	if (error != 0)
7090dc38e33SDmitry Chagin 		error = EFAULT;
7100dc38e33SDmitry Chagin 	else if (args->val3_compare == true && uval != args->val3)
7110dc38e33SDmitry Chagin 		error = EWOULDBLOCK;
7120dc38e33SDmitry Chagin 	umtxq_lock(&key);
7130dc38e33SDmitry Chagin 	umtxq_unbusy(&key);
7140dc38e33SDmitry Chagin 	if (error == 0) {
7150dc38e33SDmitry Chagin 		umtxq_lock(&key2);
7160dc38e33SDmitry Chagin 		td->td_retval[0] = umtxq_requeue(&key, nrwake, &key2, nrrequeue);
7170dc38e33SDmitry Chagin 		umtxq_unlock(&key2);
7180dc38e33SDmitry Chagin 	}
7190dc38e33SDmitry Chagin 	umtxq_unlock(&key);
7200dc38e33SDmitry Chagin 	umtx_key_release(&key2);
7210dc38e33SDmitry Chagin 	umtx_key_release(&key);
722bb62a919SDmitry Chagin 	return (error);
723bb62a919SDmitry Chagin }
724bb62a919SDmitry Chagin 
725bb62a919SDmitry Chagin static int
linux_futex_wake(struct thread * td,struct linux_futex_args * args)72619f7e2c2SDmitry Chagin linux_futex_wake(struct thread *td, struct linux_futex_args *args)
72719f7e2c2SDmitry Chagin {
7280dc38e33SDmitry Chagin 	struct umtx_key key;
72919f7e2c2SDmitry Chagin 	int error;
73019f7e2c2SDmitry Chagin 
7310dc38e33SDmitry Chagin 	if (args->val3 == 0)
7320dc38e33SDmitry Chagin 		return (EINVAL);
7330dc38e33SDmitry Chagin 
734dad10770SDmitry Chagin 	error = futex_key_get(args->uaddr, TYPE_FUTEX, GET_SHARED(args), &key);
73519f7e2c2SDmitry Chagin 	if (error != 0)
73619f7e2c2SDmitry Chagin 		return (error);
7370dc38e33SDmitry Chagin 	umtxq_lock(&key);
7380dc38e33SDmitry Chagin 	td->td_retval[0] = umtxq_signal_mask(&key, args->val, args->val3);
7390dc38e33SDmitry Chagin 	umtxq_unlock(&key);
7400dc38e33SDmitry Chagin 	umtx_key_release(&key);
74119f7e2c2SDmitry Chagin 	return (0);
74219f7e2c2SDmitry Chagin }
74319f7e2c2SDmitry Chagin 
74419f7e2c2SDmitry Chagin static int
linux_futex_wait(struct thread * td,struct linux_futex_args * args)745f6b0d275SDmitry Chagin linux_futex_wait(struct thread *td, struct linux_futex_args *args)
746f6b0d275SDmitry Chagin {
7470dc38e33SDmitry Chagin 	struct umtx_abs_timeout timo;
7480dc38e33SDmitry Chagin 	struct umtx_q *uq;
7490dc38e33SDmitry Chagin 	uint32_t uval;
750f6b0d275SDmitry Chagin 	int error;
751f6b0d275SDmitry Chagin 
7520dc38e33SDmitry Chagin 	if (args->val3 == 0)
7530dc38e33SDmitry Chagin 		error = EINVAL;
754f6b0d275SDmitry Chagin 
7550dc38e33SDmitry Chagin 	uq = td->td_umtxq;
756dad10770SDmitry Chagin 	error = futex_key_get(args->uaddr, TYPE_FUTEX, GET_SHARED(args),
7570dc38e33SDmitry Chagin 	    &uq->uq_key);
758f6b0d275SDmitry Chagin 	if (error != 0)
759f6b0d275SDmitry Chagin 		return (error);
7600dc38e33SDmitry Chagin 	if (args->ts != NULL)
7610dc38e33SDmitry Chagin 		linux_umtx_abs_timeout_init(&timo, args);
7620dc38e33SDmitry Chagin 	umtxq_lock(&uq->uq_key);
7630dc38e33SDmitry Chagin 	umtxq_busy(&uq->uq_key);
7640dc38e33SDmitry Chagin 	uq->uq_bitset = args->val3;
7650dc38e33SDmitry Chagin 	umtxq_insert(uq);
7660dc38e33SDmitry Chagin 	umtxq_unlock(&uq->uq_key);
7670dc38e33SDmitry Chagin 	error = fueword32(args->uaddr, &uval);
76875cb2382SDmitry Chagin 	if (error != 0)
7690dc38e33SDmitry Chagin 		error = EFAULT;
7700dc38e33SDmitry Chagin 	else if (uval != args->val)
7710dc38e33SDmitry Chagin 		error = EWOULDBLOCK;
7720dc38e33SDmitry Chagin 	umtxq_lock(&uq->uq_key);
7730dc38e33SDmitry Chagin 	umtxq_unbusy(&uq->uq_key);
7740dc38e33SDmitry Chagin 	if (error == 0) {
7750dc38e33SDmitry Chagin 		error = umtxq_sleep(uq, "futex",
7760dc38e33SDmitry Chagin 		    args->ts == NULL ? NULL : &timo);
7770dc38e33SDmitry Chagin 		if ((uq->uq_flags & UQF_UMTXQ) == 0)
7780dc38e33SDmitry Chagin 			error = 0;
7790dc38e33SDmitry Chagin 		else
7800dc38e33SDmitry Chagin 			umtxq_remove(uq);
7810dc38e33SDmitry Chagin 	} else if ((uq->uq_flags & UQF_UMTXQ) != 0) {
7820dc38e33SDmitry Chagin 		umtxq_remove(uq);
7830dc38e33SDmitry Chagin 	}
7840dc38e33SDmitry Chagin 	umtxq_unlock(&uq->uq_key);
7850dc38e33SDmitry Chagin 	umtx_key_release(&uq->uq_key);
786*48645905SDmitry Chagin 	if (error == ERESTART)
787*48645905SDmitry Chagin 		error = EINTR;
78875cb2382SDmitry Chagin 	return (error);
789f6b0d275SDmitry Chagin }
790f6b0d275SDmitry Chagin 
7910dc38e33SDmitry Chagin static void
linux_umtx_abs_timeout_init(struct umtx_abs_timeout * timo,struct linux_futex_args * args)7920dc38e33SDmitry Chagin linux_umtx_abs_timeout_init(struct umtx_abs_timeout *timo,
7930dc38e33SDmitry Chagin     struct linux_futex_args *args)
7940dc38e33SDmitry Chagin {
7950dc38e33SDmitry Chagin 	int clockid, absolute;
7960dc38e33SDmitry Chagin 
7970dc38e33SDmitry Chagin 	/*
7980dc38e33SDmitry Chagin 	 * The FUTEX_CLOCK_REALTIME option bit can be employed only with the
799a9bb1b1cSDmitry Chagin 	 * FUTEX_WAIT_BITSET, FUTEX_WAIT_REQUEUE_PI, FUTEX_LOCK_PI2.
8000dc38e33SDmitry Chagin 	 * For FUTEX_WAIT, timeout is interpreted as a relative value, for other
8010dc38e33SDmitry Chagin 	 * futex operations timeout is interpreted as an absolute value.
8020dc38e33SDmitry Chagin 	 * If FUTEX_CLOCK_REALTIME option bit is set, the Linux kernel measures
8030dc38e33SDmitry Chagin 	 * the timeout against the CLOCK_REALTIME clock, otherwise the kernel
8040dc38e33SDmitry Chagin 	 * measures the timeout against the CLOCK_MONOTONIC clock.
8050dc38e33SDmitry Chagin 	 */
8060dc38e33SDmitry Chagin 	clockid = args->clockrt ? CLOCK_REALTIME : CLOCK_MONOTONIC;
8070dc38e33SDmitry Chagin 	absolute = args->op == LINUX_FUTEX_WAIT ? false : true;
8080dc38e33SDmitry Chagin 	umtx_abs_timeout_init(timo, clockid, absolute, args->ts);
8090dc38e33SDmitry Chagin }
8100dc38e33SDmitry Chagin 
81179262bf1SDmitry Chagin int
linux_sys_futex(struct thread * td,struct linux_sys_futex_args * args)8122e46d0c3SDmitry Chagin linux_sys_futex(struct thread *td, struct linux_sys_futex_args *args)
8132e46d0c3SDmitry Chagin {
8142e46d0c3SDmitry Chagin 	struct linux_futex_args fargs = {
8152e46d0c3SDmitry Chagin 		.uaddr = args->uaddr,
8162e46d0c3SDmitry Chagin 		.op = args->op,
8172e46d0c3SDmitry Chagin 		.val = args->val,
8182e46d0c3SDmitry Chagin 		.ts = NULL,
8192e46d0c3SDmitry Chagin 		.uaddr2 = args->uaddr2,
8202e46d0c3SDmitry Chagin 		.val3 = args->val3,
821cf8d74e3SDmitry Chagin 		.val3_compare = true,
8222e46d0c3SDmitry Chagin 	};
8232e46d0c3SDmitry Chagin 	int error;
8242e46d0c3SDmitry Chagin 
8252e46d0c3SDmitry Chagin 	switch (args->op & LINUX_FUTEX_CMD_MASK) {
8262e46d0c3SDmitry Chagin 	case LINUX_FUTEX_WAIT:
8272e46d0c3SDmitry Chagin 	case LINUX_FUTEX_WAIT_BITSET:
8287a718f29SDmitry Chagin 	case LINUX_FUTEX_LOCK_PI:
829a9bb1b1cSDmitry Chagin 	case LINUX_FUTEX_LOCK_PI2:
8302e46d0c3SDmitry Chagin 		if (args->timeout != NULL) {
831707e567aSDmitry Chagin 			error = linux_get_timespec(&fargs.kts, args->timeout);
8322e46d0c3SDmitry Chagin 			if (error != 0)
8332e46d0c3SDmitry Chagin 				return (error);
8342e46d0c3SDmitry Chagin 			fargs.ts = &fargs.kts;
8352e46d0c3SDmitry Chagin 		}
8362e46d0c3SDmitry Chagin 		break;
8372e46d0c3SDmitry Chagin 	default:
8382e46d0c3SDmitry Chagin 		fargs.ts = PTRIN(args->timeout);
8392e46d0c3SDmitry Chagin 	}
8402e46d0c3SDmitry Chagin 	return (linux_futex(td, &fargs));
8412e46d0c3SDmitry Chagin }
8422e46d0c3SDmitry Chagin 
8432e46d0c3SDmitry Chagin #if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32))
8442e46d0c3SDmitry Chagin int
linux_sys_futex_time64(struct thread * td,struct linux_sys_futex_time64_args * args)8452e46d0c3SDmitry Chagin linux_sys_futex_time64(struct thread *td,
8462e46d0c3SDmitry Chagin     struct linux_sys_futex_time64_args *args)
8472e46d0c3SDmitry Chagin {
8482e46d0c3SDmitry Chagin 	struct linux_futex_args fargs = {
8492e46d0c3SDmitry Chagin 		.uaddr = args->uaddr,
8502e46d0c3SDmitry Chagin 		.op = args->op,
8512e46d0c3SDmitry Chagin 		.val = args->val,
8522e46d0c3SDmitry Chagin 		.ts = NULL,
8532e46d0c3SDmitry Chagin 		.uaddr2 = args->uaddr2,
8542e46d0c3SDmitry Chagin 		.val3 = args->val3,
855b33e4690SDmitry Chagin 		.val3_compare = true,
8562e46d0c3SDmitry Chagin 	};
8572e46d0c3SDmitry Chagin 	int error;
8582e46d0c3SDmitry Chagin 
8592e46d0c3SDmitry Chagin 	switch (args->op & LINUX_FUTEX_CMD_MASK) {
8602e46d0c3SDmitry Chagin 	case LINUX_FUTEX_WAIT:
8612e46d0c3SDmitry Chagin 	case LINUX_FUTEX_WAIT_BITSET:
8627a718f29SDmitry Chagin 	case LINUX_FUTEX_LOCK_PI:
863a9bb1b1cSDmitry Chagin 	case LINUX_FUTEX_LOCK_PI2:
8642e46d0c3SDmitry Chagin 		if (args->timeout != NULL) {
865707e567aSDmitry Chagin 			error = linux_get_timespec64(&fargs.kts, args->timeout);
8662e46d0c3SDmitry Chagin 			if (error != 0)
8672e46d0c3SDmitry Chagin 				return (error);
8682e46d0c3SDmitry Chagin 			fargs.ts = &fargs.kts;
8692e46d0c3SDmitry Chagin 		}
8702e46d0c3SDmitry Chagin 		break;
8712e46d0c3SDmitry Chagin 	default:
8722e46d0c3SDmitry Chagin 		fargs.ts = PTRIN(args->timeout);
8732e46d0c3SDmitry Chagin 	}
8742e46d0c3SDmitry Chagin 	return (linux_futex(td, &fargs));
8752e46d0c3SDmitry Chagin }
8762e46d0c3SDmitry Chagin #endif
8772e46d0c3SDmitry Chagin 
8782e46d0c3SDmitry Chagin int
linux_set_robust_list(struct thread * td,struct linux_set_robust_list_args * args)8794732e446SRoman Divacky linux_set_robust_list(struct thread *td, struct linux_set_robust_list_args *args)
8804732e446SRoman Divacky {
8814732e446SRoman Divacky 	struct linux_emuldata *em;
8824732e446SRoman Divacky 
883edd44176SDmitry Chagin 	if (args->len != sizeof(struct linux_robust_list_head))
8844732e446SRoman Divacky 		return (EINVAL);
8854732e446SRoman Divacky 
88681338031SDmitry Chagin 	em = em_find(td);
8874732e446SRoman Divacky 	em->robust_futexes = args->head;
8884732e446SRoman Divacky 
8894732e446SRoman Divacky 	return (0);
8904732e446SRoman Divacky }
8914732e446SRoman Divacky 
8924732e446SRoman Divacky int
linux_get_robust_list(struct thread * td,struct linux_get_robust_list_args * args)8934732e446SRoman Divacky linux_get_robust_list(struct thread *td, struct linux_get_robust_list_args *args)
8944732e446SRoman Divacky {
8954732e446SRoman Divacky 	struct linux_emuldata *em;
8964732e446SRoman Divacky 	struct linux_robust_list_head *head;
89749a5c040SDmitry Chagin 	l_size_t len;
89881338031SDmitry Chagin 	struct thread *td2;
899f88d3c52SDmitry Chagin 	int error;
9004732e446SRoman Divacky 
9014732e446SRoman Divacky 	if (!args->pid) {
90281338031SDmitry Chagin 		em = em_find(td);
90381338031SDmitry Chagin 		KASSERT(em != NULL, ("get_robust_list: emuldata notfound.\n"));
9044732e446SRoman Divacky 		head = em->robust_futexes;
9054732e446SRoman Divacky 	} else {
906c8e9d2b7SDmitry Chagin 		td2 = linux_tdfind(td, args->pid, -1);
9075e8caee2SEdward Tomasz Napierala 		if (td2 == NULL)
9084732e446SRoman Divacky 			return (ESRCH);
9096437b8e7SDmitry Chagin 		if (SV_PROC_ABI(td2->td_proc) != SV_ABI_LINUX) {
9106437b8e7SDmitry Chagin 			PROC_UNLOCK(td2->td_proc);
9116e554971SDmitry Chagin 			return (EPERM);
9126437b8e7SDmitry Chagin 		}
9134732e446SRoman Divacky 
91481338031SDmitry Chagin 		em = em_find(td2);
91581338031SDmitry Chagin 		KASSERT(em != NULL, ("get_robust_list: emuldata notfound.\n"));
9164732e446SRoman Divacky 		/* XXX: ptrace? */
9174732e446SRoman Divacky 		if (priv_check(td, PRIV_CRED_SETUID) ||
9184732e446SRoman Divacky 		    priv_check(td, PRIV_CRED_SETEUID) ||
91981338031SDmitry Chagin 		    p_candebug(td, td2->td_proc)) {
92081338031SDmitry Chagin 			PROC_UNLOCK(td2->td_proc);
9214732e446SRoman Divacky 			return (EPERM);
9220046fd5dSDmitry Chagin 		}
9234732e446SRoman Divacky 		head = em->robust_futexes;
9244732e446SRoman Divacky 
92581338031SDmitry Chagin 		PROC_UNLOCK(td2->td_proc);
9264732e446SRoman Divacky 	}
9274732e446SRoman Divacky 
92849a5c040SDmitry Chagin 	len = sizeof(struct linux_robust_list_head);
9294732e446SRoman Divacky 	error = copyout(&len, args->len, sizeof(l_size_t));
930971b53faSDmitry Chagin 	if (error != 0)
9314732e446SRoman Divacky 		return (EFAULT);
9324732e446SRoman Divacky 
9339f7bf94eSAlex S 	return (copyout(&head, args->head, sizeof(l_uintptr_t)));
9344732e446SRoman Divacky }
9354732e446SRoman Divacky 
9364732e446SRoman Divacky static int
handle_futex_death(struct thread * td,struct linux_emuldata * em,uint32_t * uaddr,unsigned int pi,bool pending_op)9374f34dc64SDmitry Chagin handle_futex_death(struct thread *td, struct linux_emuldata *em, uint32_t *uaddr,
938b59cf25eSDmitry Chagin     unsigned int pi, bool pending_op)
9394732e446SRoman Divacky {
94079262bf1SDmitry Chagin 	uint32_t uval, nval, mval;
94179262bf1SDmitry Chagin 	int error;
9424732e446SRoman Divacky 
9434732e446SRoman Divacky retry:
944cb01cc4aSDmitry Chagin 	error = fueword32(uaddr, &uval);
945cb01cc4aSDmitry Chagin 	if (error != 0)
9464732e446SRoman Divacky 		return (EFAULT);
947b59cf25eSDmitry Chagin 
948b59cf25eSDmitry Chagin 	/*
949b59cf25eSDmitry Chagin 	 * Special case for regular (non PI) futexes. The unlock path in
950b59cf25eSDmitry Chagin 	 * user space has two race scenarios:
951b59cf25eSDmitry Chagin 	 *
952b59cf25eSDmitry Chagin 	 * 1. The unlock path releases the user space futex value and
953b59cf25eSDmitry Chagin 	 *    before it can execute the futex() syscall to wake up
954b59cf25eSDmitry Chagin 	 *    waiters it is killed.
955b59cf25eSDmitry Chagin 	 *
956b59cf25eSDmitry Chagin 	 * 2. A woken up waiter is killed before it can acquire the
957b59cf25eSDmitry Chagin 	 *    futex in user space.
958b59cf25eSDmitry Chagin 	 *
959b59cf25eSDmitry Chagin 	 * In both cases the TID validation below prevents a wakeup of
960b59cf25eSDmitry Chagin 	 * potential waiters which can cause these waiters to block
961b59cf25eSDmitry Chagin 	 * forever.
962b59cf25eSDmitry Chagin 	 *
963b59cf25eSDmitry Chagin 	 * In both cases it is safe to attempt waking up a potential
964b59cf25eSDmitry Chagin 	 * waiter without touching the user space futex value and trying
965b59cf25eSDmitry Chagin 	 * to set the OWNER_DIED bit.
966b59cf25eSDmitry Chagin 	 */
967b59cf25eSDmitry Chagin 	if (pending_op && !pi && !uval) {
968b59cf25eSDmitry Chagin 		(void)futex_wake(td, uaddr, 1, true);
969b59cf25eSDmitry Chagin 		return (0);
970b59cf25eSDmitry Chagin 	}
971b59cf25eSDmitry Chagin 
97281338031SDmitry Chagin 	if ((uval & FUTEX_TID_MASK) == em->em_tid) {
9734732e446SRoman Divacky 		mval = (uval & FUTEX_WAITERS) | FUTEX_OWNER_DIED;
9744f34dc64SDmitry Chagin 		error = casueword32(uaddr, uval, &nval, mval);
9754f34dc64SDmitry Chagin 		if (error == -1)
9764732e446SRoman Divacky 			return (EFAULT);
9774f34dc64SDmitry Chagin 		if (error == 1) {
9784f34dc64SDmitry Chagin 			error = thread_check_susp(td, false);
9794f34dc64SDmitry Chagin 			if (error != 0)
9804f34dc64SDmitry Chagin 				return (error);
9814732e446SRoman Divacky 			goto retry;
9824f34dc64SDmitry Chagin 		}
9834732e446SRoman Divacky 
9844732e446SRoman Divacky 		if (!pi && (uval & FUTEX_WAITERS)) {
9854f34dc64SDmitry Chagin 			error = futex_wake(td, uaddr, 1, true);
9860dc38e33SDmitry Chagin 			if (error != 0)
98779262bf1SDmitry Chagin 				return (error);
9887a718f29SDmitry Chagin 		} else if (pi && (uval & FUTEX_WAITERS)) {
9894f34dc64SDmitry Chagin 			error = futex_wake_pi(td, uaddr, true);
9907a718f29SDmitry Chagin 			if (error != 0)
9917a718f29SDmitry Chagin 				return (error);
9924732e446SRoman Divacky 		}
9934732e446SRoman Divacky 	}
9944732e446SRoman Divacky 
9954732e446SRoman Divacky 	return (0);
9964732e446SRoman Divacky }
9974732e446SRoman Divacky 
9984732e446SRoman Divacky static int
fetch_robust_entry(struct linux_robust_list ** entry,struct linux_robust_list ** head,unsigned int * pi)9994732e446SRoman Divacky fetch_robust_entry(struct linux_robust_list **entry,
10005072ad67SDmitry Chagin     struct linux_robust_list **head, unsigned int *pi)
10014732e446SRoman Divacky {
10024732e446SRoman Divacky 	l_ulong uentry;
100319e252baSAlexander Leidinger 	int error;
10044732e446SRoman Divacky 
1005bd25bf09SDmitry Chagin 	error = copyin((const void *)head, &uentry, sizeof(uentry));
1006971b53faSDmitry Chagin 	if (error != 0)
10074732e446SRoman Divacky 		return (EFAULT);
10084732e446SRoman Divacky 
10094732e446SRoman Divacky 	*entry = (void *)(uentry & ~1UL);
10104732e446SRoman Divacky 	*pi = uentry & 1;
10114732e446SRoman Divacky 
10124732e446SRoman Divacky 	return (0);
10134732e446SRoman Divacky }
10144732e446SRoman Divacky 
1015b59cf25eSDmitry Chagin #define	LINUX_HANDLE_DEATH_PENDING	true
1016b59cf25eSDmitry Chagin #define	LINUX_HANDLE_DEATH_LIST		false
1017b59cf25eSDmitry Chagin 
10184732e446SRoman Divacky /* This walks the list of robust futexes releasing them. */
10194732e446SRoman Divacky void
release_futexes(struct thread * td,struct linux_emuldata * em)102081338031SDmitry Chagin release_futexes(struct thread *td, struct linux_emuldata *em)
10214732e446SRoman Divacky {
10226b68e8afSDmitry Chagin 	struct linux_robust_list_head *head;
10234732e446SRoman Divacky 	struct linux_robust_list *entry, *next_entry, *pending;
10244732e446SRoman Divacky 	unsigned int limit = 2048, pi, next_pi, pip;
1025b59cf25eSDmitry Chagin 	uint32_t *uaddr;
102662162dfcSKonstantin Belousov 	l_long futex_offset;
1027971b53faSDmitry Chagin 	int error;
102819e252baSAlexander Leidinger 
10294732e446SRoman Divacky 	head = em->robust_futexes;
10305e8caee2SEdward Tomasz Napierala 	if (head == NULL)
10314732e446SRoman Divacky 		return;
10324732e446SRoman Divacky 
10335e8caee2SEdward Tomasz Napierala 	if (fetch_robust_entry(&entry, PTRIN(&head->list.next), &pi))
10344732e446SRoman Divacky 		return;
10354732e446SRoman Divacky 
103619e252baSAlexander Leidinger 	error = copyin(&head->futex_offset, &futex_offset,
103719e252baSAlexander Leidinger 	    sizeof(futex_offset));
1038971b53faSDmitry Chagin 	if (error != 0)
10394732e446SRoman Divacky 		return;
10404732e446SRoman Divacky 
10415e8caee2SEdward Tomasz Napierala 	if (fetch_robust_entry(&pending, PTRIN(&head->pending_list), &pip))
10424732e446SRoman Divacky 		return;
10434732e446SRoman Divacky 
10444732e446SRoman Divacky 	while (entry != &head->list) {
1045971b53faSDmitry Chagin 		error = fetch_robust_entry(&next_entry, PTRIN(&entry->next),
1046971b53faSDmitry Chagin 		    &next_pi);
10474732e446SRoman Divacky 
1048b59cf25eSDmitry Chagin 		/*
1049b59cf25eSDmitry Chagin 		 * A pending lock might already be on the list, so
1050b59cf25eSDmitry Chagin 		 * don't process it twice.
1051b59cf25eSDmitry Chagin 		 */
1052b59cf25eSDmitry Chagin 		if (entry != pending) {
1053b59cf25eSDmitry Chagin 			uaddr = (uint32_t *)((caddr_t)entry + futex_offset);
1054b59cf25eSDmitry Chagin 			if (handle_futex_death(td, em, uaddr, pi,
1055b59cf25eSDmitry Chagin 			    LINUX_HANDLE_DEATH_LIST))
10564732e446SRoman Divacky 				return;
105719e252baSAlexander Leidinger 		}
1058971b53faSDmitry Chagin 		if (error != 0)
10594732e446SRoman Divacky 			return;
10604732e446SRoman Divacky 
10614732e446SRoman Divacky 		entry = next_entry;
10624732e446SRoman Divacky 		pi = next_pi;
10634732e446SRoman Divacky 
10644732e446SRoman Divacky 		if (!--limit)
10654732e446SRoman Divacky 			break;
10664732e446SRoman Divacky 
10674732e446SRoman Divacky 		sched_relinquish(curthread);
10684732e446SRoman Divacky 	}
10694732e446SRoman Divacky 
1070b59cf25eSDmitry Chagin 	if (pending) {
1071b59cf25eSDmitry Chagin 		uaddr = (uint32_t *)((caddr_t)pending + futex_offset);
1072b59cf25eSDmitry Chagin 		(void)handle_futex_death(td, em, uaddr, pip,
1073b59cf25eSDmitry Chagin 		    LINUX_HANDLE_DEATH_PENDING);
1074b59cf25eSDmitry Chagin 	}
10754732e446SRoman Divacky }
1076