kern_umtx.c revision 139804
1139804Simp/*-
2139013Sdavidxu * Copyright (c) 2004, David Xu <davidxu@freebsd.org>
3112904Sjeff * Copyright (c) 2002, Jeffrey Roberson <jeff@freebsd.org>
4112904Sjeff * All rights reserved.
5112904Sjeff *
6112904Sjeff * Redistribution and use in source and binary forms, with or without
7112904Sjeff * modification, are permitted provided that the following conditions
8112904Sjeff * are met:
9112904Sjeff * 1. Redistributions of source code must retain the above copyright
10112904Sjeff *    notice unmodified, this list of conditions, and the following
11112904Sjeff *    disclaimer.
12112904Sjeff * 2. Redistributions in binary form must reproduce the above copyright
13112904Sjeff *    notice, this list of conditions and the following disclaimer in the
14112904Sjeff *    documentation and/or other materials provided with the distribution.
15112904Sjeff *
16112904Sjeff * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17112904Sjeff * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18112904Sjeff * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19112904Sjeff * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20112904Sjeff * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21112904Sjeff * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22112904Sjeff * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23112904Sjeff * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24112904Sjeff * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25112904Sjeff * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26112904Sjeff */
27112904Sjeff
28116182Sobrien#include <sys/cdefs.h>
29116182Sobrien__FBSDID("$FreeBSD: head/sys/kern/kern_umtx.c 139804 2005-01-06 23:35:40Z imp $");
30116182Sobrien
31112904Sjeff#include <sys/param.h>
32112904Sjeff#include <sys/kernel.h>
33131431Smarcel#include <sys/limits.h>
34112904Sjeff#include <sys/lock.h>
35115765Sjeff#include <sys/malloc.h>
36112904Sjeff#include <sys/mutex.h>
37112904Sjeff#include <sys/proc.h>
38112904Sjeff#include <sys/sysent.h>
39112904Sjeff#include <sys/systm.h>
40112904Sjeff#include <sys/sysproto.h>
41139013Sdavidxu#include <sys/eventhandler.h>
42112904Sjeff#include <sys/thr.h>
43112904Sjeff#include <sys/umtx.h>
44112904Sjeff
45139013Sdavidxu#include <vm/vm.h>
46139013Sdavidxu#include <vm/vm_param.h>
47139013Sdavidxu#include <vm/pmap.h>
48139013Sdavidxu#include <vm/vm_map.h>
49139013Sdavidxu#include <vm/vm_object.h>
50139013Sdavidxu
51139013Sdavidxu#define UMTX_PRIVATE	0
52139013Sdavidxu#define UMTX_SHARED	1
53139013Sdavidxu
54139013Sdavidxu#define UMTX_STATIC_SHARED
55139013Sdavidxu
56139013Sdavidxustruct umtx_key {
57139013Sdavidxu	int	type;
58139013Sdavidxu	union {
59139013Sdavidxu		struct {
60139013Sdavidxu			vm_object_t	object;
61139013Sdavidxu			long		offset;
62139013Sdavidxu		} shared;
63139013Sdavidxu		struct {
64139013Sdavidxu			struct umtx	*umtx;
65139013Sdavidxu			long		pid;
66139013Sdavidxu		} private;
67139013Sdavidxu		struct {
68139013Sdavidxu			void		*ptr;
69139013Sdavidxu			long		word;
70139013Sdavidxu		} both;
71139013Sdavidxu	} info;
72139013Sdavidxu};
73139013Sdavidxu
74115765Sjeffstruct umtx_q {
75115765Sjeff	LIST_ENTRY(umtx_q)	uq_next;	/* Linked list for the hash. */
76139013Sdavidxu	struct umtx_key		uq_key;		/* Umtx key. */
77139257Sdavidxu	struct thread		*uq_thread;	/* The thread waits on. */
78139013Sdavidxu	LIST_ENTRY(umtx_q)	uq_rqnext;	/* Linked list for requeuing. */
79139013Sdavidxu	vm_offset_t		uq_addr;	/* Umtx's virtual address. */
80115765Sjeff};
81115765Sjeff
82115765SjeffLIST_HEAD(umtx_head, umtx_q);
83138224Sdavidxustruct umtxq_chain {
84139013Sdavidxu	struct mtx		uc_lock;	/* Lock for this chain. */
85139013Sdavidxu	struct umtx_head	uc_queue;	/* List of sleep queues. */
86139257Sdavidxu#define	UCF_BUSY		0x01
87139257Sdavidxu#define	UCF_WANT		0x02
88139257Sdavidxu	int			uc_flags;
89138224Sdavidxu};
90115765Sjeff
91138224Sdavidxu#define	GOLDEN_RATIO_PRIME	2654404609U
92138224Sdavidxu#define	UMTX_CHAINS		128
93138224Sdavidxu#define	UMTX_SHIFTS		(__WORD_BIT - 7)
94115765Sjeff
95138224Sdavidxustatic struct umtxq_chain umtxq_chains[UMTX_CHAINS];
96138224Sdavidxustatic MALLOC_DEFINE(M_UMTX, "umtx", "UMTX queue memory");
97115310Sjeff
98139013Sdavidxustatic void umtxq_init_chains(void *);
99139013Sdavidxustatic int umtxq_hash(struct umtx_key *key);
100139013Sdavidxustatic struct mtx *umtxq_mtx(int chain);
101139013Sdavidxustatic void umtxq_lock(struct umtx_key *key);
102139013Sdavidxustatic void umtxq_unlock(struct umtx_key *key);
103139257Sdavidxustatic void umtxq_busy(struct umtx_key *key);
104139257Sdavidxustatic void umtxq_unbusy(struct umtx_key *key);
105139013Sdavidxustatic void umtxq_insert(struct umtx_q *uq);
106139013Sdavidxustatic void umtxq_remove(struct umtx_q *uq);
107139013Sdavidxustatic int umtxq_sleep(struct thread *td, struct umtx_key *key,
108139013Sdavidxu	int prio, const char *wmesg, int timo);
109139257Sdavidxustatic int umtxq_count(struct umtx_key *key);
110139257Sdavidxustatic int umtxq_signal(struct umtx_key *key, int nr_wakeup);
111139013Sdavidxu#ifdef UMTX_DYNAMIC_SHARED
112139013Sdavidxustatic void fork_handler(void *arg, struct proc *p1, struct proc *p2,
113139013Sdavidxu	int flags);
114139013Sdavidxu#endif
115139013Sdavidxustatic int umtx_key_match(const struct umtx_key *k1, const struct umtx_key *k2);
116139013Sdavidxustatic int umtx_key_get(struct thread *td, struct umtx *umtx,
117139013Sdavidxu	struct umtx_key *key);
118139013Sdavidxustatic void umtx_key_release(struct umtx_key *key);
119115310Sjeff
120139013SdavidxuSYSINIT(umtx, SI_SUB_EVENTHANDLER+1, SI_ORDER_MIDDLE, umtxq_init_chains, NULL);
121138224Sdavidxu
122138224Sdavidxustatic void
123139013Sdavidxuumtxq_init_chains(void *arg __unused)
124138224Sdavidxu{
125138224Sdavidxu	int i;
126138224Sdavidxu
127138224Sdavidxu	for (i = 0; i < UMTX_CHAINS; ++i) {
128138224Sdavidxu		mtx_init(&umtxq_chains[i].uc_lock, "umtxq_lock", NULL,
129138224Sdavidxu			 MTX_DEF | MTX_DUPOK);
130139013Sdavidxu		LIST_INIT(&umtxq_chains[i].uc_queue);
131139257Sdavidxu		umtxq_chains[i].uc_flags = 0;
132138224Sdavidxu	}
133139013Sdavidxu#ifdef UMTX_DYNAMIC_SHARED
134139013Sdavidxu	EVENTHANDLER_REGISTER(process_fork, fork_handler, 0, 10000);
135139013Sdavidxu#endif
136138224Sdavidxu}
137138224Sdavidxu
138138224Sdavidxustatic inline int
139139013Sdavidxuumtxq_hash(struct umtx_key *key)
140138224Sdavidxu{
141139013Sdavidxu	unsigned n = (uintptr_t)key->info.both.ptr + key->info.both.word;
142138224Sdavidxu	return (((n * GOLDEN_RATIO_PRIME) >> UMTX_SHIFTS) % UMTX_CHAINS);
143138224Sdavidxu}
144138224Sdavidxu
145139013Sdavidxustatic inline int
146139013Sdavidxuumtx_key_match(const struct umtx_key *k1, const struct umtx_key *k2)
147139013Sdavidxu{
148139013Sdavidxu	return (k1->type == k2->type &&
149139013Sdavidxu		k1->info.both.ptr == k2->info.both.ptr &&
150139013Sdavidxu	        k1->info.both.word == k2->info.both.word);
151139013Sdavidxu}
152139013Sdavidxu
153139013Sdavidxustatic inline struct mtx *
154139013Sdavidxuumtxq_mtx(int chain)
155139013Sdavidxu{
156139013Sdavidxu	return (&umtxq_chains[chain].uc_lock);
157139013Sdavidxu}
158139013Sdavidxu
159138224Sdavidxustatic inline void
160139257Sdavidxuumtxq_busy(struct umtx_key *key)
161139257Sdavidxu{
162139257Sdavidxu	int chain = umtxq_hash(key);
163139257Sdavidxu
164139257Sdavidxu	mtx_assert(umtxq_mtx(chain), MA_OWNED);
165139257Sdavidxu	while (umtxq_chains[chain].uc_flags & UCF_BUSY) {
166139257Sdavidxu		umtxq_chains[chain].uc_flags |= UCF_WANT;
167139257Sdavidxu		msleep(&umtxq_chains[chain], umtxq_mtx(chain),
168139257Sdavidxu		       curthread->td_priority, "umtxq_busy", 0);
169139257Sdavidxu	}
170139257Sdavidxu	umtxq_chains[chain].uc_flags |= UCF_BUSY;
171139257Sdavidxu}
172139257Sdavidxu
173139257Sdavidxustatic inline void
174139257Sdavidxuumtxq_unbusy(struct umtx_key *key)
175139257Sdavidxu{
176139257Sdavidxu	int chain = umtxq_hash(key);
177139257Sdavidxu
178139257Sdavidxu	mtx_assert(umtxq_mtx(chain), MA_OWNED);
179139258Sdavidxu	KASSERT(umtxq_chains[chain].uc_flags & UCF_BUSY, ("not busy"));
180139257Sdavidxu	umtxq_chains[chain].uc_flags &= ~UCF_BUSY;
181139257Sdavidxu	if (umtxq_chains[chain].uc_flags & UCF_WANT) {
182139257Sdavidxu		umtxq_chains[chain].uc_flags &= ~UCF_WANT;
183139257Sdavidxu		wakeup(&umtxq_chains[chain]);
184139257Sdavidxu	}
185139257Sdavidxu}
186139257Sdavidxu
187139257Sdavidxustatic inline void
188139013Sdavidxuumtxq_lock(struct umtx_key *key)
189138224Sdavidxu{
190139013Sdavidxu	int chain = umtxq_hash(key);
191139013Sdavidxu	mtx_lock(umtxq_mtx(chain));
192138224Sdavidxu}
193138224Sdavidxu
194138225Sdavidxustatic inline void
195139013Sdavidxuumtxq_unlock(struct umtx_key *key)
196138224Sdavidxu{
197139013Sdavidxu	int chain = umtxq_hash(key);
198139013Sdavidxu	mtx_unlock(umtxq_mtx(chain));
199138224Sdavidxu}
200138224Sdavidxu
201139013Sdavidxu/*
202139013Sdavidxu * Insert a thread onto the umtx queue.
203139013Sdavidxu */
204139013Sdavidxustatic inline void
205139013Sdavidxuumtxq_insert(struct umtx_q *uq)
206115765Sjeff{
207115765Sjeff	struct umtx_head *head;
208139013Sdavidxu	int chain = umtxq_hash(&uq->uq_key);
209139013Sdavidxu
210139257Sdavidxu	mtx_assert(umtxq_mtx(chain), MA_OWNED);
211139013Sdavidxu	head = &umtxq_chains[chain].uc_queue;
212139013Sdavidxu	LIST_INSERT_HEAD(head, uq, uq_next);
213139013Sdavidxu	uq->uq_thread->td_umtxq = uq;
214139013Sdavidxu	mtx_lock_spin(&sched_lock);
215139013Sdavidxu	uq->uq_thread->td_flags |= TDF_UMTXQ;
216139013Sdavidxu	mtx_unlock_spin(&sched_lock);
217139013Sdavidxu}
218139013Sdavidxu
219139013Sdavidxu/*
220139013Sdavidxu * Remove thread from the umtx queue.
221139013Sdavidxu */
222139013Sdavidxustatic inline void
223139013Sdavidxuumtxq_remove(struct umtx_q *uq)
224139013Sdavidxu{
225139257Sdavidxu	mtx_assert(umtxq_mtx(umtxq_hash(&uq->uq_key)), MA_OWNED);
226139013Sdavidxu	if (uq->uq_thread->td_flags & TDF_UMTXQ) {
227139013Sdavidxu		LIST_REMOVE(uq, uq_next);
228139013Sdavidxu		uq->uq_thread->td_umtxq = NULL;
229139013Sdavidxu		/* turning off TDF_UMTXQ should be the last thing. */
230139013Sdavidxu		mtx_lock_spin(&sched_lock);
231139013Sdavidxu		uq->uq_thread->td_flags &= ~TDF_UMTXQ;
232139013Sdavidxu		mtx_unlock_spin(&sched_lock);
233139013Sdavidxu	}
234139013Sdavidxu}
235139013Sdavidxu
236139013Sdavidxustatic int
237139013Sdavidxuumtxq_count(struct umtx_key *key)
238139013Sdavidxu{
239115765Sjeff	struct umtx_q *uq;
240139013Sdavidxu	struct umtx_head *head;
241139013Sdavidxu	int chain, count = 0;
242115765Sjeff
243139013Sdavidxu	chain = umtxq_hash(key);
244139257Sdavidxu	mtx_assert(umtxq_mtx(chain), MA_OWNED);
245139013Sdavidxu	head = &umtxq_chains[chain].uc_queue;
246115765Sjeff	LIST_FOREACH(uq, head, uq_next) {
247139013Sdavidxu		if (umtx_key_match(&uq->uq_key, key)) {
248139013Sdavidxu			if (++count > 1)
249139013Sdavidxu				break;
250139013Sdavidxu		}
251115765Sjeff	}
252139013Sdavidxu	return (count);
253115765Sjeff}
254115765Sjeff
255139257Sdavidxustatic int
256139257Sdavidxuumtxq_signal(struct umtx_key *key, int n_wake)
257115765Sjeff{
258139257Sdavidxu	struct umtx_q *uq, *next;
259115765Sjeff	struct umtx_head *head;
260139013Sdavidxu	struct thread *blocked = NULL;
261139257Sdavidxu	int chain, ret;
262115765Sjeff
263139257Sdavidxu	ret = 0;
264139013Sdavidxu	chain = umtxq_hash(key);
265139257Sdavidxu	mtx_assert(umtxq_mtx(chain), MA_OWNED);
266139013Sdavidxu	head = &umtxq_chains[chain].uc_queue;
267139257Sdavidxu	for (uq = LIST_FIRST(head); uq; uq = next) {
268139013Sdavidxu		next = LIST_NEXT(uq, uq_next);
269139013Sdavidxu		if (umtx_key_match(&uq->uq_key, key)) {
270139013Sdavidxu			blocked = uq->uq_thread;
271139013Sdavidxu			umtxq_remove(uq);
272139013Sdavidxu			wakeup(blocked);
273139257Sdavidxu			if (++ret >= n_wake)
274139257Sdavidxu				break;
275139013Sdavidxu		}
276139013Sdavidxu	}
277139257Sdavidxu	return (ret);
278138224Sdavidxu}
279138224Sdavidxu
280138224Sdavidxustatic inline int
281139013Sdavidxuumtxq_sleep(struct thread *td, struct umtx_key *key, int priority,
282139013Sdavidxu	    const char *wmesg, int timo)
283138224Sdavidxu{
284139013Sdavidxu	int chain = umtxq_hash(key);
285139751Sdavidxu	int error = msleep(td, umtxq_mtx(chain), priority, wmesg, timo);
286139751Sdavidxu	if (error == EWOULDBLOCK)
287139751Sdavidxu		error = ETIMEDOUT;
288139751Sdavidxu	return (error);
289138224Sdavidxu}
290138224Sdavidxu
291139013Sdavidxustatic int
292139013Sdavidxuumtx_key_get(struct thread *td, struct umtx *umtx, struct umtx_key *key)
293139013Sdavidxu{
294139013Sdavidxu#if defined(UMTX_DYNAMIC_SHARED) || defined(UMTX_STATIC_SHARED)
295139013Sdavidxu	vm_map_t map;
296139013Sdavidxu	vm_map_entry_t entry;
297139013Sdavidxu	vm_pindex_t pindex;
298139013Sdavidxu	vm_prot_t prot;
299139013Sdavidxu	boolean_t wired;
300139013Sdavidxu
301139013Sdavidxu	map = &td->td_proc->p_vmspace->vm_map;
302139013Sdavidxu	if (vm_map_lookup(&map, (vm_offset_t)umtx, VM_PROT_WRITE,
303139013Sdavidxu	    &entry, &key->info.shared.object, &pindex, &prot,
304139013Sdavidxu	    &wired) != KERN_SUCCESS) {
305139013Sdavidxu		return EFAULT;
306139013Sdavidxu	}
307139013Sdavidxu#endif
308139013Sdavidxu
309139013Sdavidxu#if defined(UMTX_DYNAMIC_SHARED)
310139013Sdavidxu	key->type = UMTX_SHARED;
311139013Sdavidxu	key->info.shared.offset = entry->offset + entry->start -
312139013Sdavidxu		(vm_offset_t)umtx;
313139013Sdavidxu	/*
314139013Sdavidxu	 * Add object reference, if we don't do this, a buggy application
315139013Sdavidxu	 * deallocates the object, the object will be reused by other
316139013Sdavidxu	 * applications, then unlock will wake wrong thread.
317139013Sdavidxu	 */
318139013Sdavidxu	vm_object_reference(key->info.shared.object);
319139013Sdavidxu	vm_map_lookup_done(map, entry);
320139013Sdavidxu#elif defined(UMTX_STATIC_SHARED)
321139013Sdavidxu	if (VM_INHERIT_SHARE == entry->inheritance) {
322139013Sdavidxu		key->type = UMTX_SHARED;
323139013Sdavidxu		key->info.shared.offset = entry->offset + entry->start -
324139013Sdavidxu			(vm_offset_t)umtx;
325139013Sdavidxu		vm_object_reference(key->info.shared.object);
326139013Sdavidxu	} else {
327139013Sdavidxu		key->type = UMTX_PRIVATE;
328139013Sdavidxu		key->info.private.umtx = umtx;
329139013Sdavidxu		key->info.private.pid  = td->td_proc->p_pid;
330139013Sdavidxu	}
331139013Sdavidxu	vm_map_lookup_done(map, entry);
332139013Sdavidxu#else
333139013Sdavidxu	key->type = UMTX_PRIVATE;
334139013Sdavidxu	key->info.private.umtx = umtx;
335139013Sdavidxu	key->info.private.pid  = td->td_proc->p_pid;
336139013Sdavidxu#endif
337139013Sdavidxu	return (0);
338139013Sdavidxu}
339139013Sdavidxu
340139013Sdavidxustatic inline void
341139013Sdavidxuumtx_key_release(struct umtx_key *key)
342139013Sdavidxu{
343139013Sdavidxu	if (key->type == UMTX_SHARED)
344139013Sdavidxu		vm_object_deallocate(key->info.shared.object);
345139013Sdavidxu}
346139013Sdavidxu
347138224Sdavidxustatic inline int
348139013Sdavidxuumtxq_queue_me(struct thread *td, struct umtx *umtx, struct umtx_q *uq)
349138224Sdavidxu{
350139013Sdavidxu	int error;
351138224Sdavidxu
352139013Sdavidxu	if ((error = umtx_key_get(td, umtx, &uq->uq_key)) != 0)
353139013Sdavidxu		return (error);
354139013Sdavidxu
355139013Sdavidxu	uq->uq_addr = (vm_offset_t)umtx;
356139013Sdavidxu	uq->uq_thread = td;
357139013Sdavidxu	umtxq_lock(&uq->uq_key);
358139257Sdavidxu	/* hmm, for condition variable, we don't need busy flag. */
359139257Sdavidxu	umtxq_busy(&uq->uq_key);
360139013Sdavidxu	umtxq_insert(uq);
361139257Sdavidxu	umtxq_unbusy(&uq->uq_key);
362139013Sdavidxu	umtxq_unlock(&uq->uq_key);
363139013Sdavidxu	return (0);
364138224Sdavidxu}
365138224Sdavidxu
366139013Sdavidxu#if defined(UMTX_DYNAMIC_SHARED)
367138224Sdavidxustatic void
368139013Sdavidxufork_handler(void *arg, struct proc *p1, struct proc *p2, int flags)
369138224Sdavidxu{
370139013Sdavidxu	vm_map_t map;
371139013Sdavidxu	vm_map_entry_t entry;
372139013Sdavidxu	vm_object_t object;
373139013Sdavidxu	vm_pindex_t pindex;
374139013Sdavidxu	vm_prot_t prot;
375139013Sdavidxu	boolean_t wired;
376139013Sdavidxu	struct umtx_key key;
377139013Sdavidxu	LIST_HEAD(, umtx_q) workq;
378138224Sdavidxu	struct umtx_q *uq;
379139013Sdavidxu	struct thread *td;
380139013Sdavidxu	int onq;
381138224Sdavidxu
382139013Sdavidxu	LIST_INIT(&workq);
383139013Sdavidxu
384139013Sdavidxu	/* Collect threads waiting on umtxq */
385139013Sdavidxu	PROC_LOCK(p1);
386139013Sdavidxu	FOREACH_THREAD_IN_PROC(p1, td) {
387139013Sdavidxu		if (td->td_flags & TDF_UMTXQ) {
388139013Sdavidxu			uq = td->td_umtxq;
389139013Sdavidxu			if (uq)
390139013Sdavidxu				LIST_INSERT_HEAD(&workq, uq, uq_rqnext);
391138224Sdavidxu		}
392115765Sjeff	}
393139013Sdavidxu	PROC_UNLOCK(p1);
394139013Sdavidxu
395139013Sdavidxu	LIST_FOREACH(uq, &workq, uq_rqnext) {
396139013Sdavidxu		map = &p1->p_vmspace->vm_map;
397139013Sdavidxu		if (vm_map_lookup(&map, uq->uq_addr, VM_PROT_WRITE,
398139013Sdavidxu		    &entry, &object, &pindex, &prot, &wired) != KERN_SUCCESS) {
399139013Sdavidxu			continue;
400139013Sdavidxu		}
401139013Sdavidxu		key.type = UMTX_SHARED;
402139013Sdavidxu		key.info.shared.object = object;
403139013Sdavidxu		key.info.shared.offset = entry->offset + entry->start -
404139013Sdavidxu			uq->uq_addr;
405139013Sdavidxu		if (umtx_key_match(&key, &uq->uq_key)) {
406139013Sdavidxu			vm_map_lookup_done(map, entry);
407139013Sdavidxu			continue;
408139013Sdavidxu		}
409139013Sdavidxu
410139013Sdavidxu		umtxq_lock(&uq->uq_key);
411139257Sdavidxu		umtxq_busy(&uq->uq_key);
412139013Sdavidxu		if (uq->uq_thread->td_flags & TDF_UMTXQ) {
413139013Sdavidxu			umtxq_remove(uq);
414139013Sdavidxu			onq = 1;
415139013Sdavidxu		} else
416139013Sdavidxu			onq = 0;
417139257Sdavidxu		umtxq_unbusy(&uq->uq_key);
418139013Sdavidxu		umtxq_unlock(&uq->uq_key);
419139013Sdavidxu		if (onq) {
420139013Sdavidxu			vm_object_deallocate(uq->uq_key.info.shared.object);
421139013Sdavidxu			uq->uq_key = key;
422139013Sdavidxu			umtxq_lock(&uq->uq_key);
423139257Sdavidxu			umtxq_busy(&uq->uq_key);
424139013Sdavidxu			umtxq_insert(uq);
425139257Sdavidxu			umtxq_unbusy(&uq->uq_key);
426139013Sdavidxu			umtxq_unlock(&uq->uq_key);
427139013Sdavidxu			vm_object_reference(uq->uq_key.info.shared.object);
428139013Sdavidxu		}
429139013Sdavidxu		vm_map_lookup_done(map, entry);
430139013Sdavidxu	}
431115765Sjeff}
432139013Sdavidxu#endif
433115765Sjeff
434139013Sdavidxustatic int
435139013Sdavidxu_do_lock(struct thread *td, struct umtx *umtx, long id, int timo)
436112904Sjeff{
437139013Sdavidxu	struct umtx_q uq;
438112904Sjeff	intptr_t owner;
439112967Sjake	intptr_t old;
440138224Sdavidxu	int error = 0;
441112904Sjeff
442112904Sjeff	/*
443139013Sdavidxu	 * Care must be exercised when dealing with umtx structure.  It
444112904Sjeff	 * can fault on any access.
445112904Sjeff	 */
446112904Sjeff
447112904Sjeff	for (;;) {
448112904Sjeff		/*
449112904Sjeff		 * Try the uncontested case.  This should be done in userland.
450112904Sjeff		 */
451112904Sjeff		owner = casuptr((intptr_t *)&umtx->u_owner,
452139013Sdavidxu		    UMTX_UNOWNED, id);
453112904Sjeff
454138224Sdavidxu		/* The acquire succeeded. */
455138224Sdavidxu		if (owner == UMTX_UNOWNED)
456138224Sdavidxu			return (0);
457138224Sdavidxu
458115765Sjeff		/* The address was invalid. */
459115765Sjeff		if (owner == -1)
460115765Sjeff			return (EFAULT);
461115765Sjeff
462115765Sjeff		/* If no one owns it but it is contested try to acquire it. */
463115765Sjeff		if (owner == UMTX_CONTESTED) {
464115765Sjeff			owner = casuptr((intptr_t *)&umtx->u_owner,
465139013Sdavidxu			    UMTX_CONTESTED, id | UMTX_CONTESTED);
466115765Sjeff
467138224Sdavidxu			if (owner == UMTX_CONTESTED)
468138224Sdavidxu				return (0);
469138224Sdavidxu
470115765Sjeff			/* The address was invalid. */
471115765Sjeff			if (owner == -1)
472115765Sjeff				return (EFAULT);
473115765Sjeff
474115765Sjeff			/* If this failed the lock has changed, restart. */
475115765Sjeff			continue;
476112904Sjeff		}
477112904Sjeff
478138224Sdavidxu		/*
479138224Sdavidxu		 * If we caught a signal, we have retried and now
480138224Sdavidxu		 * exit immediately.
481138224Sdavidxu		 */
482139013Sdavidxu		if (error || (error = umtxq_queue_me(td, umtx, &uq)) != 0)
483138224Sdavidxu			return (error);
484112904Sjeff
485112904Sjeff		/*
486112904Sjeff		 * Set the contested bit so that a release in user space
487112904Sjeff		 * knows to use the system call for unlock.  If this fails
488112904Sjeff		 * either some one else has acquired the lock or it has been
489112904Sjeff		 * released.
490112904Sjeff		 */
491112967Sjake		old = casuptr((intptr_t *)&umtx->u_owner, owner,
492112967Sjake		    owner | UMTX_CONTESTED);
493112904Sjeff
494112904Sjeff		/* The address was invalid. */
495112967Sjake		if (old == -1) {
496139013Sdavidxu			umtxq_lock(&uq.uq_key);
497139257Sdavidxu			umtxq_busy(&uq.uq_key);
498139013Sdavidxu			umtxq_remove(&uq);
499139257Sdavidxu			umtxq_unbusy(&uq.uq_key);
500139013Sdavidxu			umtxq_unlock(&uq.uq_key);
501139013Sdavidxu			umtx_key_release(&uq.uq_key);
502115765Sjeff			return (EFAULT);
503112904Sjeff		}
504112904Sjeff
505112904Sjeff		/*
506115765Sjeff		 * We set the contested bit, sleep. Otherwise the lock changed
507117685Smtm		 * and we need to retry or we lost a race to the thread
508117685Smtm		 * unlocking the umtx.
509112904Sjeff		 */
510139013Sdavidxu		umtxq_lock(&uq.uq_key);
511139013Sdavidxu		if (old == owner && (td->td_flags & TDF_UMTXQ)) {
512139013Sdavidxu			error = umtxq_sleep(td, &uq.uq_key,
513139257Sdavidxu				       td->td_priority | PCATCH,
514139013Sdavidxu				       "umtx", timo);
515138224Sdavidxu		}
516139257Sdavidxu		umtxq_busy(&uq.uq_key);
517139257Sdavidxu		umtxq_remove(&uq);
518139257Sdavidxu		umtxq_unbusy(&uq.uq_key);
519139257Sdavidxu		umtxq_unlock(&uq.uq_key);
520139013Sdavidxu		umtx_key_release(&uq.uq_key);
521112904Sjeff	}
522117743Smtm
523117743Smtm	return (0);
524112904Sjeff}
525112904Sjeff
526139013Sdavidxustatic int
527139013Sdavidxudo_lock(struct thread *td, struct umtx *umtx, long id,
528139013Sdavidxu	struct timespec *abstime)
529112904Sjeff{
530139013Sdavidxu	struct timespec ts1, ts2;
531139013Sdavidxu	struct timeval tv;
532139013Sdavidxu	int timo, error;
533139013Sdavidxu
534139013Sdavidxu	if (abstime == NULL) {
535139013Sdavidxu		error = _do_lock(td, umtx, id, 0);
536139013Sdavidxu	} else {
537139013Sdavidxu		for (;;) {
538139013Sdavidxu			ts1 = *abstime;
539139013Sdavidxu			getnanotime(&ts2);
540139013Sdavidxu			timespecsub(&ts1, &ts2);
541139013Sdavidxu			TIMESPEC_TO_TIMEVAL(&tv, &ts1);
542139013Sdavidxu			if (tv.tv_sec < 0) {
543139751Sdavidxu				error = ETIMEDOUT;
544139013Sdavidxu				break;
545139013Sdavidxu			}
546139013Sdavidxu			timo = tvtohz(&tv);
547139013Sdavidxu			error = _do_lock(td, umtx, id, timo);
548139751Sdavidxu			if (error != ETIMEDOUT)
549139013Sdavidxu				break;
550139013Sdavidxu		}
551139013Sdavidxu	}
552139258Sdavidxu	/*
553139258Sdavidxu	 * This lets userland back off critical region if needed.
554139258Sdavidxu	 */
555139258Sdavidxu	if (error == ERESTART)
556139258Sdavidxu		error = EINTR;
557139013Sdavidxu	return (error);
558139013Sdavidxu}
559139013Sdavidxu
560139013Sdavidxustatic int
561139013Sdavidxudo_unlock(struct thread *td, struct umtx *umtx, long id)
562139013Sdavidxu{
563139013Sdavidxu	struct umtx_key key;
564112904Sjeff	intptr_t owner;
565112967Sjake	intptr_t old;
566139257Sdavidxu	int error;
567139257Sdavidxu	int count;
568112904Sjeff
569112904Sjeff	/*
570112904Sjeff	 * Make sure we own this mtx.
571112904Sjeff	 *
572112904Sjeff	 * XXX Need a {fu,su}ptr this is not correct on arch where
573112904Sjeff	 * sizeof(intptr_t) != sizeof(long).
574112904Sjeff	 */
575115765Sjeff	if ((owner = fuword(&umtx->u_owner)) == -1)
576115765Sjeff		return (EFAULT);
577115765Sjeff
578139013Sdavidxu	if ((owner & ~UMTX_CONTESTED) != id)
579115765Sjeff		return (EPERM);
580112904Sjeff
581117685Smtm	/* We should only ever be in here for contested locks */
582119836Stjr	if ((owner & UMTX_CONTESTED) == 0)
583119836Stjr		return (EINVAL);
584112904Sjeff
585139257Sdavidxu	if ((error = umtx_key_get(td, umtx, &key)) != 0)
586139257Sdavidxu		return (error);
587139257Sdavidxu
588139257Sdavidxu	umtxq_lock(&key);
589139257Sdavidxu	umtxq_busy(&key);
590139257Sdavidxu	count = umtxq_count(&key);
591139257Sdavidxu	umtxq_unlock(&key);
592139257Sdavidxu
593117743Smtm	/*
594117743Smtm	 * When unlocking the umtx, it must be marked as unowned if
595117743Smtm	 * there is zero or one thread only waiting for it.
596117743Smtm	 * Otherwise, it must be marked as contested.
597117743Smtm	 */
598139257Sdavidxu	old = casuptr((intptr_t *)&umtx->u_owner, owner,
599139257Sdavidxu			count <= 1 ? UMTX_UNOWNED : UMTX_CONTESTED);
600139257Sdavidxu	umtxq_lock(&key);
601139257Sdavidxu	umtxq_signal(&key, 0);
602139257Sdavidxu	umtxq_unbusy(&key);
603139257Sdavidxu	umtxq_unlock(&key);
604139257Sdavidxu	umtx_key_release(&key);
605115765Sjeff	if (old == -1)
606115765Sjeff		return (EFAULT);
607138224Sdavidxu	if (old != owner)
608138224Sdavidxu		return (EINVAL);
609115765Sjeff	return (0);
610112904Sjeff}
611139013Sdavidxu
612139013Sdavidxustatic int
613139427Sdavidxudo_wait(struct thread *td, struct umtx *umtx, long id, struct timespec *abstime)
614139013Sdavidxu{
615139013Sdavidxu	struct umtx_q uq;
616139013Sdavidxu	struct timespec ts1, ts2;
617139013Sdavidxu	struct timeval tv;
618139427Sdavidxu	long tmp;
619139013Sdavidxu	int timo, error = 0;
620139013Sdavidxu
621139427Sdavidxu	if ((error = umtxq_queue_me(td, umtx, &uq)) != 0)
622139013Sdavidxu		return (error);
623139427Sdavidxu	tmp = fuword(&umtx->u_owner);
624139427Sdavidxu	if (tmp != id) {
625139013Sdavidxu		umtxq_lock(&uq.uq_key);
626139013Sdavidxu		umtxq_remove(&uq);
627139013Sdavidxu		umtxq_unlock(&uq.uq_key);
628139427Sdavidxu	} else if (abstime == NULL) {
629139013Sdavidxu		umtxq_lock(&uq.uq_key);
630139013Sdavidxu		if (td->td_flags & TDF_UMTXQ)
631139013Sdavidxu			error = umtxq_sleep(td, &uq.uq_key,
632139013Sdavidxu			       td->td_priority | PCATCH, "ucond", 0);
633139257Sdavidxu		if (!(td->td_flags & TDF_UMTXQ))
634139257Sdavidxu			error = 0;
635139257Sdavidxu		else
636139257Sdavidxu			umtxq_remove(&uq);
637139013Sdavidxu		umtxq_unlock(&uq.uq_key);
638139013Sdavidxu	} else {
639139013Sdavidxu		for (;;) {
640139013Sdavidxu			ts1 = *abstime;
641139013Sdavidxu			getnanotime(&ts2);
642139013Sdavidxu			timespecsub(&ts1, &ts2);
643139013Sdavidxu			TIMESPEC_TO_TIMEVAL(&tv, &ts1);
644139257Sdavidxu			umtxq_lock(&uq.uq_key);
645139013Sdavidxu			if (tv.tv_sec < 0) {
646139751Sdavidxu				error = ETIMEDOUT;
647139013Sdavidxu				break;
648139013Sdavidxu			}
649139013Sdavidxu			timo = tvtohz(&tv);
650139257Sdavidxu			if (td->td_flags & TDF_UMTXQ)
651139013Sdavidxu				error = umtxq_sleep(td, &uq.uq_key,
652139257Sdavidxu					    td->td_priority | PCATCH,
653139014Sdavidxu					    "ucond", timo);
654139291Sdavidxu			if (!(td->td_flags & TDF_UMTXQ))
655139013Sdavidxu				break;
656139257Sdavidxu			umtxq_unlock(&uq.uq_key);
657139013Sdavidxu		}
658139257Sdavidxu		if (!(td->td_flags & TDF_UMTXQ))
659139257Sdavidxu			error = 0;
660139257Sdavidxu		else
661139013Sdavidxu			umtxq_remove(&uq);
662139257Sdavidxu		umtxq_unlock(&uq.uq_key);
663139013Sdavidxu	}
664139013Sdavidxu	umtx_key_release(&uq.uq_key);
665139257Sdavidxu	if (error == ERESTART)
666139257Sdavidxu		error = EINTR;
667139013Sdavidxu	return (error);
668139013Sdavidxu}
669139013Sdavidxu
670139013Sdavidxustatic int
671139257Sdavidxudo_wake(struct thread *td, void *uaddr, int n_wake)
672139013Sdavidxu{
673139013Sdavidxu	struct umtx_key key;
674139257Sdavidxu	int ret;
675139013Sdavidxu
676139257Sdavidxu	if ((ret = umtx_key_get(td, uaddr, &key)) != 0)
677139257Sdavidxu		return (ret);
678139258Sdavidxu	umtxq_lock(&key);
679139257Sdavidxu	ret = umtxq_signal(&key, n_wake);
680139258Sdavidxu	umtxq_unlock(&key);
681139257Sdavidxu	umtx_key_release(&key);
682139257Sdavidxu	td->td_retval[0] = ret;
683139013Sdavidxu	return (0);
684139013Sdavidxu}
685139013Sdavidxu
686139013Sdavidxuint
687139013Sdavidxu_umtx_lock(struct thread *td, struct _umtx_lock_args *uap)
688139013Sdavidxu    /* struct umtx *umtx */
689139013Sdavidxu{
690139013Sdavidxu	return _do_lock(td, uap->umtx, td->td_tid, 0);
691139013Sdavidxu}
692139013Sdavidxu
693139013Sdavidxuint
694139013Sdavidxu_umtx_unlock(struct thread *td, struct _umtx_unlock_args *uap)
695139013Sdavidxu    /* struct umtx *umtx */
696139013Sdavidxu{
697139013Sdavidxu	return do_unlock(td, uap->umtx, td->td_tid);
698139013Sdavidxu}
699139013Sdavidxu
700139013Sdavidxuint
701139013Sdavidxu_umtx_op(struct thread *td, struct _umtx_op_args *uap)
702139013Sdavidxu{
703139013Sdavidxu	struct timespec abstime;
704139013Sdavidxu	struct timespec *ts;
705139013Sdavidxu	int error;
706139013Sdavidxu
707139013Sdavidxu	switch(uap->op) {
708139013Sdavidxu	case UMTX_OP_LOCK:
709139013Sdavidxu		/* Allow a null timespec (wait forever). */
710139292Sdavidxu		if (uap->uaddr2 == NULL)
711139013Sdavidxu			ts = NULL;
712139013Sdavidxu		else {
713139292Sdavidxu			error = copyin(uap->uaddr2, &abstime, sizeof(abstime));
714139013Sdavidxu			if (error != 0)
715139013Sdavidxu				return (error);
716139013Sdavidxu			if (abstime.tv_nsec >= 1000000000 ||
717139013Sdavidxu			    abstime.tv_nsec < 0)
718139013Sdavidxu				return (EINVAL);
719139013Sdavidxu			ts = &abstime;
720139013Sdavidxu		}
721139013Sdavidxu		return do_lock(td, uap->umtx, uap->id, ts);
722139013Sdavidxu	case UMTX_OP_UNLOCK:
723139013Sdavidxu		return do_unlock(td, uap->umtx, uap->id);
724139427Sdavidxu	case UMTX_OP_WAIT:
725139013Sdavidxu		/* Allow a null timespec (wait forever). */
726139292Sdavidxu		if (uap->uaddr2 == NULL)
727139013Sdavidxu			ts = NULL;
728139013Sdavidxu		else {
729139292Sdavidxu			error = copyin(uap->uaddr2, &abstime, sizeof(abstime));
730139013Sdavidxu			if (error != 0)
731139013Sdavidxu				return (error);
732139013Sdavidxu			if (abstime.tv_nsec >= 1000000000 ||
733139013Sdavidxu			    abstime.tv_nsec < 0)
734139013Sdavidxu				return (EINVAL);
735139013Sdavidxu			ts = &abstime;
736139013Sdavidxu		}
737139427Sdavidxu		return do_wait(td, uap->umtx, uap->id, ts);
738139013Sdavidxu	case UMTX_OP_WAKE:
739139427Sdavidxu		return do_wake(td, uap->umtx, uap->id);
740139013Sdavidxu	default:
741139013Sdavidxu		return (EINVAL);
742139013Sdavidxu	}
743139013Sdavidxu}
744