FreeBSD/Linux Kernel Cross Reference
sys/kern/kern_umtx.c
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: releng/5.2/sys/kern/kern_umtx.c 119836 2003-09-07 11:14:52Z tjr $");
29
30 #include <sys/param.h>
31 #include <sys/kernel.h>
32 #include <sys/lock.h>
33 #include <sys/malloc.h>
34 #include <sys/mutex.h>
35 #include <sys/proc.h>
36 #include <sys/signalvar.h>
37 #include <sys/sysent.h>
38 #include <sys/systm.h>
39 #include <sys/sysproto.h>
40 #include <sys/sx.h>
41 #include <sys/thr.h>
42 #include <sys/umtx.h>
43
44 struct umtx_q {
45 LIST_ENTRY(umtx_q) uq_next; /* Linked list for the hash. */
46 TAILQ_HEAD(, thread) uq_tdq; /* List of threads blocked here. */
47 struct umtx *uq_umtx; /* Pointer key component. */
48 pid_t uq_pid; /* Pid key component. */
49 };
50
51 #define UMTX_QUEUES 128
52 #define UMTX_HASH(pid, umtx) \
53 (((uintptr_t)pid + ((uintptr_t)umtx & ~65535)) % UMTX_QUEUES)
54
55 LIST_HEAD(umtx_head, umtx_q);
56 static struct umtx_head queues[UMTX_QUEUES];
57 static MALLOC_DEFINE(M_UMTX, "umtx", "UMTX queue memory");
58
59 static struct mtx umtx_lock;
60 MTX_SYSINIT(umtx, &umtx_lock, "umtx", MTX_DEF);
61
62 #define UMTX_LOCK() mtx_lock(&umtx_lock);
63 #define UMTX_UNLOCK() mtx_unlock(&umtx_lock);
64
65
66 static struct umtx_q *umtx_lookup(struct thread *, struct umtx *umtx);
67 static struct umtx_q *umtx_insert(struct thread *, struct umtx *umtx);
68
69 static struct umtx_q *
70 umtx_lookup(struct thread *td, struct umtx *umtx)
71 {
72 struct umtx_head *head;
73 struct umtx_q *uq;
74 pid_t pid;
75
76 pid = td->td_proc->p_pid;
77
78 head = &queues[UMTX_HASH(td->td_proc->p_pid, umtx)];
79
80 LIST_FOREACH(uq, head, uq_next) {
81 if (uq->uq_pid == pid && uq->uq_umtx == umtx)
82 return (uq);
83 }
84
85 return (NULL);
86 }
87
88 /*
89 * Insert a thread onto the umtx queue.
90 */
91 static struct umtx_q *
92 umtx_insert(struct thread *td, struct umtx *umtx)
93 {
94 struct umtx_head *head;
95 struct umtx_q *uq;
96 pid_t pid;
97
98 pid = td->td_proc->p_pid;
99
100 if ((uq = umtx_lookup(td, umtx)) == NULL) {
101 struct umtx_q *ins;
102
103 UMTX_UNLOCK();
104 ins = malloc(sizeof(*uq), M_UMTX, M_ZERO | M_WAITOK);
105 UMTX_LOCK();
106
107 /*
108 * Some one else could have succeeded while we were blocked
109 * waiting on memory.
110 */
111 if ((uq = umtx_lookup(td, umtx)) == NULL) {
112 head = &queues[UMTX_HASH(pid, umtx)];
113 uq = ins;
114 uq->uq_pid = pid;
115 uq->uq_umtx = umtx;
116 LIST_INSERT_HEAD(head, uq, uq_next);
117 TAILQ_INIT(&uq->uq_tdq);
118 } else
119 free(ins, M_UMTX);
120 }
121
122 /*
123 * Insert us onto the end of the TAILQ.
124 */
125 TAILQ_INSERT_TAIL(&uq->uq_tdq, td, td_umtx);
126
127 return (uq);
128 }
129
130 static void
131 umtx_remove(struct umtx_q *uq, struct thread *td)
132 {
133 TAILQ_REMOVE(&uq->uq_tdq, td, td_umtx);
134
135 if (TAILQ_EMPTY(&uq->uq_tdq)) {
136 LIST_REMOVE(uq, uq_next);
137 free(uq, M_UMTX);
138 }
139 }
140
141 int
142 _umtx_lock(struct thread *td, struct _umtx_lock_args *uap)
143 /* struct umtx *umtx */
144 {
145 struct umtx_q *uq;
146 struct umtx *umtx;
147 intptr_t owner;
148 intptr_t old;
149 int error;
150
151 uq = NULL;
152
153 /*
154 * Care must be exercised when dealing with this structure. It
155 * can fault on any access.
156 */
157 umtx = uap->umtx;
158
159 for (;;) {
160 /*
161 * Try the uncontested case. This should be done in userland.
162 */
163 owner = casuptr((intptr_t *)&umtx->u_owner,
164 UMTX_UNOWNED, (intptr_t)td);
165
166 /* The address was invalid. */
167 if (owner == -1)
168 return (EFAULT);
169
170 /* The acquire succeeded. */
171 if (owner == UMTX_UNOWNED)
172 return (0);
173
174 /* If no one owns it but it is contested try to acquire it. */
175 if (owner == UMTX_CONTESTED) {
176 owner = casuptr((intptr_t *)&umtx->u_owner,
177 UMTX_CONTESTED, ((intptr_t)td | UMTX_CONTESTED));
178
179 /* The address was invalid. */
180 if (owner == -1)
181 return (EFAULT);
182
183 if (owner == UMTX_CONTESTED)
184 return (0);
185
186 /* If this failed the lock has changed, restart. */
187 continue;
188 }
189
190
191 UMTX_LOCK();
192 uq = umtx_insert(td, umtx);
193 UMTX_UNLOCK();
194
195 /*
196 * Set the contested bit so that a release in user space
197 * knows to use the system call for unlock. If this fails
198 * either some one else has acquired the lock or it has been
199 * released.
200 */
201 old = casuptr((intptr_t *)&umtx->u_owner, owner,
202 owner | UMTX_CONTESTED);
203
204 /* The address was invalid. */
205 if (old == -1) {
206 UMTX_LOCK();
207 umtx_remove(uq, td);
208 UMTX_UNLOCK();
209 return (EFAULT);
210 }
211
212 /*
213 * We set the contested bit, sleep. Otherwise the lock changed
214 * and we need to retry or we lost a race to the thread
215 * unlocking the umtx.
216 */
217 UMTX_LOCK();
218 mtx_lock_spin(&sched_lock);
219 if (old == owner && (td->td_flags & TDF_UMTXWAKEUP) == 0) {
220 mtx_unlock_spin(&sched_lock);
221 error = msleep(td, &umtx_lock,
222 td->td_priority | PCATCH, "umtx", 0);
223 mtx_lock_spin(&sched_lock);
224 } else
225 error = 0;
226 td->td_flags &= ~TDF_UMTXWAKEUP;
227 mtx_unlock_spin(&sched_lock);
228
229 umtx_remove(uq, td);
230 UMTX_UNLOCK();
231
232 /*
233 * If we caught a signal we might have to retry or exit
234 * immediately.
235 */
236 if (error)
237 return (error);
238 }
239
240 return (0);
241 }
242
243 int
244 _umtx_unlock(struct thread *td, struct _umtx_unlock_args *uap)
245 /* struct umtx *umtx */
246 {
247 struct thread *blocked;
248 struct umtx *umtx;
249 struct umtx_q *uq;
250 intptr_t owner;
251 intptr_t old;
252
253 umtx = uap->umtx;
254
255 /*
256 * Make sure we own this mtx.
257 *
258 * XXX Need a {fu,su}ptr this is not correct on arch where
259 * sizeof(intptr_t) != sizeof(long).
260 */
261 if ((owner = fuword(&umtx->u_owner)) == -1)
262 return (EFAULT);
263
264 if ((struct thread *)(owner & ~UMTX_CONTESTED) != td)
265 return (EPERM);
266
267 /* We should only ever be in here for contested locks */
268 if ((owner & UMTX_CONTESTED) == 0)
269 return (EINVAL);
270 blocked = NULL;
271
272 /*
273 * When unlocking the umtx, it must be marked as unowned if
274 * there is zero or one thread only waiting for it.
275 * Otherwise, it must be marked as contested.
276 */
277 UMTX_LOCK();
278 uq = umtx_lookup(td, umtx);
279 if (uq == NULL ||
280 (uq != NULL && (blocked = TAILQ_FIRST(&uq->uq_tdq)) != NULL &&
281 TAILQ_NEXT(blocked, td_umtx) == NULL)) {
282 UMTX_UNLOCK();
283 old = casuptr((intptr_t *)&umtx->u_owner, owner,
284 UMTX_UNOWNED);
285 if (old == -1)
286 return (EFAULT);
287 if (old != owner)
288 return (EINVAL);
289
290 /*
291 * Recheck the umtx queue to make sure another thread
292 * didn't put itself on it after it was unlocked.
293 */
294 UMTX_LOCK();
295 uq = umtx_lookup(td, umtx);
296 if (uq != NULL &&
297 ((blocked = TAILQ_FIRST(&uq->uq_tdq)) != NULL &&
298 TAILQ_NEXT(blocked, td_umtx) != NULL)) {
299 UMTX_UNLOCK();
300 old = casuptr((intptr_t *)&umtx->u_owner,
301 UMTX_UNOWNED, UMTX_CONTESTED);
302 } else {
303 UMTX_UNLOCK();
304 }
305 } else {
306 UMTX_UNLOCK();
307 old = casuptr((intptr_t *)&umtx->u_owner,
308 owner, UMTX_CONTESTED);
309 if (old != -1 && old != owner)
310 return (EINVAL);
311 }
312
313 if (old == -1)
314 return (EFAULT);
315
316 /*
317 * If there is a thread waiting on the umtx, wake it up.
318 */
319 if (blocked != NULL) {
320 mtx_lock_spin(&sched_lock);
321 blocked->td_flags |= TDF_UMTXWAKEUP;
322 mtx_unlock_spin(&sched_lock);
323 wakeup(blocked);
324 }
325
326 return (0);
327 }
Cache object: 03ab5356df1fee834d1d1284c046bb74
|