kern_umtx.c revision 138225
1/*
2 * Copyright (c) 2002, Jeffrey Roberson <jeff@freebsd.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice unmodified, this list of conditions, and the following
10 *    disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: head/sys/kern/kern_umtx.c 138225 2004-11-30 12:18:53Z davidxu $");
29
30#include <sys/param.h>
31#include <sys/kernel.h>
32#include <sys/limits.h>
33#include <sys/lock.h>
34#include <sys/malloc.h>
35#include <sys/mutex.h>
36#include <sys/proc.h>
37#include <sys/sysent.h>
38#include <sys/systm.h>
39#include <sys/sysproto.h>
40#include <sys/thr.h>
41#include <sys/umtx.h>
42
43struct umtx_q {
44	LIST_ENTRY(umtx_q)	uq_next;	/* Linked list for the hash. */
45	TAILQ_HEAD(, thread)	uq_tdq;		/* List of threads blocked here. */
46	struct umtx		*uq_umtx;	/* Pointer key component. */
47	pid_t			uq_pid;		/* Pid key component. */
48	int			uq_count;	/* How many threads blocked. */
49};
50
51LIST_HEAD(umtx_head, umtx_q);
52struct umtxq_chain {
53	struct mtx		uc_lock;	/* lock for this chain. */
54	struct umtx_head	uc_queues;	/* List of sleep queues. */
55};
56
57#define	GOLDEN_RATIO_PRIME	2654404609U
58#define	UMTX_CHAINS		128
59#define	UMTX_SHIFTS		(__WORD_BIT - 7)
60
61static struct umtxq_chain umtxq_chains[UMTX_CHAINS];
62static MALLOC_DEFINE(M_UMTX, "umtx", "UMTX queue memory");
63
64#define	UMTX_CONTESTED	LONG_MIN
65
66static void umtx_init_chains(void *);
67static int umtxq_hash(struct thread *, struct umtx *);
68static void umtxq_lock(struct thread *td, struct umtx *key);
69static void umtxq_unlock(struct thread *td, struct umtx *key);
70static struct umtx_q *umtxq_lookup(struct thread *, struct umtx *);
71static struct umtx_q *umtxq_insert(struct thread *, struct umtx *);
72static int umtxq_count(struct thread *td, struct umtx *umtx);
73static int umtx_sleep(struct thread *td, struct umtx *umtx, int priority,
74	   const char *wmesg, int timo);
75static void umtx_signal(struct thread *td, struct umtx *umtx);
76
77SYSINIT(umtx, SI_SUB_LOCK, SI_ORDER_MIDDLE, umtx_init_chains, NULL);
78
79static void
80umtx_init_chains(void *arg __unused)
81{
82	int i;
83
84	for (i = 0; i < UMTX_CHAINS; ++i) {
85		mtx_init(&umtxq_chains[i].uc_lock, "umtxq_lock", NULL,
86			 MTX_DEF | MTX_DUPOK);
87		LIST_INIT(&umtxq_chains[i].uc_queues);
88	}
89}
90
91static inline int
92umtxq_hash(struct thread *td, struct umtx *umtx)
93{
94	unsigned n = (uintptr_t)umtx + td->td_proc->p_pid;
95	return (((n * GOLDEN_RATIO_PRIME) >> UMTX_SHIFTS) % UMTX_CHAINS);
96}
97
98static inline void
99umtxq_lock(struct thread *td, struct umtx *key)
100{
101	int chain = umtxq_hash(td, key);
102	mtx_lock(&umtxq_chains[chain].uc_lock);
103}
104
105static inline void
106umtxq_unlock(struct thread *td, struct umtx *key)
107{
108	int chain = umtxq_hash(td, key);
109	mtx_unlock(&umtxq_chains[chain].uc_lock);
110}
111
112static struct umtx_q *
113umtxq_lookup(struct thread *td, struct umtx *umtx)
114{
115	struct umtx_head *head;
116	struct umtx_q *uq;
117	pid_t pid;
118	int chain;
119
120	chain = umtxq_hash(td, umtx);
121	mtx_assert(&umtxq_chains[chain].uc_lock, MA_OWNED);
122	pid = td->td_proc->p_pid;
123	head = &umtxq_chains[chain].uc_queues;
124	LIST_FOREACH(uq, head, uq_next) {
125		if (uq->uq_pid == pid && uq->uq_umtx == umtx)
126			return (uq);
127	}
128	return (NULL);
129}
130
131/*
132 * Insert a thread onto the umtx queue.
133 */
134static struct umtx_q *
135umtxq_insert(struct thread *td, struct umtx *umtx)
136{
137	struct umtx_head *head;
138	struct umtx_q *uq, *ins = NULL;
139	pid_t pid;
140	int chain;
141
142	chain = umtxq_hash(td, umtx);
143	pid = td->td_proc->p_pid;
144	if ((uq = umtxq_lookup(td, umtx)) == NULL) {
145		umtxq_unlock(td, umtx);
146		ins = malloc(sizeof(*uq), M_UMTX, M_ZERO | M_WAITOK);
147		umtxq_lock(td, umtx);
148
149		/*
150		 * Some one else could have succeeded while we were blocked
151		 * waiting on memory.
152		 */
153		if ((uq = umtxq_lookup(td, umtx)) == NULL) {
154			head = &umtxq_chains[chain].uc_queues;
155			uq = ins;
156			uq->uq_pid = pid;
157			uq->uq_umtx = umtx;
158			uq->uq_count = 0;
159			LIST_INSERT_HEAD(head, uq, uq_next);
160			TAILQ_INIT(&uq->uq_tdq);
161			ins = NULL;
162		}
163	}
164	TAILQ_INSERT_TAIL(&uq->uq_tdq, td, td_umtx);
165	uq->uq_count++;
166	if (ins) {
167		umtxq_unlock(td, umtx);
168		free(ins, M_UMTX);
169		umtxq_lock(td, umtx);
170	}
171	return (uq);
172}
173
174/*
175 * Remove thread from umtx queue, umtx chain lock is also
176 * released.
177 */
178static void
179umtx_remove(struct umtx_q *uq, struct thread *td, struct umtx *umtx)
180{
181	int chain;
182
183	chain = umtxq_hash(td, umtx);
184	mtx_assert(&umtxq_chains[chain].uc_lock, MA_OWNED);
185	TAILQ_REMOVE(&uq->uq_tdq, td, td_umtx);
186	uq->uq_count--;
187	if (TAILQ_EMPTY(&uq->uq_tdq)) {
188		LIST_REMOVE(uq, uq_next);
189		umtxq_unlock(td, umtx);
190		free(uq, M_UMTX);
191	} else
192		umtxq_unlock(td, umtx);
193}
194
195static inline int
196umtxq_count(struct thread *td, struct umtx *umtx)
197{
198	struct umtx_q *uq;
199	int count = 0;
200
201	umtxq_lock(td, umtx);
202	if ((uq = umtxq_lookup(td, umtx)) != NULL)
203		count = uq->uq_count;
204	umtxq_unlock(td, umtx);
205	return (count);
206}
207
208static inline int
209umtx_sleep(struct thread *td, struct umtx *umtx, int priority,
210	   const char *wmesg, int timo)
211{
212	int chain;
213
214	chain = umtxq_hash(td, umtx);
215	mtx_assert(&umtxq_chains[chain].uc_lock, MA_OWNED);
216	return (msleep(td, &umtxq_chains[chain].uc_lock, priority,
217		       wmesg, timo));
218}
219
220static void
221umtx_signal(struct thread *td, struct umtx *umtx)
222{
223	struct umtx_q *uq;
224	struct thread *blocked = NULL;
225
226	umtxq_lock(td, umtx);
227	if ((uq = umtxq_lookup(td, umtx)) != NULL) {
228		if ((blocked = TAILQ_FIRST(&uq->uq_tdq)) != NULL) {
229			mtx_lock_spin(&sched_lock);
230			blocked->td_flags |= TDF_UMTXWAKEUP;
231			mtx_unlock_spin(&sched_lock);
232		}
233	}
234	umtxq_unlock(td, umtx);
235	if (blocked != NULL)
236		wakeup(blocked);
237}
238
239int
240_umtx_lock(struct thread *td, struct _umtx_lock_args *uap)
241    /* struct umtx *umtx */
242{
243	struct umtx_q *uq;
244	struct umtx *umtx;
245	intptr_t owner;
246	intptr_t old;
247	int error = 0;
248
249	uq = NULL;
250
251	/*
252	 * Care must be exercised when dealing with this structure.  It
253	 * can fault on any access.
254	 */
255	umtx = uap->umtx;
256
257	for (;;) {
258		/*
259		 * Try the uncontested case.  This should be done in userland.
260		 */
261		owner = casuptr((intptr_t *)&umtx->u_owner,
262		    UMTX_UNOWNED, td->td_tid);
263
264		/* The acquire succeeded. */
265		if (owner == UMTX_UNOWNED)
266			return (0);
267
268		/* The address was invalid. */
269		if (owner == -1)
270			return (EFAULT);
271
272		/* If no one owns it but it is contested try to acquire it. */
273		if (owner == UMTX_CONTESTED) {
274			owner = casuptr((intptr_t *)&umtx->u_owner,
275			    UMTX_CONTESTED, td->td_tid | UMTX_CONTESTED);
276
277			if (owner == UMTX_CONTESTED)
278				return (0);
279
280			/* The address was invalid. */
281			if (owner == -1)
282				return (EFAULT);
283
284			/* If this failed the lock has changed, restart. */
285			continue;
286		}
287
288		/*
289		 * If we caught a signal, we have retried and now
290		 * exit immediately.
291		 */
292		if (error)
293			return (error);
294
295		umtxq_lock(td, umtx);
296		uq = umtxq_insert(td, umtx);
297		umtxq_unlock(td, umtx);
298
299		/*
300		 * Set the contested bit so that a release in user space
301		 * knows to use the system call for unlock.  If this fails
302		 * either some one else has acquired the lock or it has been
303		 * released.
304		 */
305		old = casuptr((intptr_t *)&umtx->u_owner, owner,
306		    owner | UMTX_CONTESTED);
307
308		/* The address was invalid. */
309		if (old == -1) {
310			umtxq_lock(td, umtx);
311			umtx_remove(uq, td, umtx);
312			/* unlocked by umtx_remove */
313			return (EFAULT);
314		}
315
316		/*
317		 * We set the contested bit, sleep. Otherwise the lock changed
318		 * and we need to retry or we lost a race to the thread
319		 * unlocking the umtx.
320		 */
321		umtxq_lock(td, umtx);
322		if (old == owner && (td->td_flags & TDF_UMTXWAKEUP) == 0)
323			error = umtx_sleep(td, umtx, td->td_priority | PCATCH,
324				    "umtx", 0);
325		else
326			error = 0;
327		umtx_remove(uq, td, umtx);
328		/* unlocked by umtx_remove */
329
330		if (td->td_flags & TDF_UMTXWAKEUP) {
331			/*
332			 * If we were resumed by umtxq_unlock, we should retry
333			 * to avoid a race.
334			 */
335			mtx_lock_spin(&sched_lock);
336			td->td_flags &= ~TDF_UMTXWAKEUP;
337			mtx_unlock_spin(&sched_lock);
338			continue;
339		}
340
341		/*
342		 * If we caught a signal, exit immediately.
343		 */
344		if (error)
345			return (error);
346	}
347
348	return (0);
349}
350
351int
352_umtx_unlock(struct thread *td, struct _umtx_unlock_args *uap)
353    /* struct umtx *umtx */
354{
355	struct umtx *umtx;
356	intptr_t owner;
357	intptr_t old;
358	int count;
359
360	umtx = uap->umtx;
361
362	/*
363	 * Make sure we own this mtx.
364	 *
365	 * XXX Need a {fu,su}ptr this is not correct on arch where
366	 * sizeof(intptr_t) != sizeof(long).
367	 */
368	if ((owner = fuword(&umtx->u_owner)) == -1)
369		return (EFAULT);
370
371	if ((owner & ~UMTX_CONTESTED) != td->td_tid)
372		return (EPERM);
373
374	/* We should only ever be in here for contested locks */
375	if ((owner & UMTX_CONTESTED) == 0)
376		return (EINVAL);
377
378	/*
379	 * When unlocking the umtx, it must be marked as unowned if
380	 * there is zero or one thread only waiting for it.
381	 * Otherwise, it must be marked as contested.
382	 */
383	old = casuptr((intptr_t *)&umtx->u_owner, owner, UMTX_UNOWNED);
384	if (old == -1)
385		return (EFAULT);
386	if (old != owner)
387		return (EINVAL);
388
389	/*
390	 * At the point, a new thread can lock the umtx before we
391	 * reach here, so contested bit will not be set, if there
392	 * are two or more threads on wait queue, we should set
393	 * contensted bit for them.
394	 */
395	count = umtxq_count(td, umtx);
396	if (count <= 0)
397		return (0);
398
399	/*
400	 * If there is second thread waiting on umtx, set contested bit,
401	 * if they are resumed before we reach here, it is harmless,
402	 * just a bit unefficient.
403	 */
404	if (count > 1) {
405		owner = UMTX_UNOWNED;
406		for (;;) {
407			old = casuptr((intptr_t *)&umtx->u_owner, owner,
408				    owner | UMTX_CONTESTED);
409			if (old == owner)
410				break;
411			if (old == -1)
412				return (EFAULT);
413			owner = old;
414		}
415		/*
416		 * Another thread locked the umtx before us, so don't bother
417		 * to wake more threads, that thread will do it when it unlocks
418		 * the umtx.
419		 */
420		if ((owner & ~UMTX_CONTESTED) != 0)
421			return (0);
422	}
423
424	/* Wake blocked thread. */
425	umtx_signal(td, umtx);
426
427	return (0);
428}
429