| Commit | Line | Data |
|---|---|---|
| c31b1324 | 1 | /* |
| c6fbe95a | 2 | * Copyright (c) 2003,2004,2009 The DragonFly Project. All rights reserved. |
| 8c10bfcf MD |
3 | * |
| 4 | * This code is derived from software contributed to The DragonFly Project | |
| 5 | * by Matthew Dillon <dillon@backplane.com> | |
| 6 | * | |
| c31b1324 MD |
7 | * Redistribution and use in source and binary forms, with or without |
| 8 | * modification, are permitted provided that the following conditions | |
| 9 | * are met: | |
| 8c10bfcf | 10 | * |
| c31b1324 MD |
11 | * 1. Redistributions of source code must retain the above copyright |
| 12 | * notice, this list of conditions and the following disclaimer. | |
| 13 | * 2. Redistributions in binary form must reproduce the above copyright | |
| 8c10bfcf MD |
14 | * notice, this list of conditions and the following disclaimer in |
| 15 | * the documentation and/or other materials provided with the | |
| 16 | * distribution. | |
| 17 | * 3. Neither the name of The DragonFly Project nor the names of its | |
| 18 | * contributors may be used to endorse or promote products derived | |
| 19 | * from this software without specific, prior written permission. | |
| 20 | * | |
| 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| 22 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| 23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | |
| 24 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | |
| 25 | * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | |
| 26 | * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, | |
| 27 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
| 28 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED | |
| 29 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |
| 30 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | |
| 31 | * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
| c31b1324 | 32 | * SUCH DAMAGE. |
| c31b1324 MD |
33 | */ |
| 34 | ||
| c6fbe95a MD |
35 | /* |
| 36 | * lwkt_token - Implement soft token locks. | |
| 37 | * | |
| 38 | * Tokens are locks which serialize a thread only while the thread is | |
| 39 | * running. If the thread blocks all tokens are released, then reacquired | |
| 40 | * when the thread resumes. | |
| 41 | * | |
| 42 | * This implementation requires no critical sections or spin locks, but | |
| 43 | * does use atomic_cmpset_ptr(). | |
| 44 | * | |
| 45 | * Tokens may be recursively acquired by the same thread. However the | |
| 46 | * caller must be sure to release such tokens in reverse order. | |
| 47 | */ | |
| c31b1324 MD |
48 | #include <sys/param.h> |
| 49 | #include <sys/systm.h> | |
| 50 | #include <sys/kernel.h> | |
| 51 | #include <sys/proc.h> | |
| 52 | #include <sys/rtprio.h> | |
| 53 | #include <sys/queue.h> | |
| c31b1324 | 54 | #include <sys/sysctl.h> |
| 4883dbe9 | 55 | #include <sys/ktr.h> |
| c31b1324 MD |
56 | #include <sys/kthread.h> |
| 57 | #include <machine/cpu.h> | |
| 58 | #include <sys/lock.h> | |
| 59 | #include <sys/caps.h> | |
| 9d265729 MD |
60 | #include <sys/spinlock.h> |
| 61 | ||
| 62 | #include <sys/thread2.h> | |
| 63 | #include <sys/spinlock2.h> | |
| 3b998fa9 | 64 | #include <sys/mplock2.h> |
| c31b1324 MD |
65 | |
| 66 | #include <vm/vm.h> | |
| 67 | #include <vm/vm_param.h> | |
| 68 | #include <vm/vm_kern.h> | |
| 69 | #include <vm/vm_object.h> | |
| 70 | #include <vm/vm_page.h> | |
| 71 | #include <vm/vm_map.h> | |
| 72 | #include <vm/vm_pager.h> | |
| 73 | #include <vm/vm_extern.h> | |
| 74 | #include <vm/vm_zone.h> | |
| 75 | ||
| 76 | #include <machine/stdarg.h> | |
| c31b1324 MD |
77 | #include <machine/smp.h> |
| 78 | ||
| b12defdc MD |
79 | extern int lwkt_sched_debug; |
| 80 | ||
| 41a01a4d | 81 | #ifndef LWKT_NUM_POOL_TOKENS |
| b12defdc | 82 | #define LWKT_NUM_POOL_TOKENS 4001 /* prime number */ |
| 41a01a4d | 83 | #endif |
| 41a01a4d | 84 | |
| 41a01a4d MD |
85 | static lwkt_token pool_tokens[LWKT_NUM_POOL_TOKENS]; |
| 86 | ||
| f917e9bc | 87 | #define TOKEN_STRING "REF=%p TOK=%p TD=%p" |
| 5bf48697 AE |
88 | #define TOKEN_ARGS lwkt_tokref_t ref, lwkt_token_t tok, struct thread *td |
| 89 | #define CONTENDED_STRING TOKEN_STRING " (contention started)" | |
| 90 | #define UNCONTENDED_STRING TOKEN_STRING " (contention stopped)" | |
| 4883dbe9 MD |
91 | #if !defined(KTR_TOKENS) |
| 92 | #define KTR_TOKENS KTR_ALL | |
| 93 | #endif | |
| 790e4db7 | 94 | |
| 4883dbe9 | 95 | KTR_INFO_MASTER(tokens); |
| 5bf48697 AE |
96 | KTR_INFO(KTR_TOKENS, tokens, fail, 0, TOKEN_STRING, TOKEN_ARGS); |
| 97 | KTR_INFO(KTR_TOKENS, tokens, succ, 1, TOKEN_STRING, TOKEN_ARGS); | |
| 7cd8d145 | 98 | #if 0 |
| 5bf48697 AE |
99 | KTR_INFO(KTR_TOKENS, tokens, release, 2, TOKEN_STRING, TOKEN_ARGS); |
| 100 | KTR_INFO(KTR_TOKENS, tokens, remote, 3, TOKEN_STRING, TOKEN_ARGS); | |
| 101 | KTR_INFO(KTR_TOKENS, tokens, reqremote, 4, TOKEN_STRING, TOKEN_ARGS); | |
| 102 | KTR_INFO(KTR_TOKENS, tokens, reqfail, 5, TOKEN_STRING, TOKEN_ARGS); | |
| 103 | KTR_INFO(KTR_TOKENS, tokens, drain, 6, TOKEN_STRING, TOKEN_ARGS); | |
| 104 | KTR_INFO(KTR_TOKENS, tokens, contention_start, 7, CONTENDED_STRING, TOKEN_ARGS); | |
| 105 | KTR_INFO(KTR_TOKENS, tokens, contention_stop, 7, UNCONTENDED_STRING, TOKEN_ARGS); | |
| 790e4db7 MD |
106 | #endif |
| 107 | ||
| f917e9bc MD |
108 | #define logtoken(name, ref) \ |
| 109 | KTR_LOG(tokens_ ## name, ref, ref->tr_tok, curthread) | |
| 4883dbe9 | 110 | |
| c31b1324 | 111 | /* |
| c9aa7a82 MD |
112 | * Global tokens. These replace the MP lock for major subsystem locking. |
| 113 | * These tokens are initially used to lockup both global and individual | |
| 114 | * operations. | |
| 115 | * | |
| 116 | * Once individual structures get their own locks these tokens are used | |
| 117 | * only to protect global lists & other variables and to interlock | |
| 118 | * allocations and teardowns and such. | |
| 119 | * | |
| 120 | * The UP initializer causes token acquisition to also acquire the MP lock | |
| 121 | * for maximum compatibility. The feature may be enabled and disabled at | |
| 122 | * any time, the MP state is copied to the tokref when the token is acquired | |
| 123 | * and will not race against sysctl changes. | |
| 124 | */ | |
| a3c18566 MD |
125 | struct lwkt_token mp_token = LWKT_TOKEN_INITIALIZER(mp_token); |
| 126 | struct lwkt_token pmap_token = LWKT_TOKEN_INITIALIZER(pmap_token); | |
| 127 | struct lwkt_token dev_token = LWKT_TOKEN_INITIALIZER(dev_token); | |
| 128 | struct lwkt_token vm_token = LWKT_TOKEN_INITIALIZER(vm_token); | |
| 129 | struct lwkt_token vmspace_token = LWKT_TOKEN_INITIALIZER(vmspace_token); | |
| 130 | struct lwkt_token kvm_token = LWKT_TOKEN_INITIALIZER(kvm_token); | |
| 131 | struct lwkt_token proc_token = LWKT_TOKEN_INITIALIZER(proc_token); | |
| 132 | struct lwkt_token tty_token = LWKT_TOKEN_INITIALIZER(tty_token); | |
| 133 | struct lwkt_token vnode_token = LWKT_TOKEN_INITIALIZER(vnode_token); | |
| 134 | struct lwkt_token vmobj_token = LWKT_TOKEN_INITIALIZER(vmobj_token); | |
| c9aa7a82 | 135 | |
| b12defdc MD |
136 | static int lwkt_token_spin = 5; |
| 137 | SYSCTL_INT(_lwkt, OID_AUTO, token_spin, CTLFLAG_RW, | |
| 138 | &lwkt_token_spin, 0, "Decontention spin loops"); | |
| 139 | static int lwkt_token_delay = 0; | |
| 140 | SYSCTL_INT(_lwkt, OID_AUTO, token_delay, CTLFLAG_RW, | |
| 141 | &lwkt_token_delay, 0, "Decontention spin delay in ns"); | |
| fc55f5f2 | 142 | |
| c9aa7a82 MD |
143 | /* |
| 144 | * The collision count is bumped every time the LWKT scheduler fails | |
| 145 | * to acquire needed tokens in addition to a normal lwkt_gettoken() | |
| 146 | * stall. | |
| 147 | */ | |
| b5d16701 MD |
148 | SYSCTL_LONG(_lwkt, OID_AUTO, mp_collisions, CTLFLAG_RW, |
| 149 | &mp_token.t_collisions, 0, "Collision counter of mp_token"); | |
| 0c52fa62 SG |
150 | SYSCTL_LONG(_lwkt, OID_AUTO, pmap_collisions, CTLFLAG_RW, |
| 151 | &pmap_token.t_collisions, 0, "Collision counter of pmap_token"); | |
| 152 | SYSCTL_LONG(_lwkt, OID_AUTO, dev_collisions, CTLFLAG_RW, | |
| 153 | &dev_token.t_collisions, 0, "Collision counter of dev_token"); | |
| 154 | SYSCTL_LONG(_lwkt, OID_AUTO, vm_collisions, CTLFLAG_RW, | |
| 155 | &vm_token.t_collisions, 0, "Collision counter of vm_token"); | |
| 156 | SYSCTL_LONG(_lwkt, OID_AUTO, vmspace_collisions, CTLFLAG_RW, | |
| 157 | &vmspace_token.t_collisions, 0, "Collision counter of vmspace_token"); | |
| 158 | SYSCTL_LONG(_lwkt, OID_AUTO, kvm_collisions, CTLFLAG_RW, | |
| 159 | &kvm_token.t_collisions, 0, "Collision counter of kvm_token"); | |
| 160 | SYSCTL_LONG(_lwkt, OID_AUTO, proc_collisions, CTLFLAG_RW, | |
| 161 | &proc_token.t_collisions, 0, "Collision counter of proc_token"); | |
| 162 | SYSCTL_LONG(_lwkt, OID_AUTO, tty_collisions, CTLFLAG_RW, | |
| 163 | &tty_token.t_collisions, 0, "Collision counter of tty_token"); | |
| 164 | SYSCTL_LONG(_lwkt, OID_AUTO, vnode_collisions, CTLFLAG_RW, | |
| 165 | &vnode_token.t_collisions, 0, "Collision counter of vnode_token"); | |
| c9aa7a82 | 166 | |
| bb4ae18c MD |
167 | #ifdef DEBUG_LOCKS_LATENCY |
| 168 | ||
| 169 | static long tokens_add_latency; | |
| 170 | SYSCTL_LONG(_debug, OID_AUTO, tokens_add_latency, CTLFLAG_RW, | |
| 171 | &tokens_add_latency, 0, | |
| 172 | "Add spinlock latency"); | |
| 173 | ||
| 174 | #endif | |
| 175 | ||
| 176 | ||
| b12defdc MD |
177 | static int _lwkt_getalltokens_sorted(thread_t td); |
| 178 | ||
| b5d16701 MD |
179 | #ifdef SMP |
| 180 | /* | |
| 181 | * Acquire the initial mplock | |
| 182 | * | |
| 183 | * (low level boot only) | |
| 184 | */ | |
| 185 | void | |
| 186 | cpu_get_initial_mplock(void) | |
| 187 | { | |
| 188 | KKASSERT(mp_token.t_ref == NULL); | |
| 189 | if (lwkt_trytoken(&mp_token) == FALSE) | |
| 190 | panic("cpu_get_initial_mplock"); | |
| 191 | } | |
| 192 | #endif | |
| 193 | ||
| c9aa7a82 | 194 | /* |
| b12defdc MD |
195 | * Return a pool token given an address. Use a prime number to reduce |
| 196 | * overlaps. | |
| c6fbe95a MD |
197 | */ |
| 198 | static __inline | |
| 199 | lwkt_token_t | |
| 200 | _lwkt_token_pool_lookup(void *ptr) | |
| 201 | { | |
| b12defdc | 202 | u_int i; |
| c6fbe95a | 203 | |
| b12defdc MD |
204 | i = (u_int)(uintptr_t)ptr % LWKT_NUM_POOL_TOKENS; |
| 205 | return(&pool_tokens[i]); | |
| c6fbe95a MD |
206 | } |
| 207 | ||
| 3b998fa9 MD |
208 | /* |
| 209 | * Initialize a tokref_t prior to making it visible in the thread's | |
| 210 | * token array. | |
| 211 | */ | |
| 212 | static __inline | |
| 213 | void | |
| 54341a3b | 214 | _lwkt_tokref_init(lwkt_tokref_t ref, lwkt_token_t tok, thread_t td, long excl) |
| 3b998fa9 MD |
215 | { |
| 216 | ref->tr_tok = tok; | |
| 54341a3b | 217 | ref->tr_count = excl; |
| 3b998fa9 | 218 | ref->tr_owner = td; |
| 3b998fa9 | 219 | } |
| c6fbe95a | 220 | |
| 85946b6c | 221 | /* |
| 54341a3b MD |
222 | * Attempt to acquire a shared or exclusive token. Returns TRUE on success, |
| 223 | * FALSE on failure. | |
| 224 | * | |
| 225 | * If TOK_EXCLUSIVE is set in mode we are attempting to get an exclusive | |
| 226 | * token, otherwise are attempting to get a shared token. | |
| 227 | * | |
| 228 | * If TOK_EXCLREQ is set in mode this is a blocking operation, otherwise | |
| 229 | * it is a non-blocking operation (for both exclusive or shared acquisions). | |
| 85946b6c | 230 | */ |
| 54341a3b | 231 | static __inline |
| b12defdc | 232 | int |
| 54341a3b | 233 | _lwkt_trytokref(lwkt_tokref_t ref, thread_t td, long mode) |
| b12defdc | 234 | { |
| 54341a3b MD |
235 | lwkt_token_t tok; |
| 236 | lwkt_tokref_t oref; | |
| 237 | long count; | |
| b12defdc | 238 | |
| 4bac0e14 | 239 | tok = ref->tr_tok; |
| 54341a3b MD |
240 | KASSERT(((mode & TOK_EXCLREQ) == 0 || /* non blocking */ |
| 241 | td->td_gd->gd_intr_nesting_level == 0 || | |
| 242 | panic_cpu_gd == mycpu), | |
| 243 | ("Attempt to acquire token %p not already " | |
| 244 | "held in hard code section", tok)); | |
| 245 | ||
| 54341a3b MD |
246 | if (mode & TOK_EXCLUSIVE) { |
| 247 | /* | |
| 248 | * Attempt to get an exclusive token | |
| 249 | */ | |
| 250 | for (;;) { | |
| 251 | count = tok->t_count; | |
| 252 | oref = tok->t_ref; /* can be NULL */ | |
| 253 | cpu_ccfence(); | |
| 254 | if ((count & ~TOK_EXCLREQ) == 0) { | |
| 255 | /* | |
| 256 | * It is possible to get the exclusive bit. | |
| 257 | * We must clear TOK_EXCLREQ on successful | |
| 258 | * acquisition. | |
| 259 | */ | |
| 260 | if (atomic_cmpset_long(&tok->t_count, count, | |
| 261 | (count & ~TOK_EXCLREQ) | | |
| 262 | TOK_EXCLUSIVE)) { | |
| 263 | KKASSERT(tok->t_ref == NULL); | |
| 264 | tok->t_ref = ref; | |
| 265 | return TRUE; | |
| 266 | } | |
| 267 | /* retry */ | |
| 268 | } else if ((count & TOK_EXCLUSIVE) && | |
| 269 | oref >= &td->td_toks_base && | |
| 270 | oref < td->td_toks_stop) { | |
| 271 | /* | |
| 272 | * Our thread already holds the exclusive | |
| 273 | * bit, we treat this tokref as a shared | |
| 274 | * token (sorta) to make the token release | |
| 275 | * code easier. | |
| 276 | * | |
| 277 | * NOTE: oref cannot race above if it | |
| 278 | * happens to be ours, so we're good. | |
| 279 | * But we must still have a stable | |
| 280 | * variable for both parts of the | |
| 281 | * comparison. | |
| 282 | * | |
| 283 | * NOTE: Since we already have an exclusive | |
| 284 | * lock and don't need to check EXCLREQ | |
| 285 | * we can just use an atomic_add here | |
| 286 | */ | |
| 287 | atomic_add_long(&tok->t_count, TOK_INCR); | |
| 288 | ref->tr_count &= ~TOK_EXCLUSIVE; | |
| 289 | return TRUE; | |
| 290 | } else if ((mode & TOK_EXCLREQ) && | |
| 291 | (count & TOK_EXCLREQ) == 0) { | |
| 292 | /* | |
| 293 | * Unable to get the exclusive bit but being | |
| 294 | * asked to set the exclusive-request bit. | |
| 295 | * Since we are going to retry anyway just | |
| 296 | * set the bit unconditionally. | |
| 297 | */ | |
| 298 | atomic_set_long(&tok->t_count, TOK_EXCLREQ); | |
| 299 | return FALSE; | |
| 300 | } else { | |
| 301 | /* | |
| 302 | * Unable to get the exclusive bit and not | |
| 303 | * being asked to set the exclusive-request | |
| 304 | * (aka lwkt_trytoken()), or EXCLREQ was | |
| 305 | * already set. | |
| 306 | */ | |
| 307 | cpu_pause(); | |
| 308 | return FALSE; | |
| 309 | } | |
| 310 | /* retry */ | |
| b12defdc | 311 | } |
| 54341a3b MD |
312 | } else { |
| 313 | /* | |
| 314 | * Attempt to get a shared token. Note that TOK_EXCLREQ | |
| 315 | * for shared tokens simply means the caller intends to | |
| 316 | * block. We never actually set the bit in tok->t_count. | |
| 317 | */ | |
| 318 | for (;;) { | |
| 319 | count = tok->t_count; | |
| 320 | oref = tok->t_ref; /* can be NULL */ | |
| 321 | cpu_ccfence(); | |
| 322 | if ((count & (TOK_EXCLUSIVE/*|TOK_EXCLREQ*/)) == 0) { | |
| 323 | /* XXX EXCLREQ should work */ | |
| 324 | /* | |
| 325 | * It is possible to get the token shared. | |
| 326 | */ | |
| 327 | if (atomic_cmpset_long(&tok->t_count, count, | |
| 328 | count + TOK_INCR)) { | |
| 329 | return TRUE; | |
| 330 | } | |
| 331 | /* retry */ | |
| 332 | } else if ((count & TOK_EXCLUSIVE) && | |
| 333 | oref >= &td->td_toks_base && | |
| 334 | oref < td->td_toks_stop) { | |
| 335 | /* | |
| 336 | * We own the exclusive bit on the token so | |
| 337 | * we can in fact also get it shared. | |
| 338 | */ | |
| 339 | atomic_add_long(&tok->t_count, TOK_INCR); | |
| 340 | return TRUE; | |
| 341 | } else { | |
| 342 | /* | |
| 343 | * We failed to get the token shared | |
| 344 | */ | |
| 345 | return FALSE; | |
| 346 | } | |
| 347 | /* retry */ | |
| b12defdc MD |
348 | } |
| 349 | } | |
| b12defdc MD |
350 | } |
| 351 | ||
| 352 | static __inline | |
| b12defdc | 353 | int |
| 54341a3b | 354 | _lwkt_trytokref_spin(lwkt_tokref_t ref, thread_t td, long mode) |
| cbdd23b1 | 355 | { |
| 54341a3b | 356 | int spin; |
| fc55f5f2 | 357 | |
| bb4ae18c MD |
358 | if (_lwkt_trytokref(ref, td, mode)) { |
| 359 | #ifdef DEBUG_LOCKS_LATENCY | |
| 360 | long j; | |
| 361 | for (j = tokens_add_latency; j > 0; --j) | |
| 362 | cpu_ccfence(); | |
| 363 | #endif | |
| 54341a3b | 364 | return TRUE; |
| bb4ae18c | 365 | } |
| 54341a3b | 366 | for (spin = lwkt_token_spin; spin > 0; --spin) { |
| b12defdc MD |
367 | if (lwkt_token_delay) |
| 368 | tsc_delay(lwkt_token_delay); | |
| 369 | else | |
| 370 | cpu_pause(); | |
| bb4ae18c MD |
371 | if (_lwkt_trytokref(ref, td, mode)) { |
| 372 | #ifdef DEBUG_LOCKS_LATENCY | |
| 373 | long j; | |
| 374 | for (j = tokens_add_latency; j > 0; --j) | |
| 375 | cpu_ccfence(); | |
| 376 | #endif | |
| 54341a3b | 377 | return TRUE; |
| bb4ae18c | 378 | } |
| fc55f5f2 | 379 | } |
| 54341a3b | 380 | return FALSE; |
| b12defdc MD |
381 | } |
| 382 | ||
| 383 | /* | |
| 54341a3b | 384 | * Release a token that we hold. |
| b12defdc MD |
385 | */ |
| 386 | static __inline | |
| 387 | void | |
| 54341a3b | 388 | _lwkt_reltokref(lwkt_tokref_t ref, thread_t td) |
| b12defdc | 389 | { |
| 54341a3b MD |
390 | lwkt_token_t tok; |
| 391 | long count; | |
| b12defdc | 392 | |
| 54341a3b | 393 | tok = ref->tr_tok; |
| b12defdc | 394 | for (;;) { |
| 54341a3b | 395 | count = tok->t_count; |
| b12defdc | 396 | cpu_ccfence(); |
| 54341a3b MD |
397 | if (tok->t_ref == ref) { |
| 398 | /* | |
| 399 | * We are an exclusive holder. We must clear tr_ref | |
| 400 | * before we clear the TOK_EXCLUSIVE bit. If we are | |
| 401 | * unable to clear the bit we must restore | |
| 402 | * tok->t_ref. | |
| 403 | */ | |
| 404 | KKASSERT(count & TOK_EXCLUSIVE); | |
| b12defdc | 405 | tok->t_ref = NULL; |
| 54341a3b MD |
406 | if (atomic_cmpset_long(&tok->t_count, count, |
| 407 | count & ~TOK_EXCLUSIVE)) { | |
| 408 | return; | |
| 409 | } | |
| 410 | tok->t_ref = ref; | |
| 411 | /* retry */ | |
| 412 | } else { | |
| 413 | /* | |
| 414 | * We are a shared holder | |
| 415 | */ | |
| 416 | KKASSERT(count & TOK_COUNTMASK); | |
| 417 | if (atomic_cmpset_long(&tok->t_count, count, | |
| 418 | count - TOK_INCR)) { | |
| 419 | return; | |
| 420 | } | |
| 421 | /* retry */ | |
| b12defdc | 422 | } |
| 54341a3b | 423 | /* retry */ |
| 2a418930 | 424 | } |
| cbdd23b1 MD |
425 | } |
| 426 | ||
| 427 | /* | |
| 9d265729 | 428 | * Obtain all the tokens required by the specified thread on the current |
| 1fe5fad2 MD |
429 | * cpu, return 0 on failure and non-zero on success. If a failure occurs |
| 430 | * any partially acquired tokens will be released prior to return. | |
| dd55d707 | 431 | * |
| 54341a3b MD |
432 | * lwkt_getalltokens is called by the LWKT scheduler to re-acquire all |
| 433 | * tokens that the thread had to release when it switched away. | |
| 7eb611ef | 434 | * |
| b12defdc MD |
435 | * If spinning is non-zero this function acquires the tokens in a particular |
| 436 | * order to deal with potential deadlocks. We simply use address order for | |
| 437 | * the case. | |
| 3b998fa9 | 438 | * |
| 7eb611ef | 439 | * Called from a critical section. |
| c31b1324 | 440 | */ |
| 41a01a4d | 441 | int |
| b12defdc | 442 | lwkt_getalltokens(thread_t td, int spinning) |
| 41a01a4d | 443 | { |
| 3b998fa9 | 444 | lwkt_tokref_t scan; |
| c6fbe95a MD |
445 | lwkt_token_t tok; |
| 446 | ||
| b12defdc MD |
447 | if (spinning) |
| 448 | return(_lwkt_getalltokens_sorted(td)); | |
| 449 | ||
| 3b998fa9 MD |
450 | /* |
| 451 | * Acquire tokens in forward order, assign or validate tok->t_ref. | |
| 452 | */ | |
| 453 | for (scan = &td->td_toks_base; scan < td->td_toks_stop; ++scan) { | |
| 454 | tok = scan->tr_tok; | |
| c6fbe95a MD |
455 | for (;;) { |
| 456 | /* | |
| 54341a3b | 457 | * Only try really hard on the last token |
| c6fbe95a | 458 | */ |
| 54341a3b MD |
459 | if (scan == td->td_toks_stop - 1) { |
| 460 | if (_lwkt_trytokref_spin(scan, td, scan->tr_count)) | |
| 461 | break; | |
| 462 | } else { | |
| 463 | if (_lwkt_trytokref(scan, td, scan->tr_count)) | |
| 464 | break; | |
| c6fbe95a MD |
465 | } |
| 466 | ||
| 467 | /* | |
| 54341a3b MD |
468 | * Otherwise we failed to acquire all the tokens. |
| 469 | * Release whatever we did get. | |
| 3b998fa9 | 470 | */ |
| 85946b6c MD |
471 | if (lwkt_sched_debug > 0) { |
| 472 | --lwkt_sched_debug; | |
| 473 | kprintf("toka %p %s %s\n", | |
| 474 | tok, tok->t_desc, td->td_comm); | |
| 475 | } | |
| 2a9d4663 | 476 | td->td_wmesg = tok->t_desc; |
| 54341a3b MD |
477 | ++tok->t_collisions; |
| 478 | while (--scan >= &td->td_toks_base) | |
| 479 | _lwkt_reltokref(scan, td); | |
| 3b998fa9 | 480 | return(FALSE); |
| 38717797 | 481 | } |
| 41a01a4d | 482 | } |
| c6fbe95a | 483 | return (TRUE); |
| c31b1324 MD |
484 | } |
| 485 | ||
| 41a01a4d | 486 | /* |
| 9d265729 | 487 | * Release all tokens owned by the specified thread on the current cpu. |
| c6fbe95a MD |
488 | * |
| 489 | * This code is really simple. Even in cases where we own all the tokens | |
| b12defdc MD |
490 | * note that t_ref may not match the scan for recursively held tokens which |
| 491 | * are held deeper in the stack, or for the case where a lwkt_getalltokens() | |
| 492 | * failed. | |
| 3b998fa9 | 493 | * |
| b12defdc | 494 | * Tokens are released in reverse order to reduce chasing race failures. |
| 7eb611ef MD |
495 | * |
| 496 | * Called from a critical section. | |
| 41a01a4d | 497 | */ |
| 9d265729 MD |
498 | void |
| 499 | lwkt_relalltokens(thread_t td) | |
| 41a01a4d | 500 | { |
| 3b998fa9 | 501 | lwkt_tokref_t scan; |
| c6fbe95a | 502 | |
| 54341a3b MD |
503 | /* |
| 504 | * Weird order is to try to avoid a panic loop | |
| 505 | */ | |
| 506 | if (td->td_toks_have) { | |
| 507 | scan = td->td_toks_have; | |
| 508 | td->td_toks_have = NULL; | |
| 509 | } else { | |
| 510 | scan = td->td_toks_stop; | |
| b12defdc | 511 | } |
| 54341a3b MD |
512 | while (--scan >= &td->td_toks_base) |
| 513 | _lwkt_reltokref(scan, td); | |
| b12defdc MD |
514 | } |
| 515 | ||
| 516 | /* | |
| 517 | * This is the decontention version of lwkt_getalltokens(). The tokens are | |
| 518 | * acquired in address-sorted order to deal with any deadlocks. Ultimately | |
| 519 | * token failures will spin into the scheduler and get here. | |
| 520 | * | |
| b12defdc MD |
521 | * Called from critical section |
| 522 | */ | |
| 523 | static | |
| 524 | int | |
| 525 | _lwkt_getalltokens_sorted(thread_t td) | |
| 526 | { | |
| b12defdc MD |
527 | lwkt_tokref_t sort_array[LWKT_MAXTOKENS]; |
| 528 | lwkt_tokref_t scan; | |
| b12defdc MD |
529 | lwkt_token_t tok; |
| 530 | int i; | |
| 531 | int j; | |
| 532 | int n; | |
| 533 | ||
| 534 | /* | |
| 535 | * Sort the token array. Yah yah, I know this isn't fun. | |
| 536 | * | |
| 537 | * NOTE: Recursively acquired tokens are ordered the same as in the | |
| 538 | * td_toks_array so we can always get the earliest one first. | |
| 539 | */ | |
| 540 | i = 0; | |
| 541 | scan = &td->td_toks_base; | |
| 542 | while (scan < td->td_toks_stop) { | |
| 543 | for (j = 0; j < i; ++j) { | |
| 544 | if (scan->tr_tok < sort_array[j]->tr_tok) | |
| 545 | break; | |
| 546 | } | |
| 547 | if (j != i) { | |
| 548 | bcopy(sort_array + j, sort_array + j + 1, | |
| 549 | (i - j) * sizeof(lwkt_tokref_t)); | |
| cbdd23b1 | 550 | } |
| b12defdc MD |
551 | sort_array[j] = scan; |
| 552 | ++scan; | |
| 553 | ++i; | |
| 41a01a4d | 554 | } |
| b12defdc MD |
555 | n = i; |
| 556 | ||
| 557 | /* | |
| 558 | * Acquire tokens in forward order, assign or validate tok->t_ref. | |
| 559 | */ | |
| 560 | for (i = 0; i < n; ++i) { | |
| 561 | scan = sort_array[i]; | |
| 562 | tok = scan->tr_tok; | |
| 563 | for (;;) { | |
| 564 | /* | |
| 54341a3b | 565 | * Only try really hard on the last token |
| b12defdc | 566 | */ |
| 54341a3b MD |
567 | if (scan == td->td_toks_stop - 1) { |
| 568 | if (_lwkt_trytokref_spin(scan, td, scan->tr_count)) | |
| 569 | break; | |
| 570 | } else { | |
| 571 | if (_lwkt_trytokref(scan, td, scan->tr_count)) | |
| 572 | break; | |
| b12defdc MD |
573 | } |
| 574 | ||
| 575 | /* | |
| 54341a3b MD |
576 | * Otherwise we failed to acquire all the tokens. |
| 577 | * Release whatever we did get. | |
| b12defdc | 578 | */ |
| 85946b6c MD |
579 | if (lwkt_sched_debug > 0) { |
| 580 | --lwkt_sched_debug; | |
| 581 | kprintf("tokb %p %s %s\n", | |
| 582 | tok, tok->t_desc, td->td_comm); | |
| 583 | } | |
| b12defdc | 584 | td->td_wmesg = tok->t_desc; |
| 54341a3b MD |
585 | ++tok->t_collisions; |
| 586 | while (--i >= 0) { | |
| 587 | scan = sort_array[i]; | |
| 588 | _lwkt_reltokref(scan, td); | |
| b12defdc | 589 | } |
| 54341a3b | 590 | return(FALSE); |
| b12defdc MD |
591 | } |
| 592 | } | |
| 593 | ||
| 594 | /* | |
| 595 | * We were successful, there is no need for another core to signal | |
| 596 | * us. | |
| 597 | */ | |
| b12defdc | 598 | return (TRUE); |
| 41a01a4d MD |
599 | } |
| 600 | ||
| 41a01a4d | 601 | /* |
| b5d16701 | 602 | * Get a serializing token. This routine can block. |
| c6fbe95a | 603 | */ |
| 7eb611ef | 604 | void |
| b5d16701 | 605 | lwkt_gettoken(lwkt_token_t tok) |
| 7eb611ef | 606 | { |
| b5d16701 MD |
607 | thread_t td = curthread; |
| 608 | lwkt_tokref_t ref; | |
| b5d16701 | 609 | |
| b5d16701 MD |
610 | ref = td->td_toks_stop; |
| 611 | KKASSERT(ref < &td->td_toks_end); | |
| 612 | ++td->td_toks_stop; | |
| 613 | cpu_ccfence(); | |
| 54341a3b | 614 | _lwkt_tokref_init(ref, tok, td, TOK_EXCLUSIVE|TOK_EXCLREQ); |
| b5d16701 | 615 | |
| 784abcf0 VS |
616 | #ifdef DEBUG_LOCKS |
| 617 | /* | |
| 618 | * Taking an exclusive token after holding it shared will | |
| 619 | * livelock. Scan for that case and assert. | |
| 620 | */ | |
| 621 | lwkt_tokref_t tk; | |
| 622 | int found = 0; | |
| 623 | for (tk = &td->td_toks_base; tk < ref; tk++) { | |
| 624 | if (tk->tr_tok != tok) | |
| 625 | continue; | |
| 626 | ||
| 627 | found++; | |
| 628 | if (tk->tr_count & TOK_EXCLUSIVE) | |
| 629 | goto good; | |
| 630 | } | |
| 631 | /* We found only shared instances of this token if found >0 here */ | |
| 632 | KASSERT((found == 0), ("Token %p s/x livelock", tok)); | |
| 633 | good: | |
| 634 | #endif | |
| 635 | ||
| 54341a3b MD |
636 | if (_lwkt_trytokref_spin(ref, td, TOK_EXCLUSIVE|TOK_EXCLREQ)) |
| 637 | return; | |
| 638 | ||
| 639 | /* | |
| 640 | * Give up running if we can't acquire the token right now. | |
| 641 | * | |
| 642 | * Since the tokref is already active the scheduler now | |
| 643 | * takes care of acquisition, so we need only call | |
| 644 | * lwkt_switch(). | |
| 645 | * | |
| 646 | * Since we failed this was not a recursive token so upon | |
| 647 | * return tr_tok->t_ref should be assigned to this specific | |
| 648 | * ref. | |
| 649 | */ | |
| 650 | td->td_wmesg = tok->t_desc; | |
| 651 | ++tok->t_collisions; | |
| 652 | logtoken(fail, ref); | |
| 653 | td->td_toks_have = td->td_toks_stop - 1; | |
| 654 | lwkt_switch(); | |
| 655 | logtoken(succ, ref); | |
| 656 | KKASSERT(tok->t_ref == ref); | |
| 7eb611ef MD |
657 | } |
| 658 | ||
| 54341a3b MD |
659 | /* |
| 660 | * Similar to gettoken but we acquire a shared token instead of an exclusive | |
| 661 | * token. | |
| 662 | */ | |
| 41a01a4d | 663 | void |
| 54341a3b | 664 | lwkt_gettoken_shared(lwkt_token_t tok) |
| 4a28fe22 MD |
665 | { |
| 666 | thread_t td = curthread; | |
| 667 | lwkt_tokref_t ref; | |
| b5d16701 | 668 | |
| 4a28fe22 MD |
669 | ref = td->td_toks_stop; |
| 670 | KKASSERT(ref < &td->td_toks_end); | |
| 4a28fe22 | 671 | ++td->td_toks_stop; |
| b528f10f | 672 | cpu_ccfence(); |
| 54341a3b | 673 | _lwkt_tokref_init(ref, tok, td, TOK_EXCLREQ); |
| b5d16701 | 674 | |
| 784abcf0 VS |
675 | #ifdef DEBUG_LOCKS |
| 676 | /* | |
| 677 | * Taking a pool token in shared mode is a bad idea; other | |
| 678 | * addresses deeper in the call stack may hash to the same pool | |
| 679 | * token and you may end up with an exclusive-shared livelock. | |
| 680 | * Warn in this condition. | |
| 681 | */ | |
| 682 | if ((tok >= &pool_tokens[0]) && | |
| 683 | (tok < &pool_tokens[LWKT_NUM_POOL_TOKENS])) | |
| 684 | kprintf("Warning! Taking pool token %p in shared mode\n", tok); | |
| 685 | #endif | |
| 686 | ||
| 687 | ||
| 54341a3b MD |
688 | if (_lwkt_trytokref_spin(ref, td, TOK_EXCLREQ)) |
| 689 | return; | |
| b5d16701 | 690 | |
| 54341a3b MD |
691 | /* |
| 692 | * Give up running if we can't acquire the token right now. | |
| 693 | * | |
| 694 | * Since the tokref is already active the scheduler now | |
| 695 | * takes care of acquisition, so we need only call | |
| 696 | * lwkt_switch(). | |
| 697 | * | |
| 698 | * Since we failed this was not a recursive token so upon | |
| 699 | * return tr_tok->t_ref should be assigned to this specific | |
| 700 | * ref. | |
| 701 | */ | |
| 702 | td->td_wmesg = tok->t_desc; | |
| 703 | ++tok->t_collisions; | |
| 704 | logtoken(fail, ref); | |
| 705 | td->td_toks_have = td->td_toks_stop - 1; | |
| 706 | lwkt_switch(); | |
| 707 | logtoken(succ, ref); | |
| c31b1324 MD |
708 | } |
| 709 | ||
| 137b3005 MD |
710 | /* |
| 711 | * Attempt to acquire a token, return TRUE on success, FALSE on failure. | |
| 54341a3b MD |
712 | * |
| 713 | * We setup the tokref in case we actually get the token (if we switch later | |
| 714 | * it becomes mandatory so we set TOK_EXCLREQ), but we call trytokref without | |
| 715 | * TOK_EXCLREQ in case we fail. | |
| 137b3005 | 716 | */ |
| c31b1324 | 717 | int |
| 3b998fa9 | 718 | lwkt_trytoken(lwkt_token_t tok) |
| c31b1324 | 719 | { |
| c6fbe95a | 720 | thread_t td = curthread; |
| 3b998fa9 | 721 | lwkt_tokref_t ref; |
| b5d16701 | 722 | |
| 3b998fa9 MD |
723 | ref = td->td_toks_stop; |
| 724 | KKASSERT(ref < &td->td_toks_end); | |
| 3b998fa9 | 725 | ++td->td_toks_stop; |
| b528f10f | 726 | cpu_ccfence(); |
| 54341a3b | 727 | _lwkt_tokref_init(ref, tok, td, TOK_EXCLUSIVE|TOK_EXCLREQ); |
| b5d16701 | 728 | |
| 54341a3b MD |
729 | if (_lwkt_trytokref(ref, td, TOK_EXCLUSIVE)) |
| 730 | return TRUE; | |
| 731 | ||
| 732 | /* | |
| 733 | * Failed, unpend the request | |
| 734 | */ | |
| 735 | cpu_ccfence(); | |
| 736 | --td->td_toks_stop; | |
| 737 | ++tok->t_collisions; | |
| 738 | return FALSE; | |
| 739 | } | |
| 740 | ||
| 741 | ||
| 742 | void | |
| 743 | lwkt_gettoken_hard(lwkt_token_t tok) | |
| 744 | { | |
| 745 | lwkt_gettoken(tok); | |
| 746 | crit_enter_hard(); | |
| 747 | } | |
| 748 | ||
| 749 | lwkt_token_t | |
| 750 | lwkt_getpooltoken(void *ptr) | |
| 751 | { | |
| 752 | lwkt_token_t tok; | |
| 753 | ||
| 754 | tok = _lwkt_token_pool_lookup(ptr); | |
| 755 | lwkt_gettoken(tok); | |
| 756 | return (tok); | |
| 41a01a4d MD |
757 | } |
| 758 | ||
| c31b1324 | 759 | /* |
| c6fbe95a MD |
760 | * Release a serializing token. |
| 761 | * | |
| 3b998fa9 MD |
762 | * WARNING! All tokens must be released in reverse order. This will be |
| 763 | * asserted. | |
| c31b1324 | 764 | */ |
| 41a01a4d | 765 | void |
| 3b998fa9 | 766 | lwkt_reltoken(lwkt_token_t tok) |
| c31b1324 | 767 | { |
| 3b998fa9 MD |
768 | thread_t td = curthread; |
| 769 | lwkt_tokref_t ref; | |
| c6fbe95a | 770 | |
| 3b998fa9 MD |
771 | /* |
| 772 | * Remove ref from thread token list and assert that it matches | |
| 773 | * the token passed in. Tokens must be released in reverse order. | |
| 774 | */ | |
| 775 | ref = td->td_toks_stop - 1; | |
| 776 | KKASSERT(ref >= &td->td_toks_base && ref->tr_tok == tok); | |
| 54341a3b | 777 | _lwkt_reltokref(ref, td); |
| b5d16701 | 778 | cpu_sfence(); |
| a3c18566 | 779 | td->td_toks_stop = ref; |
| c31b1324 MD |
780 | } |
| 781 | ||
| 4a28fe22 MD |
782 | void |
| 783 | lwkt_reltoken_hard(lwkt_token_t tok) | |
| 784 | { | |
| 785 | lwkt_reltoken(tok); | |
| 786 | crit_exit_hard(); | |
| 787 | } | |
| 788 | ||
| 41a01a4d | 789 | /* |
| 177e553a MD |
790 | * It is faster for users of lwkt_getpooltoken() to use the returned |
| 791 | * token and just call lwkt_reltoken(), but for convenience we provide | |
| 792 | * this function which looks the token up based on the ident. | |
| 793 | */ | |
| 794 | void | |
| 795 | lwkt_relpooltoken(void *ptr) | |
| 796 | { | |
| 797 | lwkt_token_t tok = _lwkt_token_pool_lookup(ptr); | |
| 798 | lwkt_reltoken(tok); | |
| 799 | } | |
| 800 | ||
| b5d16701 MD |
801 | /* |
| 802 | * Return a count of the number of token refs the thread has to the | |
| 803 | * specified token, whether it currently owns the token or not. | |
| 804 | */ | |
| 805 | int | |
| 806 | lwkt_cnttoken(lwkt_token_t tok, thread_t td) | |
| 807 | { | |
| 808 | lwkt_tokref_t scan; | |
| 809 | int count = 0; | |
| 810 | ||
| 811 | for (scan = &td->td_toks_base; scan < td->td_toks_stop; ++scan) { | |
| 812 | if (scan->tr_tok == tok) | |
| 813 | ++count; | |
| 814 | } | |
| 815 | return(count); | |
| 816 | } | |
| 817 | ||
| 177e553a | 818 | /* |
| 41a01a4d MD |
819 | * Pool tokens are used to provide a type-stable serializing token |
| 820 | * pointer that does not race against disappearing data structures. | |
| 821 | * | |
| 822 | * This routine is called in early boot just after we setup the BSP's | |
| 823 | * globaldata structure. | |
| 824 | */ | |
| 825 | void | |
| 826 | lwkt_token_pool_init(void) | |
| 827 | { | |
| c6fbe95a | 828 | int i; |
| 41a01a4d | 829 | |
| c6fbe95a | 830 | for (i = 0; i < LWKT_NUM_POOL_TOKENS; ++i) |
| a3c18566 | 831 | lwkt_token_init(&pool_tokens[i], "pool"); |
| 41a01a4d MD |
832 | } |
| 833 | ||
| 834 | lwkt_token_t | |
| c6fbe95a | 835 | lwkt_token_pool_lookup(void *ptr) |
| 41a01a4d | 836 | { |
| c6fbe95a | 837 | return (_lwkt_token_pool_lookup(ptr)); |
| 41a01a4d MD |
838 | } |
| 839 | ||
| 41a01a4d | 840 | /* |
| 5f6b9709 | 841 | * Initialize a token. |
| 41a01a4d MD |
842 | */ |
| 843 | void | |
| a3c18566 | 844 | lwkt_token_init(lwkt_token_t tok, const char *desc) |
| 41a01a4d | 845 | { |
| 54341a3b | 846 | tok->t_count = 0; |
| c6fbe95a | 847 | tok->t_ref = NULL; |
| 73d8e728 | 848 | tok->t_collisions = 0; |
| c5724852 | 849 | tok->t_desc = desc; |
| c31b1324 MD |
850 | } |
| 851 | ||
| 41a01a4d MD |
852 | void |
| 853 | lwkt_token_uninit(lwkt_token_t tok) | |
| 854 | { | |
| c6fbe95a | 855 | /* empty */ |
| 41a01a4d | 856 | } |
| 7eb611ef | 857 | |
| d79f0a1a | 858 | /* |
| 31542241 MD |
859 | * Exchange the two most recent tokens on the tokref stack. This allows |
| 860 | * you to release a token out of order. | |
| d79f0a1a | 861 | * |
| 31542241 MD |
862 | * We have to be careful about the case where the top two tokens are |
| 863 | * the same token. In this case tok->t_ref will point to the deeper | |
| 864 | * ref and must remain pointing to the deeper ref. If we were to swap | |
| 865 | * it the first release would clear the token even though a second | |
| 866 | * ref is still present. | |
| 54341a3b MD |
867 | * |
| 868 | * Only exclusively held tokens contain a reference to the tokref which | |
| 869 | * has to be flipped along with the swap. | |
| d79f0a1a VS |
870 | */ |
| 871 | void | |
| 872 | lwkt_token_swap(void) | |
| 873 | { | |
| 874 | lwkt_tokref_t ref1, ref2; | |
| 875 | lwkt_token_t tok1, tok2; | |
| 54341a3b | 876 | long count1, count2; |
| d79f0a1a VS |
877 | thread_t td = curthread; |
| 878 | ||
| 212f39f5 MD |
879 | crit_enter(); |
| 880 | ||
| d79f0a1a VS |
881 | ref1 = td->td_toks_stop - 1; |
| 882 | ref2 = td->td_toks_stop - 2; | |
| 84ecccb4 MD |
883 | KKASSERT(ref1 >= &td->td_toks_base); |
| 884 | KKASSERT(ref2 >= &td->td_toks_base); | |
| d79f0a1a VS |
885 | |
| 886 | tok1 = ref1->tr_tok; | |
| 887 | tok2 = ref2->tr_tok; | |
| 54341a3b MD |
888 | count1 = ref1->tr_count; |
| 889 | count2 = ref2->tr_count; | |
| 890 | ||
| 31542241 MD |
891 | if (tok1 != tok2) { |
| 892 | ref1->tr_tok = tok2; | |
| 54341a3b | 893 | ref1->tr_count = count2; |
| 31542241 | 894 | ref2->tr_tok = tok1; |
| 54341a3b | 895 | ref2->tr_count = count1; |
| 31542241 MD |
896 | if (tok1->t_ref == ref1) |
| 897 | tok1->t_ref = ref2; | |
| 898 | if (tok2->t_ref == ref2) | |
| 899 | tok2->t_ref = ref1; | |
| 900 | } | |
| 212f39f5 MD |
901 | |
| 902 | crit_exit(); | |
| d79f0a1a | 903 | } |