| @@ -1,14 +1,14 @@ | | | @@ -1,14 +1,14 @@ |
1 | /* $NetBSD: kern_turnstile.c,v 1.31 2011/12/02 12:31:53 yamt Exp $ */ | | 1 | /* $NetBSD: kern_turnstile.c,v 1.32 2012/06/15 13:51:40 yamt Exp $ */ |
2 | | | 2 | |
3 | /*- | | 3 | /*- |
4 | * Copyright (c) 2002, 2006, 2007, 2009 The NetBSD Foundation, Inc. | | 4 | * Copyright (c) 2002, 2006, 2007, 2009 The NetBSD Foundation, Inc. |
5 | * All rights reserved. | | 5 | * All rights reserved. |
6 | * | | 6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation | | 7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Jason R. Thorpe and Andrew Doran. | | 8 | * by Jason R. Thorpe and Andrew Doran. |
9 | * | | 9 | * |
10 | * Redistribution and use in source and binary forms, with or without | | 10 | * Redistribution and use in source and binary forms, with or without |
11 | * modification, are permitted provided that the following conditions | | 11 | * modification, are permitted provided that the following conditions |
12 | * are met: | | 12 | * are met: |
13 | * 1. Redistributions of source code must retain the above copyright | | 13 | * 1. Redistributions of source code must retain the above copyright |
14 | * notice, this list of conditions and the following disclaimer. | | 14 | * notice, this list of conditions and the following disclaimer. |
| @@ -50,27 +50,27 @@ | | | @@ -50,27 +50,27 @@ |
50 | * queue. If a thread decides it doesn't need to block after all, then this | | 50 | * queue. If a thread decides it doesn't need to block after all, then this |
51 | * interlock must be released by explicitly aborting the turnstile | | 51 | * interlock must be released by explicitly aborting the turnstile |
52 | * operation. | | 52 | * operation. |
53 | * | | 53 | * |
54 | * When a thread is awakened, it needs to get its turnstile back. If there | | 54 | * When a thread is awakened, it needs to get its turnstile back. If there |
55 | * are still other threads waiting in the active turnstile, the thread | | 55 | * are still other threads waiting in the active turnstile, the thread |
56 | * grabs a free turnstile off the free list. Otherwise, it can take back | | 56 | * grabs a free turnstile off the free list. Otherwise, it can take back |
57 | * the active turnstile from the lock (thus deactivating the turnstile). | | 57 | * the active turnstile from the lock (thus deactivating the turnstile). |
58 | * | | 58 | * |
59 | * Turnstiles are the place to do priority inheritence. | | 59 | * Turnstiles are the place to do priority inheritence. |
60 | */ | | 60 | */ |
61 | | | 61 | |
62 | #include <sys/cdefs.h> | | 62 | #include <sys/cdefs.h> |
63 | __KERNEL_RCSID(0, "$NetBSD: kern_turnstile.c,v 1.31 2011/12/02 12:31:53 yamt Exp $"); | | 63 | __KERNEL_RCSID(0, "$NetBSD: kern_turnstile.c,v 1.32 2012/06/15 13:51:40 yamt Exp $"); |
64 | | | 64 | |
65 | #include <sys/param.h> | | 65 | #include <sys/param.h> |
66 | #include <sys/lockdebug.h> | | 66 | #include <sys/lockdebug.h> |
67 | #include <sys/pool.h> | | 67 | #include <sys/pool.h> |
68 | #include <sys/proc.h> | | 68 | #include <sys/proc.h> |
69 | #include <sys/sleepq.h> | | 69 | #include <sys/sleepq.h> |
70 | #include <sys/systm.h> | | 70 | #include <sys/systm.h> |
71 | | | 71 | |
72 | #define TS_HASH_SIZE 64 | | 72 | #define TS_HASH_SIZE 64 |
73 | #define TS_HASH_MASK (TS_HASH_SIZE - 1) | | 73 | #define TS_HASH_MASK (TS_HASH_SIZE - 1) |
74 | #define TS_HASH(obj) (((uintptr_t)(obj) >> 3) & TS_HASH_MASK) | | 74 | #define TS_HASH(obj) (((uintptr_t)(obj) >> 3) & TS_HASH_MASK) |
75 | | | 75 | |
76 | static tschain_t turnstile_tab[TS_HASH_SIZE] __cacheline_aligned; | | 76 | static tschain_t turnstile_tab[TS_HASH_SIZE] __cacheline_aligned; |
| @@ -189,100 +189,120 @@ void | | | @@ -189,100 +189,120 @@ void |
189 | turnstile_exit(wchan_t obj) | | 189 | turnstile_exit(wchan_t obj) |
190 | { | | 190 | { |
191 | tschain_t *tc; | | 191 | tschain_t *tc; |
192 | | | 192 | |
193 | tc = &turnstile_tab[TS_HASH(obj)]; | | 193 | tc = &turnstile_tab[TS_HASH(obj)]; |
194 | mutex_spin_exit(tc->tc_mutex); | | 194 | mutex_spin_exit(tc->tc_mutex); |
195 | } | | 195 | } |
196 | | | 196 | |
197 | /* | | 197 | /* |
198 | * turnstile_lendpri: | | 198 | * turnstile_lendpri: |
199 | * | | 199 | * |
200 | * Lend our priority to lwps on the blocking chain. | | 200 | * Lend our priority to lwps on the blocking chain. |
201 | * | | 201 | * |
202 | * | | 202 | * If the current owner of the lock (l->l_wchan, set by sleepq_enqueue) |
| | | 203 | * has a priority lower than ours (lwp_eprio(l)), lend our priority to |
| | | 204 | * him to avoid priority inversions. |
203 | */ | | 205 | */ |
204 | | | 206 | |
205 | static void | | 207 | static void |
206 | turnstile_lendpri(lwp_t *cur) | | 208 | turnstile_lendpri(lwp_t *cur) |
207 | { | | 209 | { |
208 | lwp_t * l = cur; | | 210 | lwp_t * l = cur; |
209 | pri_t prio; | | 211 | pri_t prio; |
210 | | | 212 | |
211 | /* | | 213 | /* |
212 | * NOTE: if you get a panic in this code block, it is likely that | | 214 | * NOTE: if you get a panic in this code block, it is likely that |
213 | * a lock has been destroyed or corrupted while still in use. Try | | 215 | * a lock has been destroyed or corrupted while still in use. Try |
214 | * compiling a kernel with LOCKDEBUG to pinpoint the problem. | | 216 | * compiling a kernel with LOCKDEBUG to pinpoint the problem. |
215 | */ | | 217 | */ |
216 | | | 218 | |
217 | LOCKDEBUG_BARRIER(l->l_mutex, 1); | | 219 | LOCKDEBUG_BARRIER(l->l_mutex, 1); |
218 | KASSERT(l == curlwp); | | 220 | KASSERT(l == curlwp); |
219 | prio = lwp_eprio(l); | | 221 | prio = lwp_eprio(l); |
220 | for (;;) { | | 222 | for (;;) { |
221 | lwp_t *owner; | | 223 | lwp_t *owner; |
222 | turnstile_t *ts; | | 224 | turnstile_t *ts; |
223 | bool dolock; | | 225 | bool dolock; |
224 | | | 226 | |
225 | if (l->l_wchan == NULL) | | 227 | if (l->l_wchan == NULL) |
226 | break; | | 228 | break; |
227 | | | 229 | |
| | | 230 | /* |
| | | 231 | * Ask syncobj the owner of the lock. |
| | | 232 | */ |
228 | owner = (*l->l_syncobj->sobj_owner)(l->l_wchan); | | 233 | owner = (*l->l_syncobj->sobj_owner)(l->l_wchan); |
229 | if (owner == NULL) | | 234 | if (owner == NULL) |
230 | break; | | 235 | break; |
231 | | | 236 | |
232 | /* The owner may have changed as we have dropped the tc lock */ | | 237 | /* |
| | | 238 | * The owner may have changed as we have dropped the tc lock. |
| | | 239 | */ |
233 | if (cur == owner) { | | 240 | if (cur == owner) { |
234 | /* | | 241 | /* |
235 | * we own the lock: stop here, sleepq_block() | | 242 | * We own the lock: stop here, sleepq_block() |
236 | * should wake up immediatly | | 243 | * should wake up immediatly. |
237 | */ | | 244 | */ |
238 | break; | | 245 | break; |
239 | } | | 246 | } |
240 | if (l->l_mutex != owner->l_mutex) | | 247 | /* |
241 | dolock = true; | | 248 | * Acquire owner->l_mutex if we don't have it yet. |
242 | else | | 249 | * Because we already have another LWP lock (l->l_mutex) held, |
243 | dolock = false; | | 250 | * we need to play a try lock dance to avoid deadlock. |
| | | 251 | */ |
| | | 252 | dolock = l->l_mutex != owner->l_mutex; |
244 | if (l == owner || (dolock && !lwp_trylock(owner))) { | | 253 | if (l == owner || (dolock && !lwp_trylock(owner))) { |
245 | /* | | 254 | /* |
246 | * restart from curlwp. | | 255 | * The owner was changed behind us or trylock failed. |
| | | 256 | * Restart from curlwp. |
| | | 257 | * |
247 | * Note that there may be a livelock here: | | 258 | * Note that there may be a livelock here: |
248 | * the owner may try grabing cur's lock (which is | | 259 | * the owner may try grabing cur's lock (which is the |
249 | * the tc lock) while we're trying to grab | | 260 | * tc lock) while we're trying to grab the owner's lock. |
250 | * the owner's lock. | | | |
251 | */ | | 261 | */ |
252 | lwp_unlock(l); | | 262 | lwp_unlock(l); |
253 | l = cur; | | 263 | l = cur; |
254 | lwp_lock(l); | | 264 | lwp_lock(l); |
255 | prio = lwp_eprio(l); | | 265 | prio = lwp_eprio(l); |
256 | continue; | | 266 | continue; |
257 | } | | 267 | } |
| | | 268 | /* |
| | | 269 | * If the owner's priority is already higher than ours, |
| | | 270 | * there's nothing to do anymore. |
| | | 271 | */ |
258 | if (prio <= lwp_eprio(owner)) { | | 272 | if (prio <= lwp_eprio(owner)) { |
259 | if (dolock) | | 273 | if (dolock) |
260 | lwp_unlock(owner); | | 274 | lwp_unlock(owner); |
261 | break; | | 275 | break; |
262 | } | | 276 | } |
| | | 277 | /* |
| | | 278 | * Lend our priority to the 'owner' LWP. |
| | | 279 | * |
| | | 280 | * Update lenders info for turnstile_unlendpri. |
| | | 281 | */ |
263 | ts = l->l_ts; | | 282 | ts = l->l_ts; |
264 | KASSERT(ts->ts_inheritor == owner || ts->ts_inheritor == NULL); | | 283 | KASSERT(ts->ts_inheritor == owner || ts->ts_inheritor == NULL); |
265 | if (ts->ts_inheritor == NULL) { | | 284 | if (ts->ts_inheritor == NULL) { |
266 | ts->ts_inheritor = owner; | | 285 | ts->ts_inheritor = owner; |
267 | ts->ts_eprio = prio; | | 286 | ts->ts_eprio = prio; |
268 | SLIST_INSERT_HEAD(&owner->l_pi_lenders, ts, ts_pichain); | | 287 | SLIST_INSERT_HEAD(&owner->l_pi_lenders, ts, ts_pichain); |
269 | lwp_lendpri(owner, prio); | | 288 | lwp_lendpri(owner, prio); |
270 | } else if (prio > ts->ts_eprio) { | | 289 | } else if (prio > ts->ts_eprio) { |
271 | ts->ts_eprio = prio; | | 290 | ts->ts_eprio = prio; |
272 | lwp_lendpri(owner, prio); | | 291 | lwp_lendpri(owner, prio); |
273 | } | | 292 | } |
274 | if (dolock) | | 293 | if (dolock) |
275 | lwp_unlock(l); | | 294 | lwp_unlock(l); |
| | | 295 | LOCKDEBUG_BARRIER(owner->l_mutex, 1); |
276 | l = owner; | | 296 | l = owner; |
277 | } | | 297 | } |
278 | LOCKDEBUG_BARRIER(l->l_mutex, 1); | | 298 | LOCKDEBUG_BARRIER(l->l_mutex, 1); |
279 | if (cur->l_mutex != l->l_mutex) { | | 299 | if (cur->l_mutex != l->l_mutex) { |
280 | lwp_unlock(l); | | 300 | lwp_unlock(l); |
281 | lwp_lock(cur); | | 301 | lwp_lock(cur); |
282 | } | | 302 | } |
283 | LOCKDEBUG_BARRIER(cur->l_mutex, 1); | | 303 | LOCKDEBUG_BARRIER(cur->l_mutex, 1); |
284 | } | | 304 | } |
285 | | | 305 | |
286 | /* | | 306 | /* |
287 | * turnstile_unlendpri: undo turnstile_lendpri | | 307 | * turnstile_unlendpri: undo turnstile_lendpri |
288 | */ | | 308 | */ |