13f08bd8bSJohn Baldwin /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 38a36da99SPedro F. Giffuni * 43f08bd8bSJohn Baldwin * Copyright (c) 2006 John Baldwin <jhb@FreeBSD.org> 53f08bd8bSJohn Baldwin * 63f08bd8bSJohn Baldwin * Redistribution and use in source and binary forms, with or without 73f08bd8bSJohn Baldwin * modification, are permitted provided that the following conditions 83f08bd8bSJohn Baldwin * are met: 93f08bd8bSJohn Baldwin * 1. Redistributions of source code must retain the above copyright 103f08bd8bSJohn Baldwin * notice, this list of conditions and the following disclaimer. 113f08bd8bSJohn Baldwin * 2. Redistributions in binary form must reproduce the above copyright 123f08bd8bSJohn Baldwin * notice, this list of conditions and the following disclaimer in the 133f08bd8bSJohn Baldwin * documentation and/or other materials provided with the distribution. 143f08bd8bSJohn Baldwin * 153f08bd8bSJohn Baldwin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 163f08bd8bSJohn Baldwin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 173f08bd8bSJohn Baldwin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 183f08bd8bSJohn Baldwin * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 193f08bd8bSJohn Baldwin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 203f08bd8bSJohn Baldwin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 213f08bd8bSJohn Baldwin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 223f08bd8bSJohn Baldwin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 233f08bd8bSJohn Baldwin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 243f08bd8bSJohn Baldwin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 253f08bd8bSJohn Baldwin * SUCH DAMAGE. 263f08bd8bSJohn Baldwin */ 273f08bd8bSJohn Baldwin 283f08bd8bSJohn Baldwin /* 293f08bd8bSJohn Baldwin * Machine independent bits of reader/writer lock implementation. 303f08bd8bSJohn Baldwin */ 313f08bd8bSJohn Baldwin 323f08bd8bSJohn Baldwin #include <sys/cdefs.h> 333f08bd8bSJohn Baldwin #include "opt_ddb.h" 34f5f9340bSFabien Thomas #include "opt_hwpmc_hooks.h" 35cd6e6e4eSJohn Baldwin #include "opt_no_adaptive_rwlocks.h" 363f08bd8bSJohn Baldwin 373f08bd8bSJohn Baldwin #include <sys/param.h> 38cd2fe4e6SAttilio Rao #include <sys/kdb.h> 393f08bd8bSJohn Baldwin #include <sys/ktr.h> 4000ca0944SJeff Roberson #include <sys/kernel.h> 413f08bd8bSJohn Baldwin #include <sys/lock.h> 423f08bd8bSJohn Baldwin #include <sys/mutex.h> 433f08bd8bSJohn Baldwin #include <sys/proc.h> 443f08bd8bSJohn Baldwin #include <sys/rwlock.h> 452cba8dd3SJohn Baldwin #include <sys/sched.h> 461ada9041SMateusz Guzik #include <sys/smp.h> 4700ca0944SJeff Roberson #include <sys/sysctl.h> 483f08bd8bSJohn Baldwin #include <sys/systm.h> 493f08bd8bSJohn Baldwin #include <sys/turnstile.h> 506aa294beSAttilio Rao 513f08bd8bSJohn Baldwin #include <machine/cpu.h> 523f08bd8bSJohn Baldwin 53cd6e6e4eSJohn Baldwin #if defined(SMP) && !defined(NO_ADAPTIVE_RWLOCKS) 54cd6e6e4eSJohn Baldwin #define ADAPTIVE_RWLOCKS 55cd6e6e4eSJohn Baldwin #endif 56cd6e6e4eSJohn Baldwin 57f5f9340bSFabien Thomas #ifdef HWPMC_HOOKS 58f5f9340bSFabien Thomas #include <sys/pmckern.h> 59f5f9340bSFabien Thomas PMC_SOFT_DECLARE( , , lock, failed); 60f5f9340bSFabien Thomas #endif 61f5f9340bSFabien Thomas 6219d41533SAttilio Rao /* 6319d41533SAttilio Rao * Return the rwlock address when the lock cookie address is provided. 6419d41533SAttilio Rao * This functionality assumes that struct rwlock* have a member named rw_lock. 6519d41533SAttilio Rao */ 6619d41533SAttilio Rao #define rwlock2rw(c) (__containerof(c, struct rwlock, rw_lock)) 6719d41533SAttilio Rao 683f08bd8bSJohn Baldwin #ifdef DDB 693f08bd8bSJohn Baldwin #include <ddb/ddb.h> 703f08bd8bSJohn Baldwin 71d576deedSPawel Jakub Dawidek static void db_show_rwlock(const struct lock_object *lock); 723f08bd8bSJohn Baldwin #endif 73d576deedSPawel Jakub Dawidek static void assert_rw(const struct lock_object *lock, int what); 747faf4d90SDavide Italiano static void lock_rw(struct lock_object *lock, uintptr_t how); 75656991b0SGleb Smirnoff static int trylock_rw(struct lock_object *lock, uintptr_t how); 76a5aedd68SStacey Son #ifdef KDTRACE_HOOKS 77d576deedSPawel Jakub Dawidek static int owner_rw(const struct lock_object *lock, struct thread **owner); 78a5aedd68SStacey Son #endif 797faf4d90SDavide Italiano static uintptr_t unlock_rw(struct lock_object *lock); 803f08bd8bSJohn Baldwin 813f08bd8bSJohn Baldwin struct lock_class lock_class_rw = { 82ae8dde30SJohn Baldwin .lc_name = "rw", 83ae8dde30SJohn Baldwin .lc_flags = LC_SLEEPLOCK | LC_RECURSABLE | LC_UPGRADABLE, 84f9721b43SAttilio Rao .lc_assert = assert_rw, 853f08bd8bSJohn Baldwin #ifdef DDB 86ae8dde30SJohn Baldwin .lc_ddb_show = db_show_rwlock, 873f08bd8bSJohn Baldwin #endif 886e21afd4SJohn Baldwin .lc_lock = lock_rw, 89656991b0SGleb Smirnoff .lc_trylock = trylock_rw, 906e21afd4SJohn Baldwin .lc_unlock = unlock_rw, 91a5aedd68SStacey Son #ifdef KDTRACE_HOOKS 92a5aedd68SStacey Son .lc_owner = owner_rw, 93a5aedd68SStacey Son #endif 943f08bd8bSJohn Baldwin }; 953f08bd8bSJohn Baldwin 961ada9041SMateusz Guzik #ifdef ADAPTIVE_RWLOCKS 972e77cad1SMateusz Guzik #ifdef RWLOCK_CUSTOM_BACKOFF 986b8dd26eSMateusz Guzik static u_short __read_frequently rowner_retries; 996b8dd26eSMateusz Guzik static u_short __read_frequently rowner_loops; 1007029da5cSPawel Biernacki static SYSCTL_NODE(_debug, OID_AUTO, rwlock, 1017029da5cSPawel Biernacki CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, 1021ada9041SMateusz Guzik "rwlock debugging"); 1036b8dd26eSMateusz Guzik SYSCTL_U16(_debug_rwlock, OID_AUTO, retry, CTLFLAG_RW, &rowner_retries, 0, ""); 1046b8dd26eSMateusz Guzik SYSCTL_U16(_debug_rwlock, OID_AUTO, loops, CTLFLAG_RW, &rowner_loops, 0, ""); 1051ada9041SMateusz Guzik 106574adb65SMateusz Guzik static struct lock_delay_config __read_frequently rw_delay; 1071ada9041SMateusz Guzik 1086b8dd26eSMateusz Guzik SYSCTL_U16(_debug_rwlock, OID_AUTO, delay_base, CTLFLAG_RW, &rw_delay.base, 1091ada9041SMateusz Guzik 0, ""); 1106b8dd26eSMateusz Guzik SYSCTL_U16(_debug_rwlock, OID_AUTO, delay_max, CTLFLAG_RW, &rw_delay.max, 1111ada9041SMateusz Guzik 0, ""); 1121ada9041SMateusz Guzik 113e0e259a8SMateusz Guzik static void 114e0e259a8SMateusz Guzik rw_lock_delay_init(void *arg __unused) 115e0e259a8SMateusz Guzik { 116e0e259a8SMateusz Guzik 117e0e259a8SMateusz Guzik lock_delay_default_init(&rw_delay); 118e0e259a8SMateusz Guzik rowner_retries = 10; 119e0e259a8SMateusz Guzik rowner_loops = max(10000, rw_delay.max); 120e0e259a8SMateusz Guzik } 121e0e259a8SMateusz Guzik LOCK_DELAY_SYSINIT(rw_lock_delay_init); 1222e77cad1SMateusz Guzik #else 1232e77cad1SMateusz Guzik #define rw_delay locks_delay 1242e77cad1SMateusz Guzik #define rowner_retries locks_delay_retries 1252e77cad1SMateusz Guzik #define rowner_loops locks_delay_loops 1262e77cad1SMateusz Guzik #endif 1271ada9041SMateusz Guzik #endif 1281ada9041SMateusz Guzik 12938bf165fSJohn Baldwin /* 13038bf165fSJohn Baldwin * Return a pointer to the owning thread if the lock is write-locked or 13138bf165fSJohn Baldwin * NULL if the lock is unlocked or read-locked. 13238bf165fSJohn Baldwin */ 1333f0a0612SMateusz Guzik 1343f0a0612SMateusz Guzik #define lv_rw_wowner(v) \ 1353f0a0612SMateusz Guzik ((v) & RW_LOCK_READ ? NULL : \ 1363f0a0612SMateusz Guzik (struct thread *)RW_OWNER((v))) 1373f0a0612SMateusz Guzik 1383f0a0612SMateusz Guzik #define rw_wowner(rw) lv_rw_wowner(RW_READ_VALUE(rw)) 1393f08bd8bSJohn Baldwin 14038bf165fSJohn Baldwin /* 141f08945a7SAttilio Rao * Returns if a write owner is recursed. Write ownership is not assured 142f08945a7SAttilio Rao * here and should be previously checked. 143f08945a7SAttilio Rao */ 144f08945a7SAttilio Rao #define rw_recursed(rw) ((rw)->rw_recurse != 0) 145f08945a7SAttilio Rao 146f08945a7SAttilio Rao /* 147f08945a7SAttilio Rao * Return true if curthread helds the lock. 148f08945a7SAttilio Rao */ 149f08945a7SAttilio Rao #define rw_wlocked(rw) (rw_wowner((rw)) == curthread) 150f08945a7SAttilio Rao 151f08945a7SAttilio Rao /* 15238bf165fSJohn Baldwin * Return a pointer to the owning thread for this lock who should receive 15338bf165fSJohn Baldwin * any priority lent by threads that block on this lock. Currently this 15438bf165fSJohn Baldwin * is identical to rw_wowner(). 15538bf165fSJohn Baldwin */ 15638bf165fSJohn Baldwin #define rw_owner(rw) rw_wowner(rw) 15738bf165fSJohn Baldwin 1583f08bd8bSJohn Baldwin #ifndef INVARIANTS 15919d41533SAttilio Rao #define __rw_assert(c, what, file, line) 1603f08bd8bSJohn Baldwin #endif 1613f08bd8bSJohn Baldwin 162aaef18e6SGleb Smirnoff static void 163d576deedSPawel Jakub Dawidek assert_rw(const struct lock_object *lock, int what) 164f9721b43SAttilio Rao { 165f9721b43SAttilio Rao 166d576deedSPawel Jakub Dawidek rw_assert((const struct rwlock *)lock, what); 167f9721b43SAttilio Rao } 168f9721b43SAttilio Rao 169aaef18e6SGleb Smirnoff static void 1707faf4d90SDavide Italiano lock_rw(struct lock_object *lock, uintptr_t how) 1716e21afd4SJohn Baldwin { 1726e21afd4SJohn Baldwin struct rwlock *rw; 1736e21afd4SJohn Baldwin 1746e21afd4SJohn Baldwin rw = (struct rwlock *)lock; 1756e21afd4SJohn Baldwin if (how) 1766e21afd4SJohn Baldwin rw_rlock(rw); 177cf6b879fSDavide Italiano else 178cf6b879fSDavide Italiano rw_wlock(rw); 1796e21afd4SJohn Baldwin } 1806e21afd4SJohn Baldwin 181656991b0SGleb Smirnoff static int 182656991b0SGleb Smirnoff trylock_rw(struct lock_object *lock, uintptr_t how) 183656991b0SGleb Smirnoff { 184656991b0SGleb Smirnoff struct rwlock *rw; 185656991b0SGleb Smirnoff 186656991b0SGleb Smirnoff rw = (struct rwlock *)lock; 187656991b0SGleb Smirnoff if (how) 188656991b0SGleb Smirnoff return (rw_try_rlock(rw)); 189656991b0SGleb Smirnoff else 190656991b0SGleb Smirnoff return (rw_try_wlock(rw)); 191656991b0SGleb Smirnoff } 192656991b0SGleb Smirnoff 193aaef18e6SGleb Smirnoff static uintptr_t 1946e21afd4SJohn Baldwin unlock_rw(struct lock_object *lock) 1956e21afd4SJohn Baldwin { 1966e21afd4SJohn Baldwin struct rwlock *rw; 1976e21afd4SJohn Baldwin 1986e21afd4SJohn Baldwin rw = (struct rwlock *)lock; 1996e21afd4SJohn Baldwin rw_assert(rw, RA_LOCKED | LA_NOTRECURSED); 2006e21afd4SJohn Baldwin if (rw->rw_lock & RW_LOCK_READ) { 2016e21afd4SJohn Baldwin rw_runlock(rw); 202cf6b879fSDavide Italiano return (1); 2036e21afd4SJohn Baldwin } else { 2046e21afd4SJohn Baldwin rw_wunlock(rw); 205cf6b879fSDavide Italiano return (0); 2066e21afd4SJohn Baldwin } 2076e21afd4SJohn Baldwin } 2086e21afd4SJohn Baldwin 209a5aedd68SStacey Son #ifdef KDTRACE_HOOKS 210aaef18e6SGleb Smirnoff static int 211d576deedSPawel Jakub Dawidek owner_rw(const struct lock_object *lock, struct thread **owner) 212a5aedd68SStacey Son { 213d576deedSPawel Jakub Dawidek const struct rwlock *rw = (const struct rwlock *)lock; 214a5aedd68SStacey Son uintptr_t x = rw->rw_lock; 215a5aedd68SStacey Son 216a5aedd68SStacey Son *owner = rw_wowner(rw); 217a5aedd68SStacey Son return ((x & RW_LOCK_READ) != 0 ? (RW_READERS(x) != 0) : 218a5aedd68SStacey Son (*owner != NULL)); 219a5aedd68SStacey Son } 220a5aedd68SStacey Son #endif 221a5aedd68SStacey Son 2226e21afd4SJohn Baldwin void 22319d41533SAttilio Rao _rw_init_flags(volatile uintptr_t *c, const char *name, int opts) 2243f08bd8bSJohn Baldwin { 22519d41533SAttilio Rao struct rwlock *rw; 226f08945a7SAttilio Rao int flags; 227f08945a7SAttilio Rao 22819d41533SAttilio Rao rw = rwlock2rw(c); 22919d41533SAttilio Rao 230f08945a7SAttilio Rao MPASS((opts & ~(RW_DUPOK | RW_NOPROFILE | RW_NOWITNESS | RW_QUIET | 231fd07ddcfSDmitry Chagin RW_RECURSE | RW_NEW)) == 0); 232353998acSAttilio Rao ASSERT_ATOMIC_LOAD_PTR(rw->rw_lock, 233353998acSAttilio Rao ("%s: rw_lock not aligned for %s: %p", __func__, name, 234353998acSAttilio Rao &rw->rw_lock)); 235f08945a7SAttilio Rao 236f0830182SAttilio Rao flags = LO_UPGRADABLE; 237f08945a7SAttilio Rao if (opts & RW_DUPOK) 238f08945a7SAttilio Rao flags |= LO_DUPOK; 239f08945a7SAttilio Rao if (opts & RW_NOPROFILE) 240f08945a7SAttilio Rao flags |= LO_NOPROFILE; 241f08945a7SAttilio Rao if (!(opts & RW_NOWITNESS)) 242f08945a7SAttilio Rao flags |= LO_WITNESS; 243f0830182SAttilio Rao if (opts & RW_RECURSE) 244f0830182SAttilio Rao flags |= LO_RECURSABLE; 245f08945a7SAttilio Rao if (opts & RW_QUIET) 246f08945a7SAttilio Rao flags |= LO_QUIET; 247fd07ddcfSDmitry Chagin if (opts & RW_NEW) 248fd07ddcfSDmitry Chagin flags |= LO_NEW; 2493f08bd8bSJohn Baldwin 250b5fb43e5SJohn Baldwin lock_init(&rw->lock_object, &lock_class_rw, name, NULL, flags); 2513f08bd8bSJohn Baldwin rw->rw_lock = RW_UNLOCKED; 252f08945a7SAttilio Rao rw->rw_recurse = 0; 2533f08bd8bSJohn Baldwin } 2543f08bd8bSJohn Baldwin 2553f08bd8bSJohn Baldwin void 25619d41533SAttilio Rao _rw_destroy(volatile uintptr_t *c) 2573f08bd8bSJohn Baldwin { 25819d41533SAttilio Rao struct rwlock *rw; 25919d41533SAttilio Rao 26019d41533SAttilio Rao rw = rwlock2rw(c); 2613f08bd8bSJohn Baldwin 2622430ab46SBjoern A. Zeeb KASSERT(rw->rw_lock == RW_UNLOCKED, ("rw lock %p not unlocked", rw)); 2632430ab46SBjoern A. Zeeb KASSERT(rw->rw_recurse == 0, ("rw lock %p still recursed", rw)); 2640026c92cSJohn Baldwin rw->rw_lock = RW_DESTROYED; 265aa89d8cdSJohn Baldwin lock_destroy(&rw->lock_object); 2663f08bd8bSJohn Baldwin } 2673f08bd8bSJohn Baldwin 2683f08bd8bSJohn Baldwin void 2693f08bd8bSJohn Baldwin rw_sysinit(void *arg) 2703f08bd8bSJohn Baldwin { 271755230ebSMark Johnston struct rw_args *args; 2723f08bd8bSJohn Baldwin 273755230ebSMark Johnston args = arg; 27419d41533SAttilio Rao rw_init_flags((struct rwlock *)args->ra_rw, args->ra_desc, 27519d41533SAttilio Rao args->ra_flags); 276e1d881baSKip Macy } 277e1d881baSKip Macy 2788525230aSRobert Watson int 27919d41533SAttilio Rao _rw_wowned(const volatile uintptr_t *c) 2808525230aSRobert Watson { 2818525230aSRobert Watson 28219d41533SAttilio Rao return (rw_wowner(rwlock2rw(c)) == curthread); 2838525230aSRobert Watson } 2848525230aSRobert Watson 2853f08bd8bSJohn Baldwin void 28619d41533SAttilio Rao _rw_wlock_cookie(volatile uintptr_t *c, const char *file, int line) 2873f08bd8bSJohn Baldwin { 28819d41533SAttilio Rao struct rwlock *rw; 289993ddec4SMateusz Guzik uintptr_t tid, v; 2903f08bd8bSJohn Baldwin 29119d41533SAttilio Rao rw = rwlock2rw(c); 29219d41533SAttilio Rao 293704cb42fSMark Johnston KASSERT(kdb_active != 0 || SCHEDULER_STOPPED() || 294704cb42fSMark Johnston !TD_IS_IDLETHREAD(curthread), 295e3ae0dfeSAttilio Rao ("rw_wlock() by idle thread %p on rwlock %s @ %s:%d", 296e3ae0dfeSAttilio Rao curthread, rw->lock_object.lo_name, file, line)); 2970026c92cSJohn Baldwin KASSERT(rw->rw_lock != RW_DESTROYED, 2980026c92cSJohn Baldwin ("rw_wlock() of destroyed rwlock @ %s:%d", file, line)); 299aa89d8cdSJohn Baldwin WITNESS_CHECKORDER(&rw->lock_object, LOP_NEWORDER | LOP_EXCLUSIVE, file, 30041313430SJohn Baldwin line, NULL); 301993ddec4SMateusz Guzik tid = (uintptr_t)curthread; 302993ddec4SMateusz Guzik v = RW_UNLOCKED; 303993ddec4SMateusz Guzik if (!_rw_write_lock_fetch(rw, &v, tid)) 304013c0b49SMateusz Guzik _rw_wlock_hard(rw, v, file, line); 305993ddec4SMateusz Guzik else 306993ddec4SMateusz Guzik LOCKSTAT_PROFILE_OBTAIN_RWLOCK_SUCCESS(rw__acquire, rw, 307993ddec4SMateusz Guzik 0, 0, file, line, LOCKSTAT_WRITER); 308993ddec4SMateusz Guzik 309f08945a7SAttilio Rao LOCK_LOG_LOCK("WLOCK", &rw->lock_object, 0, rw->rw_recurse, file, line); 310aa89d8cdSJohn Baldwin WITNESS_LOCK(&rw->lock_object, LOP_EXCLUSIVE, file, line); 311ce1c953eSMark Johnston TD_LOCKS_INC(curthread); 3123f08bd8bSJohn Baldwin } 3133f08bd8bSJohn Baldwin 314b31a149bSAttilio Rao int 315c1e1a7ecSMateusz Guzik __rw_try_wlock_int(struct rwlock *rw LOCK_FILE_LINE_ARG_DEF) 316b31a149bSAttilio Rao { 3175c5df0d9SMateusz Guzik struct thread *td; 3185c5df0d9SMateusz Guzik uintptr_t tid, v; 319b31a149bSAttilio Rao int rval; 3205c5df0d9SMateusz Guzik bool recursed; 321b31a149bSAttilio Rao 3225c5df0d9SMateusz Guzik td = curthread; 3235c5df0d9SMateusz Guzik tid = (uintptr_t)td; 3246b353101SOlivier Certner if (SCHEDULER_STOPPED()) 32535370593SAndriy Gapon return (1); 32635370593SAndriy Gapon 327704cb42fSMark Johnston KASSERT(kdb_active != 0 || !TD_IS_IDLETHREAD(td), 328e3ae0dfeSAttilio Rao ("rw_try_wlock() by idle thread %p on rwlock %s @ %s:%d", 329e3ae0dfeSAttilio Rao curthread, rw->lock_object.lo_name, file, line)); 330b31a149bSAttilio Rao KASSERT(rw->rw_lock != RW_DESTROYED, 331b31a149bSAttilio Rao ("rw_try_wlock() of destroyed rwlock @ %s:%d", file, line)); 332b31a149bSAttilio Rao 3335c5df0d9SMateusz Guzik rval = 1; 3345c5df0d9SMateusz Guzik recursed = false; 3355c5df0d9SMateusz Guzik v = RW_UNLOCKED; 336b247fd39SMateusz Guzik for (;;) { 337b247fd39SMateusz Guzik if (atomic_fcmpset_acq_ptr(&rw->rw_lock, &v, tid)) 338b247fd39SMateusz Guzik break; 339b247fd39SMateusz Guzik if (v == RW_UNLOCKED) 340b247fd39SMateusz Guzik continue; 3415c5df0d9SMateusz Guzik if (v == tid && (rw->lock_object.lo_flags & LO_RECURSABLE)) { 342b31a149bSAttilio Rao rw->rw_recurse++; 343dbccc810SMateusz Guzik atomic_set_ptr(&rw->rw_lock, RW_LOCK_WRITER_RECURSED); 344b247fd39SMateusz Guzik break; 3455c5df0d9SMateusz Guzik } 346b247fd39SMateusz Guzik rval = 0; 347b247fd39SMateusz Guzik break; 3485c5df0d9SMateusz Guzik } 349b31a149bSAttilio Rao 350b31a149bSAttilio Rao LOCK_LOG_TRY("WLOCK", &rw->lock_object, 0, rval, file, line); 351b31a149bSAttilio Rao if (rval) { 352b31a149bSAttilio Rao WITNESS_LOCK(&rw->lock_object, LOP_EXCLUSIVE | LOP_TRYLOCK, 353b31a149bSAttilio Rao file, line); 3545c5df0d9SMateusz Guzik if (!recursed) 355de2c95ccSMark Johnston LOCKSTAT_PROFILE_OBTAIN_RWLOCK_SUCCESS(rw__acquire, 356de2c95ccSMark Johnston rw, 0, 0, file, line, LOCKSTAT_WRITER); 357ce1c953eSMark Johnston TD_LOCKS_INC(curthread); 358b31a149bSAttilio Rao } 359b31a149bSAttilio Rao return (rval); 360b31a149bSAttilio Rao } 361b31a149bSAttilio Rao 362c1e1a7ecSMateusz Guzik int 363c1e1a7ecSMateusz Guzik __rw_try_wlock(volatile uintptr_t *c, const char *file, int line) 364c1e1a7ecSMateusz Guzik { 365c1e1a7ecSMateusz Guzik struct rwlock *rw; 366c1e1a7ecSMateusz Guzik 367c1e1a7ecSMateusz Guzik rw = rwlock2rw(c); 3685ba6facfSMateusz Guzik return (__rw_try_wlock_int(rw LOCK_FILE_LINE_ARG)); 369c1e1a7ecSMateusz Guzik } 370c1e1a7ecSMateusz Guzik 3713f08bd8bSJohn Baldwin void 37219d41533SAttilio Rao _rw_wunlock_cookie(volatile uintptr_t *c, const char *file, int line) 3733f08bd8bSJohn Baldwin { 37419d41533SAttilio Rao struct rwlock *rw; 3753f08bd8bSJohn Baldwin 37619d41533SAttilio Rao rw = rwlock2rw(c); 37719d41533SAttilio Rao 3780026c92cSJohn Baldwin KASSERT(rw->rw_lock != RW_DESTROYED, 3790026c92cSJohn Baldwin ("rw_wunlock() of destroyed rwlock @ %s:%d", file, line)); 38019d41533SAttilio Rao __rw_assert(c, RA_WLOCKED, file, line); 381aa89d8cdSJohn Baldwin WITNESS_UNLOCK(&rw->lock_object, LOP_EXCLUSIVE, file, line); 382f08945a7SAttilio Rao LOCK_LOG_LOCK("WUNLOCK", &rw->lock_object, 0, rw->rw_recurse, file, 383f08945a7SAttilio Rao line); 384dbccc810SMateusz Guzik 385ffd5c94cSMateusz Guzik #ifdef LOCK_PROFILING 386993ddec4SMateusz Guzik _rw_wunlock_hard(rw, (uintptr_t)curthread, file, line); 387ffd5c94cSMateusz Guzik #else 388ffd5c94cSMateusz Guzik __rw_wunlock(rw, curthread, file, line); 389ffd5c94cSMateusz Guzik #endif 390993ddec4SMateusz Guzik 391ce1c953eSMark Johnston TD_LOCKS_DEC(curthread); 3923f08bd8bSJohn Baldwin } 393ce1c953eSMark Johnston 3945dff04c3SJeff Roberson /* 3955dff04c3SJeff Roberson * Determines whether a new reader can acquire a lock. Succeeds if the 3965dff04c3SJeff Roberson * reader already owns a read lock and the lock is locked for read to 3975dff04c3SJeff Roberson * prevent deadlock from reader recursion. Also succeeds if the lock 3985dff04c3SJeff Roberson * is unlocked and has no writer waiters or spinners. Failing otherwise 3995dff04c3SJeff Roberson * prioritizes writers before readers. 4005dff04c3SJeff Roberson */ 4013c84b4b3SRyan Libby static __always_inline bool 40270502e39SMateusz Guzik __rw_can_read(struct thread *td, uintptr_t v, bool fp) 40370502e39SMateusz Guzik { 40470502e39SMateusz Guzik 40570502e39SMateusz Guzik if ((v & (RW_LOCK_READ | RW_LOCK_WRITE_WAITERS | RW_LOCK_WRITE_SPINNER)) 40670502e39SMateusz Guzik == RW_LOCK_READ) 40770502e39SMateusz Guzik return (true); 40870502e39SMateusz Guzik if (!fp && td->td_rw_rlocks && (v & RW_LOCK_READ)) 40970502e39SMateusz Guzik return (true); 41070502e39SMateusz Guzik return (false); 41170502e39SMateusz Guzik } 4123f08bd8bSJohn Baldwin 4133c84b4b3SRyan Libby static __always_inline bool 41470502e39SMateusz Guzik __rw_rlock_try(struct rwlock *rw, struct thread *td, uintptr_t *vp, bool fp 415013c0b49SMateusz Guzik LOCK_FILE_LINE_ARG_DEF) 416b0a61642SMateusz Guzik { 417b0a61642SMateusz Guzik 418b0a61642SMateusz Guzik /* 419b0a61642SMateusz Guzik * Handle the easy case. If no other thread has a write 420b0a61642SMateusz Guzik * lock, then try to bump up the count of read locks. Note 421b0a61642SMateusz Guzik * that we have to preserve the current state of the 422b0a61642SMateusz Guzik * RW_LOCK_WRITE_WAITERS flag. If we fail to acquire a 423b0a61642SMateusz Guzik * read lock, then rw_lock must have changed, so restart 424b0a61642SMateusz Guzik * the loop. Note that this handles the case of a 425b0a61642SMateusz Guzik * completely unlocked rwlock since such a lock is encoded 426b0a61642SMateusz Guzik * as a read lock with no waiters. 427b0a61642SMateusz Guzik */ 42870502e39SMateusz Guzik while (__rw_can_read(td, *vp, fp)) { 429b0a61642SMateusz Guzik if (atomic_fcmpset_acq_ptr(&rw->rw_lock, vp, 430b0a61642SMateusz Guzik *vp + RW_ONE_READER)) { 431b0a61642SMateusz Guzik if (LOCK_LOG_TEST(&rw->lock_object, 0)) 432b0a61642SMateusz Guzik CTR4(KTR_LOCK, 433b0a61642SMateusz Guzik "%s: %p succeed %p -> %p", __func__, 434b0a61642SMateusz Guzik rw, (void *)*vp, 435b0a61642SMateusz Guzik (void *)(*vp + RW_ONE_READER)); 436b0a61642SMateusz Guzik td->td_rw_rlocks++; 437b0a61642SMateusz Guzik return (true); 438b0a61642SMateusz Guzik } 439b0a61642SMateusz Guzik } 440b0a61642SMateusz Guzik return (false); 441b0a61642SMateusz Guzik } 442b0a61642SMateusz Guzik 443b0a61642SMateusz Guzik static void __noinline 444013c0b49SMateusz Guzik __rw_rlock_hard(struct rwlock *rw, struct thread *td, uintptr_t v 445013c0b49SMateusz Guzik LOCK_FILE_LINE_ARG_DEF) 4463f08bd8bSJohn Baldwin { 4472502c107SJeff Roberson struct turnstile *ts; 4482e106e04SMateusz Guzik struct thread *owner; 449cd6e6e4eSJohn Baldwin #ifdef ADAPTIVE_RWLOCKS 45000ca0944SJeff Roberson int spintries = 0; 451d07e22cdSMateusz Guzik int i, n; 4525884c1a0SWojciech A. Koszek #endif 4531723a064SJeff Roberson #ifdef LOCK_PROFILING 454ddb38a1fSJohn Baldwin uint64_t waittime = 0; 455f183910bSKip Macy int contested = 0; 4561723a064SJeff Roberson #endif 45704126895SMateusz Guzik #if defined(ADAPTIVE_RWLOCKS) || defined(KDTRACE_HOOKS) 4581ada9041SMateusz Guzik struct lock_delay_arg lda; 4591ada9041SMateusz Guzik #endif 460a5aedd68SStacey Son #ifdef KDTRACE_HOOKS 46161852185SMateusz Guzik u_int sleep_cnt = 0; 462a5aedd68SStacey Son int64_t sleep_time = 0; 463076dd8ebSAndriy Gapon int64_t all_time = 0; 464*928864a9SKristof Provost uintptr_t state = 0; 465a5aedd68SStacey Son #endif 4663af30059SMateusz Guzik #if defined(KDTRACE_HOOKS) || defined(LOCK_PROFILING) 46709bdec20SMateusz Guzik int doing_lockprof = 0; 46809bdec20SMateusz Guzik #endif 46909bdec20SMateusz Guzik 47009bdec20SMateusz Guzik #ifdef KDTRACE_HOOKS 47109bdec20SMateusz Guzik if (LOCKSTAT_PROFILE_ENABLED(rw__acquire)) { 47209bdec20SMateusz Guzik if (__rw_rlock_try(rw, td, &v, false LOCK_FILE_LINE_ARG)) 47309bdec20SMateusz Guzik goto out_lockstat; 47409bdec20SMateusz Guzik doing_lockprof = 1; 47509bdec20SMateusz Guzik all_time -= lockstat_nsecs(&rw->lock_object); 47609bdec20SMateusz Guzik } 477*928864a9SKristof Provost state = v; 47809bdec20SMateusz Guzik #endif 47909bdec20SMateusz Guzik #ifdef LOCK_PROFILING 48009bdec20SMateusz Guzik doing_lockprof = 1; 4813af30059SMateusz Guzik #endif 4823f08bd8bSJohn Baldwin 48335370593SAndriy Gapon if (SCHEDULER_STOPPED()) 48435370593SAndriy Gapon return; 48535370593SAndriy Gapon 486fa5000a4SMateusz Guzik #if defined(ADAPTIVE_RWLOCKS) 4871ada9041SMateusz Guzik lock_delay_arg_init(&lda, &rw_delay); 488fa5000a4SMateusz Guzik #elif defined(KDTRACE_HOOKS) 489c795344fSMateusz Guzik lock_delay_arg_init_noadapt(&lda); 4901ada9041SMateusz Guzik #endif 49119d41533SAttilio Rao 492ae7d25a4SMateusz Guzik #ifdef HWPMC_HOOKS 493ae7d25a4SMateusz Guzik PMC_SOFT_CALL( , , lock, failed); 494ae7d25a4SMateusz Guzik #endif 4956a467cc5SMateusz Guzik lock_profile_obtain_lock_failed(&rw->lock_object, false, 496ae7d25a4SMateusz Guzik &contested, &waittime); 497ae7d25a4SMateusz Guzik 4987530de77SMateusz Guzik THREAD_CONTENDS_ON_LOCK(&rw->lock_object); 4997530de77SMateusz Guzik 5003f08bd8bSJohn Baldwin for (;;) { 50170502e39SMateusz Guzik if (__rw_rlock_try(rw, td, &v, false LOCK_FILE_LINE_ARG)) 5023f08bd8bSJohn Baldwin break; 50397cc6870SMark Johnston #ifdef KDTRACE_HOOKS 5041ada9041SMateusz Guzik lda.spin_cnt++; 50597cc6870SMark Johnston #endif 5063f08bd8bSJohn Baldwin 50749aead8aSAttilio Rao #ifdef ADAPTIVE_RWLOCKS 50849aead8aSAttilio Rao /* 50949aead8aSAttilio Rao * If the owner is running on another CPU, spin until 51049aead8aSAttilio Rao * the owner stops running or the state of the lock 51149aead8aSAttilio Rao * changes. 51249aead8aSAttilio Rao */ 5135dff04c3SJeff Roberson if ((v & RW_LOCK_READ) == 0) { 5145dff04c3SJeff Roberson owner = (struct thread *)RW_OWNER(v); 51549aead8aSAttilio Rao if (TD_IS_RUNNING(owner)) { 51649aead8aSAttilio Rao if (LOCK_LOG_TEST(&rw->lock_object, 0)) 5175dff04c3SJeff Roberson CTR3(KTR_LOCK, 5185dff04c3SJeff Roberson "%s: spinning on %p held by %p", 51949aead8aSAttilio Rao __func__, rw, owner); 5202cba8dd3SJohn Baldwin KTR_STATE1(KTR_SCHED, "thread", 5212cba8dd3SJohn Baldwin sched_tdname(curthread), "spinning", 5222cba8dd3SJohn Baldwin "lockname:\"%s\"", rw->lock_object.lo_name); 5233f0a0612SMateusz Guzik do { 5241ada9041SMateusz Guzik lock_delay(&lda); 5253f0a0612SMateusz Guzik v = RW_READ_VALUE(rw); 5263f0a0612SMateusz Guzik owner = lv_rw_wowner(v); 5273f0a0612SMateusz Guzik } while (owner != NULL && TD_IS_RUNNING(owner)); 5282cba8dd3SJohn Baldwin KTR_STATE0(KTR_SCHED, "thread", 5292cba8dd3SJohn Baldwin sched_tdname(curthread), "running"); 53049aead8aSAttilio Rao continue; 53149aead8aSAttilio Rao } 5329feec7efSMateusz Guzik } else { 5339feec7efSMateusz Guzik if ((v & RW_LOCK_WRITE_SPINNER) && RW_READERS(v) == 0) { 5349feec7efSMateusz Guzik MPASS(!__rw_can_read(td, v, false)); 5359feec7efSMateusz Guzik lock_delay_spin(2); 5369feec7efSMateusz Guzik v = RW_READ_VALUE(rw); 5379feec7efSMateusz Guzik continue; 5389feec7efSMateusz Guzik } 5399feec7efSMateusz Guzik if (spintries < rowner_retries) { 54000ca0944SJeff Roberson spintries++; 5412cba8dd3SJohn Baldwin KTR_STATE1(KTR_SCHED, "thread", sched_tdname(curthread), 5422cba8dd3SJohn Baldwin "spinning", "lockname:\"%s\"", 5432cba8dd3SJohn Baldwin rw->lock_object.lo_name); 544d07e22cdSMateusz Guzik n = RW_READERS(v); 5459feec7efSMateusz Guzik for (i = 0; i < rowner_loops; i += n) { 546d07e22cdSMateusz Guzik lock_delay_spin(n); 5473f0a0612SMateusz Guzik v = RW_READ_VALUE(rw); 5489feec7efSMateusz Guzik if (!(v & RW_LOCK_READ)) 5499feec7efSMateusz Guzik break; 5509feec7efSMateusz Guzik n = RW_READERS(v); 5519feec7efSMateusz Guzik if (n == 0) 5529feec7efSMateusz Guzik break; 5539feec7efSMateusz Guzik if (__rw_can_read(td, v, false)) 55400ca0944SJeff Roberson break; 55500ca0944SJeff Roberson } 556e7a9eed7SAttilio Rao #ifdef KDTRACE_HOOKS 5571ada9041SMateusz Guzik lda.spin_cnt += rowner_loops - i; 558e7a9eed7SAttilio Rao #endif 5592cba8dd3SJohn Baldwin KTR_STATE0(KTR_SCHED, "thread", sched_tdname(curthread), 5602cba8dd3SJohn Baldwin "running"); 561efa9f177SMateusz Guzik if (i < rowner_loops) 56200ca0944SJeff Roberson continue; 5635dff04c3SJeff Roberson } 5649feec7efSMateusz Guzik } 56549aead8aSAttilio Rao #endif 56649aead8aSAttilio Rao 5673f08bd8bSJohn Baldwin /* 5683f08bd8bSJohn Baldwin * Okay, now it's the hard case. Some other thread already 5695dff04c3SJeff Roberson * has a write lock or there are write waiters present, 5705dff04c3SJeff Roberson * acquire the turnstile lock so we can begin the process 5715dff04c3SJeff Roberson * of blocking. 5723f08bd8bSJohn Baldwin */ 5732502c107SJeff Roberson ts = turnstile_trywait(&rw->lock_object); 5743f08bd8bSJohn Baldwin 5753f08bd8bSJohn Baldwin /* 5763f08bd8bSJohn Baldwin * The lock might have been released while we spun, so 5775dff04c3SJeff Roberson * recheck its state and restart the loop if needed. 5783f08bd8bSJohn Baldwin */ 5793f0a0612SMateusz Guzik v = RW_READ_VALUE(rw); 58093118b62SMateusz Guzik retry_ts: 5819feec7efSMateusz Guzik if (((v & RW_LOCK_WRITE_SPINNER) && RW_READERS(v) == 0) || 5829feec7efSMateusz Guzik __rw_can_read(td, v, false)) { 5832502c107SJeff Roberson turnstile_cancel(ts); 5843f08bd8bSJohn Baldwin continue; 5853f08bd8bSJohn Baldwin } 5863f08bd8bSJohn Baldwin 5872e106e04SMateusz Guzik owner = lv_rw_wowner(v); 5882e106e04SMateusz Guzik 58949aead8aSAttilio Rao #ifdef ADAPTIVE_RWLOCKS 59049aead8aSAttilio Rao /* 591fa29f023SJohn Baldwin * The current lock owner might have started executing 592fa29f023SJohn Baldwin * on another CPU (or the lock could have changed 593fa29f023SJohn Baldwin * owners) while we were waiting on the turnstile 594fa29f023SJohn Baldwin * chain lock. If so, drop the turnstile lock and try 595fa29f023SJohn Baldwin * again. 59649aead8aSAttilio Rao */ 5972e106e04SMateusz Guzik if (owner != NULL) { 59849aead8aSAttilio Rao if (TD_IS_RUNNING(owner)) { 59949aead8aSAttilio Rao turnstile_cancel(ts); 60049aead8aSAttilio Rao continue; 60149aead8aSAttilio Rao } 6025dff04c3SJeff Roberson } 60349aead8aSAttilio Rao #endif 60449aead8aSAttilio Rao 6053f08bd8bSJohn Baldwin /* 6065dff04c3SJeff Roberson * The lock is held in write mode or it already has waiters. 6073f08bd8bSJohn Baldwin */ 60862b0676cSMateusz Guzik MPASS(!__rw_can_read(td, v, false)); 6095dff04c3SJeff Roberson 6105dff04c3SJeff Roberson /* 6115dff04c3SJeff Roberson * If the RW_LOCK_READ_WAITERS flag is already set, then 6125dff04c3SJeff Roberson * we can go ahead and block. If it is not set then try 6135dff04c3SJeff Roberson * to set it. If we fail to set it drop the turnstile 6145dff04c3SJeff Roberson * lock and restart the loop. 6155dff04c3SJeff Roberson */ 6165dff04c3SJeff Roberson if (!(v & RW_LOCK_READ_WAITERS)) { 61793118b62SMateusz Guzik if (!atomic_fcmpset_ptr(&rw->rw_lock, &v, 61893118b62SMateusz Guzik v | RW_LOCK_READ_WAITERS)) 61993118b62SMateusz Guzik goto retry_ts; 620aa89d8cdSJohn Baldwin if (LOCK_LOG_TEST(&rw->lock_object, 0)) 62138bf165fSJohn Baldwin CTR2(KTR_LOCK, "%s: %p set read waiters flag", 62238bf165fSJohn Baldwin __func__, rw); 62338bf165fSJohn Baldwin } 6243f08bd8bSJohn Baldwin 6253f08bd8bSJohn Baldwin /* 6263f08bd8bSJohn Baldwin * We were unable to acquire the lock and the read waiters 6273f08bd8bSJohn Baldwin * flag is set, so we must block on the turnstile. 6283f08bd8bSJohn Baldwin */ 629aa89d8cdSJohn Baldwin if (LOCK_LOG_TEST(&rw->lock_object, 0)) 6303f08bd8bSJohn Baldwin CTR2(KTR_LOCK, "%s: %p blocking on turnstile", __func__, 6313f08bd8bSJohn Baldwin rw); 632a5aedd68SStacey Son #ifdef KDTRACE_HOOKS 633e2b25737SMark Johnston sleep_time -= lockstat_nsecs(&rw->lock_object); 634a5aedd68SStacey Son #endif 6352e106e04SMateusz Guzik MPASS(owner == rw_owner(rw)); 6362e106e04SMateusz Guzik turnstile_wait(ts, owner, TS_SHARED_QUEUE); 637a5aedd68SStacey Son #ifdef KDTRACE_HOOKS 638e2b25737SMark Johnston sleep_time += lockstat_nsecs(&rw->lock_object); 639a5aedd68SStacey Son sleep_cnt++; 640a5aedd68SStacey Son #endif 641aa89d8cdSJohn Baldwin if (LOCK_LOG_TEST(&rw->lock_object, 0)) 6423f08bd8bSJohn Baldwin CTR2(KTR_LOCK, "%s: %p resuming from turnstile", 6433f08bd8bSJohn Baldwin __func__, rw); 6443f0a0612SMateusz Guzik v = RW_READ_VALUE(rw); 6453f08bd8bSJohn Baldwin } 6467530de77SMateusz Guzik THREAD_CONTENTION_DONE(&rw->lock_object); 6473af30059SMateusz Guzik #if defined(KDTRACE_HOOKS) || defined(LOCK_PROFILING) 6483af30059SMateusz Guzik if (__predict_true(!doing_lockprof)) 6493af30059SMateusz Guzik return; 6503af30059SMateusz Guzik #endif 651076dd8ebSAndriy Gapon #ifdef KDTRACE_HOOKS 652e2b25737SMark Johnston all_time += lockstat_nsecs(&rw->lock_object); 653076dd8ebSAndriy Gapon if (sleep_time) 65432cd0147SMark Johnston LOCKSTAT_RECORD4(rw__block, rw, sleep_time, 655076dd8ebSAndriy Gapon LOCKSTAT_READER, (state & RW_LOCK_READ) == 0, 656076dd8ebSAndriy Gapon (state & RW_LOCK_READ) == 0 ? 0 : RW_READERS(state)); 6573f08bd8bSJohn Baldwin 658076dd8ebSAndriy Gapon /* Record only the loops spinning and not sleeping. */ 6591ada9041SMateusz Guzik if (lda.spin_cnt > sleep_cnt) 66032cd0147SMark Johnston LOCKSTAT_RECORD4(rw__spin, rw, all_time - sleep_time, 661076dd8ebSAndriy Gapon LOCKSTAT_READER, (state & RW_LOCK_READ) == 0, 662076dd8ebSAndriy Gapon (state & RW_LOCK_READ) == 0 ? 0 : RW_READERS(state)); 66309bdec20SMateusz Guzik out_lockstat: 664076dd8ebSAndriy Gapon #endif 6653f08bd8bSJohn Baldwin /* 6663f08bd8bSJohn Baldwin * TODO: acquire "owner of record" here. Here be turnstile dragons 6673f08bd8bSJohn Baldwin * however. turnstiles don't like owners changing between calls to 6683f08bd8bSJohn Baldwin * turnstile_wait() currently. 6693f08bd8bSJohn Baldwin */ 670de2c95ccSMark Johnston LOCKSTAT_PROFILE_OBTAIN_RWLOCK_SUCCESS(rw__acquire, rw, contested, 671de2c95ccSMark Johnston waittime, file, line, LOCKSTAT_READER); 672b0a61642SMateusz Guzik } 673b0a61642SMateusz Guzik 674b0a61642SMateusz Guzik void 675013c0b49SMateusz Guzik __rw_rlock_int(struct rwlock *rw LOCK_FILE_LINE_ARG_DEF) 676b0a61642SMateusz Guzik { 677b0a61642SMateusz Guzik struct thread *td; 678b0a61642SMateusz Guzik uintptr_t v; 679b0a61642SMateusz Guzik 680b0a61642SMateusz Guzik td = curthread; 681b0a61642SMateusz Guzik 6826b353101SOlivier Certner KASSERT(kdb_active != 0 || SCHEDULER_STOPPED() || 683704cb42fSMark Johnston !TD_IS_IDLETHREAD(td), 684b0a61642SMateusz Guzik ("rw_rlock() by idle thread %p on rwlock %s @ %s:%d", 685b0a61642SMateusz Guzik td, rw->lock_object.lo_name, file, line)); 686b0a61642SMateusz Guzik KASSERT(rw->rw_lock != RW_DESTROYED, 687b0a61642SMateusz Guzik ("rw_rlock() of destroyed rwlock @ %s:%d", file, line)); 688b0a61642SMateusz Guzik KASSERT(rw_wowner(rw) != td, 689b0a61642SMateusz Guzik ("rw_rlock: wlock already held for %s @ %s:%d", 690b0a61642SMateusz Guzik rw->lock_object.lo_name, file, line)); 691b0a61642SMateusz Guzik WITNESS_CHECKORDER(&rw->lock_object, LOP_NEWORDER, file, line, NULL); 692b0a61642SMateusz Guzik 693b0a61642SMateusz Guzik v = RW_READ_VALUE(rw); 694e4ccf57fSMateusz Guzik if (__predict_false(LOCKSTAT_PROFILE_ENABLED(rw__acquire) || 69570502e39SMateusz Guzik !__rw_rlock_try(rw, td, &v, true LOCK_FILE_LINE_ARG))) 696013c0b49SMateusz Guzik __rw_rlock_hard(rw, td, v LOCK_FILE_LINE_ARG); 697e4ccf57fSMateusz Guzik else 6986a467cc5SMateusz Guzik lock_profile_obtain_lock_success(&rw->lock_object, false, 0, 0, 699e4ccf57fSMateusz Guzik file, line); 700b0a61642SMateusz Guzik 701aa89d8cdSJohn Baldwin LOCK_LOG_LOCK("RLOCK", &rw->lock_object, 0, 0, file, line); 702aa89d8cdSJohn Baldwin WITNESS_LOCK(&rw->lock_object, 0, file, line); 703ce1c953eSMark Johnston TD_LOCKS_INC(curthread); 7043f08bd8bSJohn Baldwin } 7053f08bd8bSJohn Baldwin 706013c0b49SMateusz Guzik void 707013c0b49SMateusz Guzik __rw_rlock(volatile uintptr_t *c, const char *file, int line) 708013c0b49SMateusz Guzik { 709013c0b49SMateusz Guzik struct rwlock *rw; 710013c0b49SMateusz Guzik 711013c0b49SMateusz Guzik rw = rwlock2rw(c); 712013c0b49SMateusz Guzik __rw_rlock_int(rw LOCK_FILE_LINE_ARG); 713013c0b49SMateusz Guzik } 714013c0b49SMateusz Guzik 715b31a149bSAttilio Rao int 716c1e1a7ecSMateusz Guzik __rw_try_rlock_int(struct rwlock *rw LOCK_FILE_LINE_ARG_DEF) 717b31a149bSAttilio Rao { 718b31a149bSAttilio Rao uintptr_t x; 719b31a149bSAttilio Rao 72035370593SAndriy Gapon if (SCHEDULER_STOPPED()) 72135370593SAndriy Gapon return (1); 72235370593SAndriy Gapon 723cd2fe4e6SAttilio Rao KASSERT(kdb_active != 0 || !TD_IS_IDLETHREAD(curthread), 724e3ae0dfeSAttilio Rao ("rw_try_rlock() by idle thread %p on rwlock %s @ %s:%d", 725e3ae0dfeSAttilio Rao curthread, rw->lock_object.lo_name, file, line)); 726e3ae0dfeSAttilio Rao 727b31a149bSAttilio Rao x = rw->rw_lock; 7285c5df0d9SMateusz Guzik for (;;) { 729b31a149bSAttilio Rao KASSERT(rw->rw_lock != RW_DESTROYED, 730b31a149bSAttilio Rao ("rw_try_rlock() of destroyed rwlock @ %s:%d", file, line)); 731b31a149bSAttilio Rao if (!(x & RW_LOCK_READ)) 732b31a149bSAttilio Rao break; 7335c5df0d9SMateusz Guzik if (atomic_fcmpset_acq_ptr(&rw->rw_lock, &x, x + RW_ONE_READER)) { 734b31a149bSAttilio Rao LOCK_LOG_TRY("RLOCK", &rw->lock_object, 0, 1, file, 735b31a149bSAttilio Rao line); 736b31a149bSAttilio Rao WITNESS_LOCK(&rw->lock_object, LOP_TRYLOCK, file, line); 737de2c95ccSMark Johnston LOCKSTAT_PROFILE_OBTAIN_RWLOCK_SUCCESS(rw__acquire, 738de2c95ccSMark Johnston rw, 0, 0, file, line, LOCKSTAT_READER); 739ce1c953eSMark Johnston TD_LOCKS_INC(curthread); 740b31a149bSAttilio Rao curthread->td_rw_rlocks++; 741b31a149bSAttilio Rao return (1); 742b31a149bSAttilio Rao } 743b31a149bSAttilio Rao } 744b31a149bSAttilio Rao 745b31a149bSAttilio Rao LOCK_LOG_TRY("RLOCK", &rw->lock_object, 0, 0, file, line); 746b31a149bSAttilio Rao return (0); 747b31a149bSAttilio Rao } 748b31a149bSAttilio Rao 749c1e1a7ecSMateusz Guzik int 750c1e1a7ecSMateusz Guzik __rw_try_rlock(volatile uintptr_t *c, const char *file, int line) 751c1e1a7ecSMateusz Guzik { 752c1e1a7ecSMateusz Guzik struct rwlock *rw; 753c1e1a7ecSMateusz Guzik 754c1e1a7ecSMateusz Guzik rw = rwlock2rw(c); 7555ba6facfSMateusz Guzik return (__rw_try_rlock_int(rw LOCK_FILE_LINE_ARG)); 756c1e1a7ecSMateusz Guzik } 757c1e1a7ecSMateusz Guzik 7583c84b4b3SRyan Libby static __always_inline bool 759b0a61642SMateusz Guzik __rw_runlock_try(struct rwlock *rw, struct thread *td, uintptr_t *vp) 7603f08bd8bSJohn Baldwin { 7613f08bd8bSJohn Baldwin 7623f08bd8bSJohn Baldwin for (;;) { 7639feec7efSMateusz Guzik if (RW_READERS(*vp) > 1 || !(*vp & RW_LOCK_WAITERS)) { 764b0a61642SMateusz Guzik if (atomic_fcmpset_rel_ptr(&rw->rw_lock, vp, 765b0a61642SMateusz Guzik *vp - RW_ONE_READER)) { 766aa89d8cdSJohn Baldwin if (LOCK_LOG_TEST(&rw->lock_object, 0)) 7673f08bd8bSJohn Baldwin CTR4(KTR_LOCK, 7683f08bd8bSJohn Baldwin "%s: %p succeeded %p -> %p", 769b0a61642SMateusz Guzik __func__, rw, (void *)*vp, 770b0a61642SMateusz Guzik (void *)(*vp - RW_ONE_READER)); 771b0a61642SMateusz Guzik td->td_rw_rlocks--; 772b0a61642SMateusz Guzik return (true); 7733f08bd8bSJohn Baldwin } 7743f08bd8bSJohn Baldwin continue; 775ddb38a1fSJohn Baldwin } 776b0a61642SMateusz Guzik break; 777b0a61642SMateusz Guzik } 778b0a61642SMateusz Guzik return (false); 779b0a61642SMateusz Guzik } 780b0a61642SMateusz Guzik 781b0a61642SMateusz Guzik static void __noinline 782013c0b49SMateusz Guzik __rw_runlock_hard(struct rwlock *rw, struct thread *td, uintptr_t v 783013c0b49SMateusz Guzik LOCK_FILE_LINE_ARG_DEF) 784b0a61642SMateusz Guzik { 785b0a61642SMateusz Guzik struct turnstile *ts; 78687ee63baSMateusz Guzik uintptr_t setv, passedv, queue; 787b0a61642SMateusz Guzik 788b0a61642SMateusz Guzik if (SCHEDULER_STOPPED()) 789b0a61642SMateusz Guzik return; 790b0a61642SMateusz Guzik 79187ee63baSMateusz Guzik passedv = v; 792b0a61642SMateusz Guzik if (__rw_runlock_try(rw, td, &v)) 793f795032bSMateusz Guzik goto out_lockstat; 794b0a61642SMateusz Guzik 7953f08bd8bSJohn Baldwin /* 7965dff04c3SJeff Roberson * Ok, we know we have waiters and we think we are the 7975dff04c3SJeff Roberson * last reader, so grab the turnstile lock. 7983f08bd8bSJohn Baldwin */ 7992502c107SJeff Roberson turnstile_chain_lock(&rw->lock_object); 80093118b62SMateusz Guzik v = RW_READ_VALUE(rw); 801f795032bSMateusz Guzik for (;;) { 802f795032bSMateusz Guzik if (__rw_runlock_try(rw, td, &v)) 80384f2a8a4SMateusz Guzik break; 804e57b2b18SMateusz Guzik 8055dff04c3SJeff Roberson MPASS(v & RW_LOCK_WAITERS); 8063f08bd8bSJohn Baldwin 8073f08bd8bSJohn Baldwin /* 8083f08bd8bSJohn Baldwin * Try to drop our lock leaving the lock in a unlocked 8093f08bd8bSJohn Baldwin * state. 8103f08bd8bSJohn Baldwin * 8113f08bd8bSJohn Baldwin * If you wanted to do explicit lock handoff you'd have to 8123f08bd8bSJohn Baldwin * do it here. You'd also want to use turnstile_signal() 8133f08bd8bSJohn Baldwin * and you'd have to handle the race where a higher 8143f08bd8bSJohn Baldwin * priority thread blocks on the write lock before the 8153f08bd8bSJohn Baldwin * thread you wakeup actually runs and have the new thread 8163f08bd8bSJohn Baldwin * "steal" the lock. For now it's a lot simpler to just 8173f08bd8bSJohn Baldwin * wakeup all of the waiters. 8183f08bd8bSJohn Baldwin * 8193f08bd8bSJohn Baldwin * As above, if we fail, then another thread might have 8203f08bd8bSJohn Baldwin * acquired a read lock, so drop the turnstile lock and 8213f08bd8bSJohn Baldwin * restart. 8223f08bd8bSJohn Baldwin */ 82380c39f6cSMateusz Guzik setv = RW_UNLOCKED; 82480c39f6cSMateusz Guzik queue = TS_SHARED_QUEUE; 8255dff04c3SJeff Roberson if (v & RW_LOCK_WRITE_WAITERS) { 8265dff04c3SJeff Roberson queue = TS_EXCLUSIVE_QUEUE; 82780c39f6cSMateusz Guzik setv |= (v & RW_LOCK_READ_WAITERS); 82880c39f6cSMateusz Guzik } 8299feec7efSMateusz Guzik setv |= (v & RW_LOCK_WRITE_SPINNER); 83080c39f6cSMateusz Guzik if (!atomic_fcmpset_rel_ptr(&rw->rw_lock, &v, setv)) 831f795032bSMateusz Guzik continue; 832aa89d8cdSJohn Baldwin if (LOCK_LOG_TEST(&rw->lock_object, 0)) 8333f08bd8bSJohn Baldwin CTR2(KTR_LOCK, "%s: %p last succeeded with waiters", 8343f08bd8bSJohn Baldwin __func__, rw); 8353f08bd8bSJohn Baldwin 8363f08bd8bSJohn Baldwin /* 8373f08bd8bSJohn Baldwin * Ok. The lock is released and all that's left is to 8383f08bd8bSJohn Baldwin * wake up the waiters. Note that the lock might not be 8393f08bd8bSJohn Baldwin * free anymore, but in that case the writers will just 8403f08bd8bSJohn Baldwin * block again if they run before the new lock holder(s) 8413f08bd8bSJohn Baldwin * release the lock. 8423f08bd8bSJohn Baldwin */ 843aa89d8cdSJohn Baldwin ts = turnstile_lookup(&rw->lock_object); 84487ee63baSMateusz Guzik if (__predict_false(ts == NULL)) { 84573da0265SJohn Baldwin panic("got NULL turnstile on rwlock %p passedv %p v %p", 84673da0265SJohn Baldwin rw, (void *)passedv, (void *)v); 84787ee63baSMateusz Guzik } 8485dff04c3SJeff Roberson turnstile_broadcast(ts, queue); 849d0a22279SMateusz Guzik turnstile_unpend(ts); 850b0a61642SMateusz Guzik td->td_rw_rlocks--; 8513f08bd8bSJohn Baldwin break; 8523f08bd8bSJohn Baldwin } 853f795032bSMateusz Guzik turnstile_chain_unlock(&rw->lock_object); 854f795032bSMateusz Guzik out_lockstat: 855de2c95ccSMark Johnston LOCKSTAT_PROFILE_RELEASE_RWLOCK(rw__release, rw, LOCKSTAT_READER); 8563f08bd8bSJohn Baldwin } 8573f08bd8bSJohn Baldwin 858b0a61642SMateusz Guzik void 859013c0b49SMateusz Guzik _rw_runlock_cookie_int(struct rwlock *rw LOCK_FILE_LINE_ARG_DEF) 860b0a61642SMateusz Guzik { 861b0a61642SMateusz Guzik struct thread *td; 862b0a61642SMateusz Guzik uintptr_t v; 863b0a61642SMateusz Guzik 864b0a61642SMateusz Guzik KASSERT(rw->rw_lock != RW_DESTROYED, 865b0a61642SMateusz Guzik ("rw_runlock() of destroyed rwlock @ %s:%d", file, line)); 866013c0b49SMateusz Guzik __rw_assert(&rw->rw_lock, RA_RLOCKED, file, line); 867b0a61642SMateusz Guzik WITNESS_UNLOCK(&rw->lock_object, 0, file, line); 868b0a61642SMateusz Guzik LOCK_LOG_LOCK("RUNLOCK", &rw->lock_object, 0, 0, file, line); 869b0a61642SMateusz Guzik 870b0a61642SMateusz Guzik td = curthread; 871b0a61642SMateusz Guzik v = RW_READ_VALUE(rw); 872b0a61642SMateusz Guzik 873e4ccf57fSMateusz Guzik if (__predict_false(LOCKSTAT_PROFILE_ENABLED(rw__release) || 874b0a61642SMateusz Guzik !__rw_runlock_try(rw, td, &v))) 875013c0b49SMateusz Guzik __rw_runlock_hard(rw, td, v LOCK_FILE_LINE_ARG); 876e4ccf57fSMateusz Guzik else 8776a467cc5SMateusz Guzik lock_profile_release_lock(&rw->lock_object, false); 878b0a61642SMateusz Guzik 879b0a61642SMateusz Guzik TD_LOCKS_DEC(curthread); 880b0a61642SMateusz Guzik } 881b0a61642SMateusz Guzik 882013c0b49SMateusz Guzik void 883013c0b49SMateusz Guzik _rw_runlock_cookie(volatile uintptr_t *c, const char *file, int line) 884013c0b49SMateusz Guzik { 885013c0b49SMateusz Guzik struct rwlock *rw; 886013c0b49SMateusz Guzik 887013c0b49SMateusz Guzik rw = rwlock2rw(c); 888013c0b49SMateusz Guzik _rw_runlock_cookie_int(rw LOCK_FILE_LINE_ARG); 889013c0b49SMateusz Guzik } 890013c0b49SMateusz Guzik 8919feec7efSMateusz Guzik #ifdef ADAPTIVE_RWLOCKS 8929feec7efSMateusz Guzik static inline void 8939feec7efSMateusz Guzik rw_drop_critical(uintptr_t v, bool *in_critical, int *extra_work) 8949feec7efSMateusz Guzik { 8959feec7efSMateusz Guzik 8969feec7efSMateusz Guzik if (v & RW_LOCK_WRITE_SPINNER) 8979feec7efSMateusz Guzik return; 8989feec7efSMateusz Guzik if (*in_critical) { 8999feec7efSMateusz Guzik critical_exit(); 9009feec7efSMateusz Guzik *in_critical = false; 9019feec7efSMateusz Guzik (*extra_work)--; 9029feec7efSMateusz Guzik } 9039feec7efSMateusz Guzik } 9049feec7efSMateusz Guzik #else 9059feec7efSMateusz Guzik #define rw_drop_critical(v, in_critical, extra_work) do { } while (0) 9069feec7efSMateusz Guzik #endif 9079feec7efSMateusz Guzik 9083f08bd8bSJohn Baldwin /* 9093f08bd8bSJohn Baldwin * This function is called when we are unable to obtain a write lock on the 9103f08bd8bSJohn Baldwin * first try. This means that at least one other thread holds either a 9113f08bd8bSJohn Baldwin * read or write lock. 9123f08bd8bSJohn Baldwin */ 9133f08bd8bSJohn Baldwin void 914013c0b49SMateusz Guzik __rw_wlock_hard(volatile uintptr_t *c, uintptr_t v LOCK_FILE_LINE_ARG_DEF) 9153f08bd8bSJohn Baldwin { 916013c0b49SMateusz Guzik uintptr_t tid; 91719d41533SAttilio Rao struct rwlock *rw; 9182502c107SJeff Roberson struct turnstile *ts; 9192e106e04SMateusz Guzik struct thread *owner; 920cd6e6e4eSJohn Baldwin #ifdef ADAPTIVE_RWLOCKS 9215dff04c3SJeff Roberson int spintries = 0; 922d07e22cdSMateusz Guzik int i, n; 9231dce110fSMatt Macy enum { READERS, WRITER } sleep_reason = READERS; 9249feec7efSMateusz Guzik bool in_critical = false; 9255884c1a0SWojciech A. Koszek #endif 9269feec7efSMateusz Guzik uintptr_t setv; 9271723a064SJeff Roberson #ifdef LOCK_PROFILING 9281723a064SJeff Roberson uint64_t waittime = 0; 9296aa294beSAttilio Rao int contested = 0; 9301723a064SJeff Roberson #endif 93104126895SMateusz Guzik #if defined(ADAPTIVE_RWLOCKS) || defined(KDTRACE_HOOKS) 9321ada9041SMateusz Guzik struct lock_delay_arg lda; 9331ada9041SMateusz Guzik #endif 934a5aedd68SStacey Son #ifdef KDTRACE_HOOKS 93561852185SMateusz Guzik u_int sleep_cnt = 0; 936a5aedd68SStacey Son int64_t sleep_time = 0; 937076dd8ebSAndriy Gapon int64_t all_time = 0; 938*928864a9SKristof Provost uintptr_t state = 0; 939a5aedd68SStacey Son #endif 9402567807cSMateusz Guzik #if defined(KDTRACE_HOOKS) || defined(LOCK_PROFILING) 94109bdec20SMateusz Guzik int doing_lockprof = 0; 9422567807cSMateusz Guzik #endif 9439feec7efSMateusz Guzik int extra_work = 0; 9443f08bd8bSJohn Baldwin 945013c0b49SMateusz Guzik tid = (uintptr_t)curthread; 94609bdec20SMateusz Guzik rw = rwlock2rw(c); 94709bdec20SMateusz Guzik 94809bdec20SMateusz Guzik #ifdef KDTRACE_HOOKS 94909bdec20SMateusz Guzik if (LOCKSTAT_PROFILE_ENABLED(rw__acquire)) { 95009bdec20SMateusz Guzik while (v == RW_UNLOCKED) { 95109bdec20SMateusz Guzik if (_rw_write_lock_fetch(rw, &v, tid)) 95209bdec20SMateusz Guzik goto out_lockstat; 95309bdec20SMateusz Guzik } 9549feec7efSMateusz Guzik extra_work = 1; 95509bdec20SMateusz Guzik doing_lockprof = 1; 95609bdec20SMateusz Guzik all_time -= lockstat_nsecs(&rw->lock_object); 95709bdec20SMateusz Guzik } 958*928864a9SKristof Provost state = v; 95909bdec20SMateusz Guzik #endif 96009bdec20SMateusz Guzik #ifdef LOCK_PROFILING 9619feec7efSMateusz Guzik extra_work = 1; 96209bdec20SMateusz Guzik doing_lockprof = 1; 96309bdec20SMateusz Guzik #endif 96409bdec20SMateusz Guzik 96535370593SAndriy Gapon if (SCHEDULER_STOPPED()) 96635370593SAndriy Gapon return; 96735370593SAndriy Gapon 968c1aaf63cSMateusz Guzik if (__predict_false(v == RW_UNLOCKED)) 969c1aaf63cSMateusz Guzik v = RW_READ_VALUE(rw); 97019d41533SAttilio Rao 9713f0a0612SMateusz Guzik if (__predict_false(lv_rw_wowner(v) == (struct thread *)tid)) { 972f0830182SAttilio Rao KASSERT(rw->lock_object.lo_flags & LO_RECURSABLE, 973f08945a7SAttilio Rao ("%s: recursing but non-recursive rw %s @ %s:%d\n", 974f08945a7SAttilio Rao __func__, rw->lock_object.lo_name, file, line)); 975f08945a7SAttilio Rao rw->rw_recurse++; 976dbccc810SMateusz Guzik atomic_set_ptr(&rw->rw_lock, RW_LOCK_WRITER_RECURSED); 977f08945a7SAttilio Rao if (LOCK_LOG_TEST(&rw->lock_object, 0)) 978f08945a7SAttilio Rao CTR2(KTR_LOCK, "%s: %p recursing", __func__, rw); 979f08945a7SAttilio Rao return; 980f08945a7SAttilio Rao } 981f08945a7SAttilio Rao 982aa89d8cdSJohn Baldwin if (LOCK_LOG_TEST(&rw->lock_object, 0)) 9833f08bd8bSJohn Baldwin CTR5(KTR_LOCK, "%s: %s contested (lock=%p) at %s:%d", __func__, 984aa89d8cdSJohn Baldwin rw->lock_object.lo_name, (void *)rw->rw_lock, file, line); 9853f08bd8bSJohn Baldwin 986f90d57b8SMateusz Guzik #if defined(ADAPTIVE_RWLOCKS) 987f90d57b8SMateusz Guzik lock_delay_arg_init(&lda, &rw_delay); 988f90d57b8SMateusz Guzik #elif defined(KDTRACE_HOOKS) 989f90d57b8SMateusz Guzik lock_delay_arg_init_noadapt(&lda); 990f90d57b8SMateusz Guzik #endif 991f90d57b8SMateusz Guzik 992ae7d25a4SMateusz Guzik #ifdef HWPMC_HOOKS 993ae7d25a4SMateusz Guzik PMC_SOFT_CALL( , , lock, failed); 994ae7d25a4SMateusz Guzik #endif 9956a467cc5SMateusz Guzik lock_profile_obtain_lock_failed(&rw->lock_object, false, 996ae7d25a4SMateusz Guzik &contested, &waittime); 997ae7d25a4SMateusz Guzik 9987530de77SMateusz Guzik THREAD_CONTENDS_ON_LOCK(&rw->lock_object); 9997530de77SMateusz Guzik 1000fc4f686dSMateusz Guzik for (;;) { 10013f0a0612SMateusz Guzik if (v == RW_UNLOCKED) { 1002c84f3479SMateusz Guzik if (_rw_write_lock_fetch(rw, &v, tid)) 1003fc4f686dSMateusz Guzik break; 10043f0a0612SMateusz Guzik continue; 10053f0a0612SMateusz Guzik } 1006a5aedd68SStacey Son #ifdef KDTRACE_HOOKS 10071ada9041SMateusz Guzik lda.spin_cnt++; 1008a5aedd68SStacey Son #endif 1009ae7d25a4SMateusz Guzik 101049aead8aSAttilio Rao #ifdef ADAPTIVE_RWLOCKS 10119feec7efSMateusz Guzik if (v == (RW_LOCK_READ | RW_LOCK_WRITE_SPINNER)) { 10129feec7efSMateusz Guzik if (atomic_fcmpset_acq_ptr(&rw->rw_lock, &v, tid)) 10139feec7efSMateusz Guzik break; 10149feec7efSMateusz Guzik continue; 10159feec7efSMateusz Guzik } 10169feec7efSMateusz Guzik 101749aead8aSAttilio Rao /* 101849aead8aSAttilio Rao * If the lock is write locked and the owner is 101949aead8aSAttilio Rao * running on another CPU, spin until the owner stops 102049aead8aSAttilio Rao * running or the state of the lock changes. 102149aead8aSAttilio Rao */ 1022d94df98cSMateusz Guzik if (!(v & RW_LOCK_READ)) { 10239feec7efSMateusz Guzik rw_drop_critical(v, &in_critical, &extra_work); 1024d94df98cSMateusz Guzik sleep_reason = WRITER; 10253f0a0612SMateusz Guzik owner = lv_rw_wowner(v); 1026d94df98cSMateusz Guzik if (!TD_IS_RUNNING(owner)) 1027d94df98cSMateusz Guzik goto ts; 102849aead8aSAttilio Rao if (LOCK_LOG_TEST(&rw->lock_object, 0)) 102949aead8aSAttilio Rao CTR3(KTR_LOCK, "%s: spinning on %p held by %p", 103049aead8aSAttilio Rao __func__, rw, owner); 10312cba8dd3SJohn Baldwin KTR_STATE1(KTR_SCHED, "thread", sched_tdname(curthread), 10322cba8dd3SJohn Baldwin "spinning", "lockname:\"%s\"", 10332cba8dd3SJohn Baldwin rw->lock_object.lo_name); 10343f0a0612SMateusz Guzik do { 10351ada9041SMateusz Guzik lock_delay(&lda); 10363f0a0612SMateusz Guzik v = RW_READ_VALUE(rw); 10373f0a0612SMateusz Guzik owner = lv_rw_wowner(v); 10383f0a0612SMateusz Guzik } while (owner != NULL && TD_IS_RUNNING(owner)); 10392cba8dd3SJohn Baldwin KTR_STATE0(KTR_SCHED, "thread", sched_tdname(curthread), 10402cba8dd3SJohn Baldwin "running"); 104149aead8aSAttilio Rao continue; 1042d94df98cSMateusz Guzik } else if (RW_READERS(v) > 0) { 1043d94df98cSMateusz Guzik sleep_reason = READERS; 1044d94df98cSMateusz Guzik if (spintries == rowner_retries) 1045d94df98cSMateusz Guzik goto ts; 10465dff04c3SJeff Roberson if (!(v & RW_LOCK_WRITE_SPINNER)) { 10479feec7efSMateusz Guzik if (!in_critical) { 10489feec7efSMateusz Guzik critical_enter(); 10499feec7efSMateusz Guzik in_critical = true; 10509feec7efSMateusz Guzik extra_work++; 10519feec7efSMateusz Guzik } 1052c7e4e92eSMateusz Guzik if (!atomic_fcmpset_ptr(&rw->rw_lock, &v, 10535dff04c3SJeff Roberson v | RW_LOCK_WRITE_SPINNER)) { 10549feec7efSMateusz Guzik critical_exit(); 10559feec7efSMateusz Guzik in_critical = false; 10569feec7efSMateusz Guzik extra_work--; 10573f08bd8bSJohn Baldwin continue; 10583f08bd8bSJohn Baldwin } 10595dff04c3SJeff Roberson } 10605dff04c3SJeff Roberson spintries++; 10612cba8dd3SJohn Baldwin KTR_STATE1(KTR_SCHED, "thread", sched_tdname(curthread), 10622cba8dd3SJohn Baldwin "spinning", "lockname:\"%s\"", 10632cba8dd3SJohn Baldwin rw->lock_object.lo_name); 1064d07e22cdSMateusz Guzik n = RW_READERS(v); 10659feec7efSMateusz Guzik for (i = 0; i < rowner_loops; i += n) { 1066d07e22cdSMateusz Guzik lock_delay_spin(n); 106720a15d17SMateusz Guzik v = RW_READ_VALUE(rw); 10689feec7efSMateusz Guzik if (!(v & RW_LOCK_WRITE_SPINNER)) 10699feec7efSMateusz Guzik break; 10709feec7efSMateusz Guzik if (!(v & RW_LOCK_READ)) 10719feec7efSMateusz Guzik break; 10729feec7efSMateusz Guzik n = RW_READERS(v); 10739feec7efSMateusz Guzik if (n == 0) 107420a15d17SMateusz Guzik break; 10755dff04c3SJeff Roberson } 1076d94df98cSMateusz Guzik #ifdef KDTRACE_HOOKS 1077d94df98cSMateusz Guzik lda.spin_cnt += i; 1078d94df98cSMateusz Guzik #endif 10792cba8dd3SJohn Baldwin KTR_STATE0(KTR_SCHED, "thread", sched_tdname(curthread), 10802cba8dd3SJohn Baldwin "running"); 1081efa9f177SMateusz Guzik if (i < rowner_loops) 10825dff04c3SJeff Roberson continue; 10835dff04c3SJeff Roberson } 1084d94df98cSMateusz Guzik ts: 10855dff04c3SJeff Roberson #endif 10865dff04c3SJeff Roberson ts = turnstile_trywait(&rw->lock_object); 10873f0a0612SMateusz Guzik v = RW_READ_VALUE(rw); 108893118b62SMateusz Guzik retry_ts: 10892e106e04SMateusz Guzik owner = lv_rw_wowner(v); 10903f08bd8bSJohn Baldwin 109149aead8aSAttilio Rao #ifdef ADAPTIVE_RWLOCKS 109249aead8aSAttilio Rao /* 1093fa29f023SJohn Baldwin * The current lock owner might have started executing 1094fa29f023SJohn Baldwin * on another CPU (or the lock could have changed 1095fa29f023SJohn Baldwin * owners) while we were waiting on the turnstile 1096fa29f023SJohn Baldwin * chain lock. If so, drop the turnstile lock and try 1097fa29f023SJohn Baldwin * again. 109849aead8aSAttilio Rao */ 10992e106e04SMateusz Guzik if (owner != NULL) { 110049aead8aSAttilio Rao if (TD_IS_RUNNING(owner)) { 110149aead8aSAttilio Rao turnstile_cancel(ts); 11029feec7efSMateusz Guzik rw_drop_critical(v, &in_critical, &extra_work); 110349aead8aSAttilio Rao continue; 110449aead8aSAttilio Rao } 1105d94df98cSMateusz Guzik } else if (RW_READERS(v) > 0 && sleep_reason == WRITER) { 110628f1a9e3SMateusz Guzik turnstile_cancel(ts); 11079feec7efSMateusz Guzik rw_drop_critical(v, &in_critical, &extra_work); 110828f1a9e3SMateusz Guzik continue; 110949aead8aSAttilio Rao } 111049aead8aSAttilio Rao #endif 11113f08bd8bSJohn Baldwin /* 111248972152SAttilio Rao * Check for the waiters flags about this rwlock. 111348972152SAttilio Rao * If the lock was released, without maintain any pending 111448972152SAttilio Rao * waiters queue, simply try to acquire it. 111548972152SAttilio Rao * If a pending waiters queue is present, claim the lock 111648972152SAttilio Rao * ownership and maintain the pending queue. 11173f08bd8bSJohn Baldwin */ 11189feec7efSMateusz Guzik setv = v & (RW_LOCK_WAITERS | RW_LOCK_WRITE_SPINNER); 11199feec7efSMateusz Guzik if ((v & ~setv) == RW_UNLOCKED) { 11209feec7efSMateusz Guzik setv &= ~RW_LOCK_WRITE_SPINNER; 11219feec7efSMateusz Guzik if (atomic_fcmpset_acq_ptr(&rw->rw_lock, &v, tid | setv)) { 11229feec7efSMateusz Guzik if (setv) 11232502c107SJeff Roberson turnstile_claim(ts); 11245dff04c3SJeff Roberson else 11255dff04c3SJeff Roberson turnstile_cancel(ts); 11263f08bd8bSJohn Baldwin break; 11273f08bd8bSJohn Baldwin } 112893118b62SMateusz Guzik goto retry_ts; 11293f08bd8bSJohn Baldwin } 11309feec7efSMateusz Guzik 11319feec7efSMateusz Guzik #ifdef ADAPTIVE_RWLOCKS 11329feec7efSMateusz Guzik if (in_critical) { 11339feec7efSMateusz Guzik if ((v & RW_LOCK_WRITE_SPINNER) || 11349feec7efSMateusz Guzik !((v & RW_LOCK_WRITE_WAITERS))) { 11359feec7efSMateusz Guzik setv = v & ~RW_LOCK_WRITE_SPINNER; 11369feec7efSMateusz Guzik setv |= RW_LOCK_WRITE_WAITERS; 11379feec7efSMateusz Guzik if (!atomic_fcmpset_ptr(&rw->rw_lock, &v, setv)) 11389feec7efSMateusz Guzik goto retry_ts; 11399feec7efSMateusz Guzik } 11409feec7efSMateusz Guzik critical_exit(); 11419feec7efSMateusz Guzik in_critical = false; 11429feec7efSMateusz Guzik extra_work--; 11439feec7efSMateusz Guzik } else { 11449feec7efSMateusz Guzik #endif 11453f08bd8bSJohn Baldwin /* 11463f08bd8bSJohn Baldwin * If the RW_LOCK_WRITE_WAITERS flag isn't set, then try to 11473f08bd8bSJohn Baldwin * set it. If we fail to set it, then loop back and try 11483f08bd8bSJohn Baldwin * again. 11493f08bd8bSJohn Baldwin */ 115038bf165fSJohn Baldwin if (!(v & RW_LOCK_WRITE_WAITERS)) { 115193118b62SMateusz Guzik if (!atomic_fcmpset_ptr(&rw->rw_lock, &v, 115293118b62SMateusz Guzik v | RW_LOCK_WRITE_WAITERS)) 115393118b62SMateusz Guzik goto retry_ts; 1154aa89d8cdSJohn Baldwin if (LOCK_LOG_TEST(&rw->lock_object, 0)) 11553f08bd8bSJohn Baldwin CTR2(KTR_LOCK, "%s: %p set write waiters flag", 11563f08bd8bSJohn Baldwin __func__, rw); 115738bf165fSJohn Baldwin } 11589feec7efSMateusz Guzik #ifdef ADAPTIVE_RWLOCKS 11599feec7efSMateusz Guzik } 11609feec7efSMateusz Guzik #endif 11613f08bd8bSJohn Baldwin /* 11623f08bd8bSJohn Baldwin * We were unable to acquire the lock and the write waiters 11633f08bd8bSJohn Baldwin * flag is set, so we must block on the turnstile. 11643f08bd8bSJohn Baldwin */ 1165aa89d8cdSJohn Baldwin if (LOCK_LOG_TEST(&rw->lock_object, 0)) 11663f08bd8bSJohn Baldwin CTR2(KTR_LOCK, "%s: %p blocking on turnstile", __func__, 11673f08bd8bSJohn Baldwin rw); 1168a5aedd68SStacey Son #ifdef KDTRACE_HOOKS 1169e2b25737SMark Johnston sleep_time -= lockstat_nsecs(&rw->lock_object); 1170a5aedd68SStacey Son #endif 11712e106e04SMateusz Guzik MPASS(owner == rw_owner(rw)); 11722e106e04SMateusz Guzik turnstile_wait(ts, owner, TS_EXCLUSIVE_QUEUE); 1173a5aedd68SStacey Son #ifdef KDTRACE_HOOKS 1174e2b25737SMark Johnston sleep_time += lockstat_nsecs(&rw->lock_object); 1175a5aedd68SStacey Son sleep_cnt++; 1176a5aedd68SStacey Son #endif 1177aa89d8cdSJohn Baldwin if (LOCK_LOG_TEST(&rw->lock_object, 0)) 11783f08bd8bSJohn Baldwin CTR2(KTR_LOCK, "%s: %p resuming from turnstile", 11793f08bd8bSJohn Baldwin __func__, rw); 11805dff04c3SJeff Roberson #ifdef ADAPTIVE_RWLOCKS 11815dff04c3SJeff Roberson spintries = 0; 11825dff04c3SJeff Roberson #endif 11833f0a0612SMateusz Guzik v = RW_READ_VALUE(rw); 11843f08bd8bSJohn Baldwin } 11857530de77SMateusz Guzik THREAD_CONTENTION_DONE(&rw->lock_object); 11869feec7efSMateusz Guzik if (__predict_true(!extra_work)) 11879feec7efSMateusz Guzik return; 11889feec7efSMateusz Guzik #ifdef ADAPTIVE_RWLOCKS 11899feec7efSMateusz Guzik if (in_critical) 11909feec7efSMateusz Guzik critical_exit(); 11919feec7efSMateusz Guzik #endif 11922567807cSMateusz Guzik #if defined(KDTRACE_HOOKS) || defined(LOCK_PROFILING) 11932567807cSMateusz Guzik if (__predict_true(!doing_lockprof)) 11942567807cSMateusz Guzik return; 11952567807cSMateusz Guzik #endif 1196076dd8ebSAndriy Gapon #ifdef KDTRACE_HOOKS 1197e2b25737SMark Johnston all_time += lockstat_nsecs(&rw->lock_object); 1198076dd8ebSAndriy Gapon if (sleep_time) 119932cd0147SMark Johnston LOCKSTAT_RECORD4(rw__block, rw, sleep_time, 1200076dd8ebSAndriy Gapon LOCKSTAT_WRITER, (state & RW_LOCK_READ) == 0, 1201076dd8ebSAndriy Gapon (state & RW_LOCK_READ) == 0 ? 0 : RW_READERS(state)); 1202076dd8ebSAndriy Gapon 1203076dd8ebSAndriy Gapon /* Record only the loops spinning and not sleeping. */ 12041ada9041SMateusz Guzik if (lda.spin_cnt > sleep_cnt) 120532cd0147SMark Johnston LOCKSTAT_RECORD4(rw__spin, rw, all_time - sleep_time, 12067a54be18SMateusz Guzik LOCKSTAT_WRITER, (state & RW_LOCK_READ) == 0, 1207076dd8ebSAndriy Gapon (state & RW_LOCK_READ) == 0 ? 0 : RW_READERS(state)); 120809bdec20SMateusz Guzik out_lockstat: 1209076dd8ebSAndriy Gapon #endif 1210de2c95ccSMark Johnston LOCKSTAT_PROFILE_OBTAIN_RWLOCK_SUCCESS(rw__acquire, rw, contested, 1211de2c95ccSMark Johnston waittime, file, line, LOCKSTAT_WRITER); 12123f08bd8bSJohn Baldwin } 12133f08bd8bSJohn Baldwin 12143f08bd8bSJohn Baldwin /* 12153b3cf014SMateusz Guzik * This function is called if lockstat is active or the first try at releasing 12163b3cf014SMateusz Guzik * a write lock failed. The latter means that the lock is recursed or one of 12173b3cf014SMateusz Guzik * the 2 waiter bits must be set indicating that at least one thread is waiting 12183b3cf014SMateusz Guzik * on this lock. 12193f08bd8bSJohn Baldwin */ 12203f08bd8bSJohn Baldwin void 1221b584eb2eSMateusz Guzik __rw_wunlock_hard(volatile uintptr_t *c, uintptr_t v LOCK_FILE_LINE_ARG_DEF) 12223f08bd8bSJohn Baldwin { 122319d41533SAttilio Rao struct rwlock *rw; 12243f08bd8bSJohn Baldwin struct turnstile *ts; 122587ee63baSMateusz Guzik uintptr_t tid, setv, passedv; 12263f08bd8bSJohn Baldwin int queue; 12273f08bd8bSJohn Baldwin 1228b584eb2eSMateusz Guzik tid = (uintptr_t)curthread; 122935370593SAndriy Gapon if (SCHEDULER_STOPPED()) 123035370593SAndriy Gapon return; 123135370593SAndriy Gapon 123219d41533SAttilio Rao rw = rwlock2rw(c); 1233b584eb2eSMateusz Guzik if (__predict_false(v == tid)) 12343b3cf014SMateusz Guzik v = RW_READ_VALUE(rw); 1235b584eb2eSMateusz Guzik 12363b3cf014SMateusz Guzik if (v & RW_LOCK_WRITER_RECURSED) { 1237dbccc810SMateusz Guzik if (--(rw->rw_recurse) == 0) 1238dbccc810SMateusz Guzik atomic_clear_ptr(&rw->rw_lock, RW_LOCK_WRITER_RECURSED); 12393b3cf014SMateusz Guzik if (LOCK_LOG_TEST(&rw->lock_object, 0)) 12403b3cf014SMateusz Guzik CTR2(KTR_LOCK, "%s: %p unrecursing", __func__, rw); 1241dbccc810SMateusz Guzik return; 1242dbccc810SMateusz Guzik } 1243f08945a7SAttilio Rao 12443b3cf014SMateusz Guzik LOCKSTAT_PROFILE_RELEASE_RWLOCK(rw__release, rw, LOCKSTAT_WRITER); 12453b3cf014SMateusz Guzik if (v == tid && _rw_write_unlock(rw, tid)) 12463b3cf014SMateusz Guzik return; 12473b3cf014SMateusz Guzik 12483f08bd8bSJohn Baldwin KASSERT(rw->rw_lock & (RW_LOCK_READ_WAITERS | RW_LOCK_WRITE_WAITERS), 12493f08bd8bSJohn Baldwin ("%s: neither of the waiter flags are set", __func__)); 12503f08bd8bSJohn Baldwin 1251aa89d8cdSJohn Baldwin if (LOCK_LOG_TEST(&rw->lock_object, 0)) 12523f08bd8bSJohn Baldwin CTR2(KTR_LOCK, "%s: %p contested", __func__, rw); 12533f08bd8bSJohn Baldwin 12542502c107SJeff Roberson turnstile_chain_lock(&rw->lock_object); 12553f08bd8bSJohn Baldwin 12563f08bd8bSJohn Baldwin /* 12573f08bd8bSJohn Baldwin * Use the same algo as sx locks for now. Prefer waking up shared 12583f08bd8bSJohn Baldwin * waiters if we have any over writers. This is probably not ideal. 12593f08bd8bSJohn Baldwin * 12603f08bd8bSJohn Baldwin * 'v' is the value we are going to write back to rw_lock. If we 12613f08bd8bSJohn Baldwin * have waiters on both queues, we need to preserve the state of 12623f08bd8bSJohn Baldwin * the waiter flag for the queue we don't wake up. For now this is 12633f08bd8bSJohn Baldwin * hardcoded for the algorithm mentioned above. 12643f08bd8bSJohn Baldwin * 12653f08bd8bSJohn Baldwin * In the case of both readers and writers waiting we wakeup the 12663f08bd8bSJohn Baldwin * readers but leave the RW_LOCK_WRITE_WAITERS flag set. If a 12673f08bd8bSJohn Baldwin * new writer comes in before a reader it will claim the lock up 12683f08bd8bSJohn Baldwin * above. There is probably a potential priority inversion in 12693f08bd8bSJohn Baldwin * there that could be worked around either by waking both queues 12703f08bd8bSJohn Baldwin * of waiters or doing some complicated lock handoff gymnastics. 12713f08bd8bSJohn Baldwin */ 12728fef6b2cSMateusz Guzik setv = RW_UNLOCKED; 127387ee63baSMateusz Guzik passedv = v; 12748fef6b2cSMateusz Guzik v = RW_READ_VALUE(rw); 12750fef2c50SJeff Roberson queue = TS_SHARED_QUEUE; 12768fef6b2cSMateusz Guzik if (v & RW_LOCK_WRITE_WAITERS) { 12778fef6b2cSMateusz Guzik queue = TS_EXCLUSIVE_QUEUE; 12788fef6b2cSMateusz Guzik setv |= (v & RW_LOCK_READ_WAITERS); 12798fef6b2cSMateusz Guzik } 12808fef6b2cSMateusz Guzik atomic_store_rel_ptr(&rw->rw_lock, setv); 1281efa86db6SJohn Baldwin 1282efa86db6SJohn Baldwin /* Wake up all waiters for the specific queue. */ 1283aa89d8cdSJohn Baldwin if (LOCK_LOG_TEST(&rw->lock_object, 0)) 12843f08bd8bSJohn Baldwin CTR3(KTR_LOCK, "%s: %p waking up %s waiters", __func__, rw, 12853f08bd8bSJohn Baldwin queue == TS_SHARED_QUEUE ? "read" : "write"); 12868fef6b2cSMateusz Guzik 12878fef6b2cSMateusz Guzik ts = turnstile_lookup(&rw->lock_object); 128887ee63baSMateusz Guzik if (__predict_false(ts == NULL)) { 128973da0265SJohn Baldwin panic("got NULL turnstile on rwlock %p passedv %p v %p", rw, 129073da0265SJohn Baldwin (void *)passedv, (void *)v); 129187ee63baSMateusz Guzik } 12923f08bd8bSJohn Baldwin turnstile_broadcast(ts, queue); 1293d0a22279SMateusz Guzik turnstile_unpend(ts); 12942502c107SJeff Roberson turnstile_chain_unlock(&rw->lock_object); 12953f08bd8bSJohn Baldwin } 12963f08bd8bSJohn Baldwin 1297fea3efe5SJohn Baldwin /* 1298fea3efe5SJohn Baldwin * Attempt to do a non-blocking upgrade from a read lock to a write 1299fea3efe5SJohn Baldwin * lock. This will only succeed if this thread holds a single read 1300fea3efe5SJohn Baldwin * lock. Returns true if the upgrade succeeded and false otherwise. 1301fea3efe5SJohn Baldwin */ 1302fea3efe5SJohn Baldwin int 1303013c0b49SMateusz Guzik __rw_try_upgrade_int(struct rwlock *rw LOCK_FILE_LINE_ARG_DEF) 1304fea3efe5SJohn Baldwin { 130504457342SMateusz Guzik uintptr_t v, setv, tid; 13062502c107SJeff Roberson struct turnstile *ts; 1307fea3efe5SJohn Baldwin int success; 1308fea3efe5SJohn Baldwin 130935370593SAndriy Gapon if (SCHEDULER_STOPPED()) 131035370593SAndriy Gapon return (1); 131135370593SAndriy Gapon 13120026c92cSJohn Baldwin KASSERT(rw->rw_lock != RW_DESTROYED, 13130026c92cSJohn Baldwin ("rw_try_upgrade() of destroyed rwlock @ %s:%d", file, line)); 1314013c0b49SMateusz Guzik __rw_assert(&rw->rw_lock, RA_RLOCKED, file, line); 1315fea3efe5SJohn Baldwin 1316fea3efe5SJohn Baldwin /* 1317fea3efe5SJohn Baldwin * Attempt to switch from one reader to a writer. If there 1318fea3efe5SJohn Baldwin * are any write waiters, then we will have to lock the 1319fea3efe5SJohn Baldwin * turnstile first to prevent races with another writer 1320fea3efe5SJohn Baldwin * calling turnstile_wait() before we have claimed this 1321fea3efe5SJohn Baldwin * turnstile. So, do the simple case of no waiters first. 1322fea3efe5SJohn Baldwin */ 1323fea3efe5SJohn Baldwin tid = (uintptr_t)curthread; 13245dff04c3SJeff Roberson success = 0; 132504457342SMateusz Guzik v = RW_READ_VALUE(rw); 13265dff04c3SJeff Roberson for (;;) { 13275dff04c3SJeff Roberson if (RW_READERS(v) > 1) 13285dff04c3SJeff Roberson break; 13295dff04c3SJeff Roberson if (!(v & RW_LOCK_WAITERS)) { 133004457342SMateusz Guzik success = atomic_fcmpset_acq_ptr(&rw->rw_lock, &v, tid); 13315dff04c3SJeff Roberson if (!success) 13325dff04c3SJeff Roberson continue; 13335dff04c3SJeff Roberson break; 1334fea3efe5SJohn Baldwin } 1335fea3efe5SJohn Baldwin 1336fea3efe5SJohn Baldwin /* 13375dff04c3SJeff Roberson * Ok, we think we have waiters, so lock the turnstile. 1338fea3efe5SJohn Baldwin */ 13392502c107SJeff Roberson ts = turnstile_trywait(&rw->lock_object); 134004457342SMateusz Guzik v = RW_READ_VALUE(rw); 134104457342SMateusz Guzik retry_ts: 13425dff04c3SJeff Roberson if (RW_READERS(v) > 1) { 13435dff04c3SJeff Roberson turnstile_cancel(ts); 13445dff04c3SJeff Roberson break; 13455dff04c3SJeff Roberson } 1346fea3efe5SJohn Baldwin /* 1347fea3efe5SJohn Baldwin * Try to switch from one reader to a writer again. This time 13485dff04c3SJeff Roberson * we honor the current state of the waiters flags. 13495dff04c3SJeff Roberson * If we obtain the lock with the flags set, then claim 135049aead8aSAttilio Rao * ownership of the turnstile. 1351fea3efe5SJohn Baldwin */ 135204457342SMateusz Guzik setv = tid | (v & RW_LOCK_WAITERS); 135304457342SMateusz Guzik success = atomic_fcmpset_ptr(&rw->rw_lock, &v, setv); 13545dff04c3SJeff Roberson if (success) { 135504457342SMateusz Guzik if (v & RW_LOCK_WAITERS) 13562502c107SJeff Roberson turnstile_claim(ts); 1357fea3efe5SJohn Baldwin else 13582502c107SJeff Roberson turnstile_cancel(ts); 13595dff04c3SJeff Roberson break; 13605dff04c3SJeff Roberson } 136104457342SMateusz Guzik goto retry_ts; 13625dff04c3SJeff Roberson } 1363aa89d8cdSJohn Baldwin LOCK_LOG_TRY("WUPGRADE", &rw->lock_object, 0, success, file, line); 13645dff04c3SJeff Roberson if (success) { 13655dff04c3SJeff Roberson curthread->td_rw_rlocks--; 1366aa89d8cdSJohn Baldwin WITNESS_UPGRADE(&rw->lock_object, LOP_EXCLUSIVE | LOP_TRYLOCK, 1367fea3efe5SJohn Baldwin file, line); 136832cd0147SMark Johnston LOCKSTAT_RECORD0(rw__upgrade, rw); 13695dff04c3SJeff Roberson } 1370fea3efe5SJohn Baldwin return (success); 1371fea3efe5SJohn Baldwin } 1372fea3efe5SJohn Baldwin 1373013c0b49SMateusz Guzik int 1374013c0b49SMateusz Guzik __rw_try_upgrade(volatile uintptr_t *c, const char *file, int line) 1375013c0b49SMateusz Guzik { 1376013c0b49SMateusz Guzik struct rwlock *rw; 1377013c0b49SMateusz Guzik 1378013c0b49SMateusz Guzik rw = rwlock2rw(c); 1379013c0b49SMateusz Guzik return (__rw_try_upgrade_int(rw LOCK_FILE_LINE_ARG)); 1380013c0b49SMateusz Guzik } 1381013c0b49SMateusz Guzik 1382fea3efe5SJohn Baldwin /* 1383fea3efe5SJohn Baldwin * Downgrade a write lock into a single read lock. 1384fea3efe5SJohn Baldwin */ 1385fea3efe5SJohn Baldwin void 1386013c0b49SMateusz Guzik __rw_downgrade_int(struct rwlock *rw LOCK_FILE_LINE_ARG_DEF) 1387fea3efe5SJohn Baldwin { 1388fea3efe5SJohn Baldwin struct turnstile *ts; 1389fea3efe5SJohn Baldwin uintptr_t tid, v; 13905dff04c3SJeff Roberson int rwait, wwait; 1391fea3efe5SJohn Baldwin 139235370593SAndriy Gapon if (SCHEDULER_STOPPED()) 139335370593SAndriy Gapon return; 139435370593SAndriy Gapon 13950026c92cSJohn Baldwin KASSERT(rw->rw_lock != RW_DESTROYED, 13960026c92cSJohn Baldwin ("rw_downgrade() of destroyed rwlock @ %s:%d", file, line)); 1397013c0b49SMateusz Guzik __rw_assert(&rw->rw_lock, RA_WLOCKED | RA_NOTRECURSED, file, line); 1398f08945a7SAttilio Rao #ifndef INVARIANTS 1399f08945a7SAttilio Rao if (rw_recursed(rw)) 1400f08945a7SAttilio Rao panic("downgrade of a recursed lock"); 1401f08945a7SAttilio Rao #endif 1402fea3efe5SJohn Baldwin 1403aa89d8cdSJohn Baldwin WITNESS_DOWNGRADE(&rw->lock_object, 0, file, line); 1404fea3efe5SJohn Baldwin 1405fea3efe5SJohn Baldwin /* 1406fea3efe5SJohn Baldwin * Convert from a writer to a single reader. First we handle 1407fea3efe5SJohn Baldwin * the easy case with no waiters. If there are any waiters, we 14085dff04c3SJeff Roberson * lock the turnstile and "disown" the lock. 1409fea3efe5SJohn Baldwin */ 1410fea3efe5SJohn Baldwin tid = (uintptr_t)curthread; 1411fea3efe5SJohn Baldwin if (atomic_cmpset_rel_ptr(&rw->rw_lock, tid, RW_READERS_LOCK(1))) 1412fea3efe5SJohn Baldwin goto out; 1413fea3efe5SJohn Baldwin 1414fea3efe5SJohn Baldwin /* 1415fea3efe5SJohn Baldwin * Ok, we think we have waiters, so lock the turnstile so we can 1416fea3efe5SJohn Baldwin * read the waiter flags without any races. 1417fea3efe5SJohn Baldwin */ 14182502c107SJeff Roberson turnstile_chain_lock(&rw->lock_object); 14195dff04c3SJeff Roberson v = rw->rw_lock & RW_LOCK_WAITERS; 14205dff04c3SJeff Roberson rwait = v & RW_LOCK_READ_WAITERS; 14215dff04c3SJeff Roberson wwait = v & RW_LOCK_WRITE_WAITERS; 14225dff04c3SJeff Roberson MPASS(rwait | wwait); 1423fea3efe5SJohn Baldwin 1424fea3efe5SJohn Baldwin /* 14255dff04c3SJeff Roberson * Downgrade from a write lock while preserving waiters flag 14265dff04c3SJeff Roberson * and give up ownership of the turnstile. 1427fea3efe5SJohn Baldwin */ 1428aa89d8cdSJohn Baldwin ts = turnstile_lookup(&rw->lock_object); 1429fea3efe5SJohn Baldwin MPASS(ts != NULL); 14305dff04c3SJeff Roberson if (!wwait) 14315dff04c3SJeff Roberson v &= ~RW_LOCK_READ_WAITERS; 14325dff04c3SJeff Roberson atomic_store_rel_ptr(&rw->rw_lock, RW_READERS_LOCK(1) | v); 14335dff04c3SJeff Roberson /* 14345dff04c3SJeff Roberson * Wake other readers if there are no writers pending. Otherwise they 14355dff04c3SJeff Roberson * won't be able to acquire the lock anyway. 14365dff04c3SJeff Roberson */ 14375dff04c3SJeff Roberson if (rwait && !wwait) { 1438fea3efe5SJohn Baldwin turnstile_broadcast(ts, TS_SHARED_QUEUE); 1439d0a22279SMateusz Guzik turnstile_unpend(ts); 14405dff04c3SJeff Roberson } else 1441fea3efe5SJohn Baldwin turnstile_disown(ts); 14422502c107SJeff Roberson turnstile_chain_unlock(&rw->lock_object); 1443fea3efe5SJohn Baldwin out: 14445dff04c3SJeff Roberson curthread->td_rw_rlocks++; 1445aa89d8cdSJohn Baldwin LOCK_LOG_LOCK("WDOWNGRADE", &rw->lock_object, 0, 0, file, line); 144632cd0147SMark Johnston LOCKSTAT_RECORD0(rw__downgrade, rw); 1447fea3efe5SJohn Baldwin } 1448fea3efe5SJohn Baldwin 1449013c0b49SMateusz Guzik void 1450013c0b49SMateusz Guzik __rw_downgrade(volatile uintptr_t *c, const char *file, int line) 1451013c0b49SMateusz Guzik { 1452013c0b49SMateusz Guzik struct rwlock *rw; 1453013c0b49SMateusz Guzik 1454013c0b49SMateusz Guzik rw = rwlock2rw(c); 1455013c0b49SMateusz Guzik __rw_downgrade_int(rw LOCK_FILE_LINE_ARG); 1456013c0b49SMateusz Guzik } 1457013c0b49SMateusz Guzik 14583f08bd8bSJohn Baldwin #ifdef INVARIANT_SUPPORT 1459803e980dSScott Long #ifndef INVARIANTS 146019d41533SAttilio Rao #undef __rw_assert 14613f08bd8bSJohn Baldwin #endif 14623f08bd8bSJohn Baldwin 14633f08bd8bSJohn Baldwin /* 14643f08bd8bSJohn Baldwin * In the non-WITNESS case, rw_assert() can only detect that at least 14653f08bd8bSJohn Baldwin * *some* thread owns an rlock, but it cannot guarantee that *this* 14663f08bd8bSJohn Baldwin * thread owns an rlock. 14673f08bd8bSJohn Baldwin */ 14683f08bd8bSJohn Baldwin void 146919d41533SAttilio Rao __rw_assert(const volatile uintptr_t *c, int what, const char *file, int line) 14703f08bd8bSJohn Baldwin { 147119d41533SAttilio Rao const struct rwlock *rw; 14723f08bd8bSJohn Baldwin 1473d54474e6SEric van Gyzen if (SCHEDULER_STOPPED()) 14743f08bd8bSJohn Baldwin return; 147519d41533SAttilio Rao 147619d41533SAttilio Rao rw = rwlock2rw(c); 147719d41533SAttilio Rao 14783f08bd8bSJohn Baldwin switch (what) { 14793f08bd8bSJohn Baldwin case RA_LOCKED: 1480f08945a7SAttilio Rao case RA_LOCKED | RA_RECURSED: 1481f08945a7SAttilio Rao case RA_LOCKED | RA_NOTRECURSED: 14823f08bd8bSJohn Baldwin case RA_RLOCKED: 148395d28652SJohn Baldwin case RA_RLOCKED | RA_RECURSED: 148495d28652SJohn Baldwin case RA_RLOCKED | RA_NOTRECURSED: 14853f08bd8bSJohn Baldwin #ifdef WITNESS 1486aa89d8cdSJohn Baldwin witness_assert(&rw->lock_object, what, file, line); 14873f08bd8bSJohn Baldwin #else 14883f08bd8bSJohn Baldwin /* 14893f08bd8bSJohn Baldwin * If some other thread has a write lock or we have one 14903f08bd8bSJohn Baldwin * and are asserting a read lock, fail. Also, if no one 14913f08bd8bSJohn Baldwin * has a lock at all, fail. 14923f08bd8bSJohn Baldwin */ 1493019a2f40SScott Long if (rw->rw_lock == RW_UNLOCKED || 149495d28652SJohn Baldwin (!(rw->rw_lock & RW_LOCK_READ) && (what & RA_RLOCKED || 149538bf165fSJohn Baldwin rw_wowner(rw) != curthread))) 14963f08bd8bSJohn Baldwin panic("Lock %s not %slocked @ %s:%d\n", 149795d28652SJohn Baldwin rw->lock_object.lo_name, (what & RA_RLOCKED) ? 14983f08bd8bSJohn Baldwin "read " : "", file, line); 1499f08945a7SAttilio Rao 150095d28652SJohn Baldwin if (!(rw->rw_lock & RW_LOCK_READ) && !(what & RA_RLOCKED)) { 1501f08945a7SAttilio Rao if (rw_recursed(rw)) { 1502f08945a7SAttilio Rao if (what & RA_NOTRECURSED) 1503f08945a7SAttilio Rao panic("Lock %s recursed @ %s:%d\n", 1504f08945a7SAttilio Rao rw->lock_object.lo_name, file, 1505f08945a7SAttilio Rao line); 1506f08945a7SAttilio Rao } else if (what & RA_RECURSED) 1507f08945a7SAttilio Rao panic("Lock %s not recursed @ %s:%d\n", 1508f08945a7SAttilio Rao rw->lock_object.lo_name, file, line); 1509f08945a7SAttilio Rao } 15103f08bd8bSJohn Baldwin #endif 15113f08bd8bSJohn Baldwin break; 15123f08bd8bSJohn Baldwin case RA_WLOCKED: 1513f08945a7SAttilio Rao case RA_WLOCKED | RA_RECURSED: 1514f08945a7SAttilio Rao case RA_WLOCKED | RA_NOTRECURSED: 151538bf165fSJohn Baldwin if (rw_wowner(rw) != curthread) 15163f08bd8bSJohn Baldwin panic("Lock %s not exclusively locked @ %s:%d\n", 1517aa89d8cdSJohn Baldwin rw->lock_object.lo_name, file, line); 1518f08945a7SAttilio Rao if (rw_recursed(rw)) { 1519f08945a7SAttilio Rao if (what & RA_NOTRECURSED) 1520f08945a7SAttilio Rao panic("Lock %s recursed @ %s:%d\n", 1521f08945a7SAttilio Rao rw->lock_object.lo_name, file, line); 1522f08945a7SAttilio Rao } else if (what & RA_RECURSED) 1523f08945a7SAttilio Rao panic("Lock %s not recursed @ %s:%d\n", 1524f08945a7SAttilio Rao rw->lock_object.lo_name, file, line); 15253f08bd8bSJohn Baldwin break; 15263f08bd8bSJohn Baldwin case RA_UNLOCKED: 15273f08bd8bSJohn Baldwin #ifdef WITNESS 1528aa89d8cdSJohn Baldwin witness_assert(&rw->lock_object, what, file, line); 15293f08bd8bSJohn Baldwin #else 15303f08bd8bSJohn Baldwin /* 15313f08bd8bSJohn Baldwin * If we hold a write lock fail. We can't reliably check 15323f08bd8bSJohn Baldwin * to see if we hold a read lock or not. 15333f08bd8bSJohn Baldwin */ 153438bf165fSJohn Baldwin if (rw_wowner(rw) == curthread) 15353f08bd8bSJohn Baldwin panic("Lock %s exclusively locked @ %s:%d\n", 1536aa89d8cdSJohn Baldwin rw->lock_object.lo_name, file, line); 15373f08bd8bSJohn Baldwin #endif 15383f08bd8bSJohn Baldwin break; 15393f08bd8bSJohn Baldwin default: 15403f08bd8bSJohn Baldwin panic("Unknown rw lock assertion: %d @ %s:%d", what, file, 15413f08bd8bSJohn Baldwin line); 15423f08bd8bSJohn Baldwin } 15433f08bd8bSJohn Baldwin } 15443f08bd8bSJohn Baldwin #endif /* INVARIANT_SUPPORT */ 15453f08bd8bSJohn Baldwin 15463f08bd8bSJohn Baldwin #ifdef DDB 1547aaef18e6SGleb Smirnoff static void 1548d576deedSPawel Jakub Dawidek db_show_rwlock(const struct lock_object *lock) 15493f08bd8bSJohn Baldwin { 1550d576deedSPawel Jakub Dawidek const struct rwlock *rw; 15513f08bd8bSJohn Baldwin struct thread *td; 15523f08bd8bSJohn Baldwin 1553d576deedSPawel Jakub Dawidek rw = (const struct rwlock *)lock; 15543f08bd8bSJohn Baldwin 15553f08bd8bSJohn Baldwin db_printf(" state: "); 15563f08bd8bSJohn Baldwin if (rw->rw_lock == RW_UNLOCKED) 15573f08bd8bSJohn Baldwin db_printf("UNLOCKED\n"); 15580026c92cSJohn Baldwin else if (rw->rw_lock == RW_DESTROYED) { 15590026c92cSJohn Baldwin db_printf("DESTROYED\n"); 15600026c92cSJohn Baldwin return; 15610026c92cSJohn Baldwin } else if (rw->rw_lock & RW_LOCK_READ) 1562c1f2a533SJohn Baldwin db_printf("RLOCK: %ju locks\n", 1563c1f2a533SJohn Baldwin (uintmax_t)(RW_READERS(rw->rw_lock))); 15643f08bd8bSJohn Baldwin else { 156538bf165fSJohn Baldwin td = rw_wowner(rw); 15663f08bd8bSJohn Baldwin db_printf("WLOCK: %p (tid %d, pid %d, \"%s\")\n", td, 1567431f8906SJulian Elischer td->td_tid, td->td_proc->p_pid, td->td_name); 1568f08945a7SAttilio Rao if (rw_recursed(rw)) 1569f08945a7SAttilio Rao db_printf(" recursed: %u\n", rw->rw_recurse); 15703f08bd8bSJohn Baldwin } 15713f08bd8bSJohn Baldwin db_printf(" waiters: "); 15723f08bd8bSJohn Baldwin switch (rw->rw_lock & (RW_LOCK_READ_WAITERS | RW_LOCK_WRITE_WAITERS)) { 15733f08bd8bSJohn Baldwin case RW_LOCK_READ_WAITERS: 15743f08bd8bSJohn Baldwin db_printf("readers\n"); 15753f08bd8bSJohn Baldwin break; 15763f08bd8bSJohn Baldwin case RW_LOCK_WRITE_WAITERS: 15773f08bd8bSJohn Baldwin db_printf("writers\n"); 15783f08bd8bSJohn Baldwin break; 15793f08bd8bSJohn Baldwin case RW_LOCK_READ_WAITERS | RW_LOCK_WRITE_WAITERS: 15804b493b1aSJohn Baldwin db_printf("readers and writers\n"); 15813f08bd8bSJohn Baldwin break; 15823f08bd8bSJohn Baldwin default: 15833f08bd8bSJohn Baldwin db_printf("none\n"); 15843f08bd8bSJohn Baldwin break; 15853f08bd8bSJohn Baldwin } 15863f08bd8bSJohn Baldwin } 15873f08bd8bSJohn Baldwin 15883f08bd8bSJohn Baldwin #endif 1589