diff --git a/sys/kern/kern_mutex.c b/sys/kern/kern_mutex.c index 2ca1781..af0db3a 100644 --- a/sys/kern/kern_mutex.c +++ b/sys/kern/kern_mutex.c @@ -142,30 +142,15 @@ static SYSCTL_NODE(_debug, OID_AUTO, mtx, CTLFLAG_RD, NULL, "mtx debugging"); static struct lock_delay_config mtx_delay = { .initial = 1000, - .step = 500, - .min = 100, .max = 5000, }; SYSCTL_INT(_debug_mtx, OID_AUTO, delay_initial, CTLFLAG_RW, &mtx_delay.initial, 0, ""); -SYSCTL_INT(_debug_mtx, OID_AUTO, delay_step, CTLFLAG_RW, &mtx_delay.step, - 0, ""); -SYSCTL_INT(_debug_mtx, OID_AUTO, delay_min, CTLFLAG_RW, &mtx_delay.min, - 0, ""); SYSCTL_INT(_debug_mtx, OID_AUTO, delay_max, CTLFLAG_RW, &mtx_delay.max, 0, ""); -static void -mtx_delay_sysinit(void *dummy) -{ - - mtx_delay.initial = mp_ncpus * 25; - mtx_delay.step = (mp_ncpus * 25) / 2; - mtx_delay.min = mp_ncpus * 5; - mtx_delay.max = mp_ncpus * 25 * 10; -} -LOCK_DELAY_SYSINIT(mtx_delay_sysinit); +LOCK_DELAY_SYSINIT_DEFAULT(mtx_delay); #endif static SYSCTL_NODE(_debug, OID_AUTO, mtx_spin, CTLFLAG_RD, NULL, @@ -173,30 +158,15 @@ static SYSCTL_NODE(_debug, OID_AUTO, mtx_spin, CTLFLAG_RD, NULL, static struct lock_delay_config mtx_spin_delay = { .initial = 1000, - .step = 500, - .min = 100, .max = 5000, }; SYSCTL_INT(_debug_mtx_spin, OID_AUTO, delay_initial, CTLFLAG_RW, &mtx_spin_delay.initial, 0, ""); -SYSCTL_INT(_debug_mtx_spin, OID_AUTO, delay_step, CTLFLAG_RW, &mtx_spin_delay.step, - 0, ""); -SYSCTL_INT(_debug_mtx_spin, OID_AUTO, delay_min, CTLFLAG_RW, &mtx_spin_delay.min, - 0, ""); SYSCTL_INT(_debug_mtx_spin, OID_AUTO, delay_max, CTLFLAG_RW, &mtx_spin_delay.max, 0, ""); -static void -mtx_spin_delay_sysinit(void *dummy) -{ - - mtx_spin_delay.initial = mp_ncpus * 25; - mtx_spin_delay.step = (mp_ncpus * 25) / 2; - mtx_spin_delay.min = mp_ncpus * 5; - mtx_spin_delay.max = mp_ncpus * 25 * 10; -} -LOCK_DELAY_SYSINIT(mtx_spin_delay_sysinit); +LOCK_DELAY_SYSINIT_DEFAULT(mtx_spin_delay); /* * System-wide mutexes @@ -455,12 +425,11 @@ _mtx_trylock_flags_(volatile uintptr_t *c, int opts, const char *file, int line) * sleep waiting for it), or if we need to recurse on it. */ void -__mtx_lock_sleep(volatile uintptr_t *c, uintptr_t tid, int opts, +__mtx_lock_sleep(volatile uintptr_t *c, uintptr_t v, uintptr_t tid, int opts, const char *file, int line) { struct mtx *m; struct turnstile *ts; - uintptr_t v; #ifdef ADAPTIVE_MUTEXES volatile struct thread *owner; #endif @@ -489,7 +458,6 @@ __mtx_lock_sleep(volatile uintptr_t *c, uintptr_t tid, int opts, lock_delay_arg_init(&lda, NULL); #endif m = mtxlock2mtx(c); - v = MTX_READ_VALUE(m); if (__predict_false(lv_mtx_owner(v) == (struct thread *)tid)) { KASSERT((m->lock_object.lo_flags & LO_RECURSABLE) != 0 || @@ -520,9 +488,8 @@ __mtx_lock_sleep(volatile uintptr_t *c, uintptr_t tid, int opts, for (;;) { if (v == MTX_UNOWNED) { - if (_mtx_obtain_lock(m, tid)) + if (_mtx_obtain_lock_fetch(m, &v, tid)) break; - v = MTX_READ_VALUE(m); continue; } #ifdef KDTRACE_HOOKS @@ -674,12 +641,11 @@ _mtx_lock_spin_failed(struct mtx *m) * is handled inline. */ void -_mtx_lock_spin_cookie(volatile uintptr_t *c, uintptr_t tid, int opts, - const char *file, int line) +_mtx_lock_spin_cookie(volatile uintptr_t *c, uintptr_t v, uintptr_t tid, + int opts, const char *file, int line) { struct mtx *m; struct lock_delay_arg lda; - uintptr_t v; #ifdef LOCK_PROFILING int contested = 0; uint64_t waittime = 0; @@ -706,12 +672,10 @@ _mtx_lock_spin_cookie(volatile uintptr_t *c, uintptr_t tid, int opts, #ifdef KDTRACE_HOOKS spin_time -= lockstat_nsecs(&m->lock_object); #endif - v = MTX_READ_VALUE(m); for (;;) { if (v == MTX_UNOWNED) { - if (_mtx_obtain_lock(m, tid)) + if (_mtx_obtain_lock_fetch(m, &v, tid)) break; - v = MTX_READ_VALUE(m); continue; } /* Give interrupts a chance while we spin. */ @@ -796,14 +760,11 @@ retry: m->lock_object.lo_name, file, line)); WITNESS_CHECKORDER(&m->lock_object, opts | LOP_NEWORDER | LOP_EXCLUSIVE, file, line, NULL); - v = MTX_READ_VALUE(m); for (;;) { - if (v == MTX_UNOWNED) { - if (_mtx_obtain_lock(m, tid)) - break; - v = MTX_READ_VALUE(m); + if (_mtx_obtain_lock_fetch(m, &v, tid)) + break; + if (v == MTX_UNOWNED) continue; - } if (v == tid) { m->mtx_recurse++; break; @@ -892,7 +853,8 @@ thread_lock_set(struct thread *td, struct mtx *new) * need to wake up a blocked thread). */ void -__mtx_unlock_sleep(volatile uintptr_t *c, int opts, const char *file, int line) +__mtx_unlock_sleep(volatile uintptr_t *c, uintptr_t v, int opts, + const char *file, int line) { struct mtx *m; struct turnstile *ts; diff --git a/sys/kern/kern_rwlock.c b/sys/kern/kern_rwlock.c index ea2f969..b8211ad 100644 --- a/sys/kern/kern_rwlock.c +++ b/sys/kern/kern_rwlock.c @@ -55,6 +55,11 @@ __FBSDID("$FreeBSD$"); #define ADAPTIVE_RWLOCKS #endif +SDT_PROVIDER_DECLARE(lock); +SDT_PROBE_DEFINE6(lock, rwlock, obtain, read, "u_int", "u_int", "u_int", "u_int", "u_int", "u_int"); +SDT_PROBE_DEFINE6(lock, rwlock, obtain, write, "u_int", "u_int", "u_int", "u_int", "u_int", "u_int"); + + #ifdef HWPMC_HOOKS #include PMC_SOFT_DECLARE( , , lock, failed); @@ -102,39 +107,64 @@ SYSCTL_INT(_debug_rwlock, OID_AUTO, loops, CTLFLAG_RW, &rowner_loops, 0, ""); static struct lock_delay_config rw_delay = { .initial = 1000, - .step = 500, - .min = 100, .max = 5000, }; SYSCTL_INT(_debug_rwlock, OID_AUTO, delay_initial, CTLFLAG_RW, &rw_delay.initial, 0, ""); -SYSCTL_INT(_debug_rwlock, OID_AUTO, delay_step, CTLFLAG_RW, &rw_delay.step, +SYSCTL_INT(_debug_rwlock, OID_AUTO, delay_max, CTLFLAG_RW, &rw_delay.max, 0, ""); -SYSCTL_INT(_debug_rwlock, OID_AUTO, delay_min, CTLFLAG_RW, &rw_delay.min, + +LOCK_DELAY_SYSINIT_DEFAULT(rw_delay); + +static int rw_delay_reader_enable = 0; +SYSCTL_INT(_debug_rwlock, OID_AUTO, rw_delay_reader_enable, CTLFLAG_RW, &rw_delay_reader_enable, 0, ""); + +static struct lock_delay_config rw_reader_delay = { + .initial = 1000, + .max = 5000, + .cap = 20000, +}; + +SYSCTL_INT(_debug_rwlock, OID_AUTO, reader_delay_initial, CTLFLAG_RW, &rw_reader_delay.initial, 0, ""); -SYSCTL_INT(_debug_rwlock, OID_AUTO, delay_max, CTLFLAG_RW, &rw_delay.max, +SYSCTL_INT(_debug_rwlock, OID_AUTO, reader_delay_max, CTLFLAG_RW, &rw_reader_delay.max, + 0, ""); +SYSCTL_INT(_debug_rwlock, OID_AUTO, reader_delay_cap, CTLFLAG_RW, &rw_reader_delay.cap, 0, ""); -static void -rw_delay_sysinit(void *dummy) -{ +LOCK_DELAY_CAP_SYSINIT_DEFAULT(rw_reader_delay); + +static int rw_delay_writer_enable = 0; +SYSCTL_INT(_debug_rwlock, OID_AUTO, rw_delay_writer_enable, CTLFLAG_RW, &rw_delay_writer_enable, 0, ""); + +static struct lock_delay_config rw_writer_delay = { + .initial = 1000, + .max = 5000, + .cap = 20000, +}; + +SYSCTL_INT(_debug_rwlock, OID_AUTO, writer_delay_initial, CTLFLAG_RW, &rw_writer_delay.initial, + 0, ""); +SYSCTL_INT(_debug_rwlock, OID_AUTO, writer_delay_max, CTLFLAG_RW, &rw_writer_delay.max, + 0, ""); +SYSCTL_INT(_debug_rwlock, OID_AUTO, writer_delay_cap, CTLFLAG_RW, &rw_writer_delay.cap, + 0, ""); + +LOCK_DELAY_CAP_SYSINIT_DEFAULT(rw_writer_delay); - rw_delay.initial = mp_ncpus * 25; - rw_delay.step = (mp_ncpus * 25) / 2; - rw_delay.min = mp_ncpus * 5; - rw_delay.max = mp_ncpus * 25 * 10; -} -LOCK_DELAY_SYSINIT(rw_delay_sysinit); #endif /* * Return a pointer to the owning thread if the lock is write-locked or * NULL if the lock is unlocked or read-locked. */ -#define rw_wowner(rw) \ - ((rw)->rw_lock & RW_LOCK_READ ? NULL : \ - (struct thread *)RW_OWNER((rw)->rw_lock)) + +#define lv_rw_wowner(v) \ + ((v) & RW_LOCK_READ ? NULL : \ + (struct thread *)RW_OWNER((v))) + +#define rw_wowner(rw) lv_rw_wowner(RW_READ_VALUE(rw)) /* * Returns if a write owner is recursed. Write ownership is not assured @@ -385,6 +415,7 @@ __rw_rlock(volatile uintptr_t *c, const char *file, int line) uintptr_t v; #if defined(ADAPTIVE_RWLOCKS) || defined(KDTRACE_HOOKS) struct lock_delay_arg lda; + struct lock_delay_arg lda_reader; #endif #ifdef KDTRACE_HOOKS uintptr_t state; @@ -392,12 +423,16 @@ __rw_rlock(volatile uintptr_t *c, const char *file, int line) int64_t sleep_time = 0; int64_t all_time = 0; #endif + int spin_loops = 0; + int given_up = 0; + int reached_limit = 0; if (SCHEDULER_STOPPED()) return; #if defined(ADAPTIVE_RWLOCKS) lock_delay_arg_init(&lda, &rw_delay); + lock_delay_arg_init(&lda_reader, &rw_reader_delay); #elif defined(KDTRACE_HOOKS) lock_delay_arg_init(&lda, NULL); #endif @@ -415,7 +450,10 @@ __rw_rlock(volatile uintptr_t *c, const char *file, int line) #ifdef KDTRACE_HOOKS all_time -= lockstat_nsecs(&rw->lock_object); - state = rw->rw_lock; +#endif + v = RW_READ_VALUE(rw); +#ifdef KDTRACE_HOOKS + state = v; #endif for (;;) { /* @@ -428,14 +466,13 @@ __rw_rlock(volatile uintptr_t *c, const char *file, int line) * completely unlocked rwlock since such a lock is encoded * as a read lock with no waiters. */ - v = rw->rw_lock; if (RW_CAN_READ(v)) { /* * The RW_LOCK_READ_WAITERS flag should only be set * if the lock has been unlocked and write waiters * were present. */ - if (atomic_cmpset_acq_ptr(&rw->rw_lock, v, + if (atomic_fcmpset_long(&rw->rw_lock, &v, v + RW_ONE_READER)) { if (LOCK_LOG_TEST(&rw->lock_object, 0)) CTR4(KTR_LOCK, @@ -471,31 +508,52 @@ __rw_rlock(volatile uintptr_t *c, const char *file, int line) KTR_STATE1(KTR_SCHED, "thread", sched_tdname(curthread), "spinning", "lockname:\"%s\"", rw->lock_object.lo_name); - while ((struct thread*)RW_OWNER(rw->rw_lock) == - owner && TD_IS_RUNNING(owner)) + do { lock_delay(&lda); + v = RW_READ_VALUE(rw); + owner = lv_rw_wowner(v); + } while (owner != NULL && TD_IS_RUNNING(owner)); KTR_STATE0(KTR_SCHED, "thread", sched_tdname(curthread), "running"); continue; } + } else if (rw_delay_reader_enable) { + int reached_cap = 0; + for (;;) { + if (lock_delay_capped(&lda_reader) == 0) { + reached_cap = 1; + given_up++; + break; + } + v = RW_READ_VALUE(rw); + if ((v & RW_LOCK_READ) == 0 || RW_CAN_READ(v)) + break; + } + if (!reached_cap) + continue; } else if (spintries < rowner_retries) { spintries++; KTR_STATE1(KTR_SCHED, "thread", sched_tdname(curthread), "spinning", "lockname:\"%s\"", rw->lock_object.lo_name); for (i = 0; i < rowner_loops; i++) { - v = rw->rw_lock; + v = RW_READ_VALUE(rw); if ((v & RW_LOCK_READ) == 0 || RW_CAN_READ(v)) break; cpu_spinwait(); } + v = RW_READ_VALUE(rw); + spin_loops += i; #ifdef KDTRACE_HOOKS - lda.spin_cnt += rowner_loops - i; + lda.spin_cnt += i; #endif KTR_STATE0(KTR_SCHED, "thread", sched_tdname(curthread), "running"); if (i != rowner_loops) continue; + reached_limit++; + } else { + given_up++; } #endif @@ -511,7 +569,7 @@ __rw_rlock(volatile uintptr_t *c, const char *file, int line) * The lock might have been released while we spun, so * recheck its state and restart the loop if needed. */ - v = rw->rw_lock; + v = RW_READ_VALUE(rw); if (RW_CAN_READ(v)) { turnstile_cancel(ts); continue; @@ -549,6 +607,7 @@ __rw_rlock(volatile uintptr_t *c, const char *file, int line) if (!atomic_cmpset_ptr(&rw->rw_lock, v, v | RW_LOCK_READ_WAITERS)) { turnstile_cancel(ts); + v = RW_READ_VALUE(rw); continue; } if (LOCK_LOG_TEST(&rw->lock_object, 0)) @@ -574,7 +633,9 @@ __rw_rlock(volatile uintptr_t *c, const char *file, int line) if (LOCK_LOG_TEST(&rw->lock_object, 0)) CTR2(KTR_LOCK, "%s: %p resuming from turnstile", __func__, rw); + v = RW_READ_VALUE(rw); } + SDT_PROBE6(lock, rwlock, obtain, read, sleep_cnt, lda.spin_cnt, spin_loops, spintries, given_up, reached_limit); #ifdef KDTRACE_HOOKS all_time += lockstat_nsecs(&rw->lock_object); if (sleep_time) @@ -657,15 +718,14 @@ _rw_runlock_cookie(volatile uintptr_t *c, const char *file, int line) LOCK_LOG_LOCK("RUNLOCK", &rw->lock_object, 0, 0, file, line); /* TODO: drop "owner of record" here. */ - + x = RW_READ_VALUE(rw); for (;;) { /* * See if there is more than one read lock held. If so, * just drop one and return. */ - x = rw->rw_lock; if (RW_READERS(x) > 1) { - if (atomic_cmpset_rel_ptr(&rw->rw_lock, x, + if (atomic_fcmpset_rel_ptr(&rw->rw_lock, &x, x - RW_ONE_READER)) { if (LOCK_LOG_TEST(&rw->lock_object, 0)) CTR4(KTR_LOCK, @@ -683,7 +743,7 @@ _rw_runlock_cookie(volatile uintptr_t *c, const char *file, int line) if (!(x & RW_LOCK_WAITERS)) { MPASS((x & ~RW_LOCK_WRITE_SPINNER) == RW_READERS_LOCK(1)); - if (atomic_cmpset_rel_ptr(&rw->rw_lock, x, + if (atomic_fcmpset_rel_ptr(&rw->rw_lock, &x, RW_UNLOCKED)) { if (LOCK_LOG_TEST(&rw->lock_object, 0)) CTR2(KTR_LOCK, "%s: %p last succeeded", @@ -725,6 +785,7 @@ _rw_runlock_cookie(volatile uintptr_t *c, const char *file, int line) if (!atomic_cmpset_rel_ptr(&rw->rw_lock, RW_READERS_LOCK(1) | v, x)) { turnstile_chain_unlock(&rw->lock_object); + x = RW_READ_VALUE(rw); continue; } if (LOCK_LOG_TEST(&rw->lock_object, 0)) @@ -756,8 +817,8 @@ _rw_runlock_cookie(volatile uintptr_t *c, const char *file, int line) * read or write lock. */ void -__rw_wlock_hard(volatile uintptr_t *c, uintptr_t tid, const char *file, - int line) +__rw_wlock_hard(volatile uintptr_t *c, uintptr_t v, uintptr_t tid, + const char *file, int line) { struct rwlock *rw; struct turnstile *ts; @@ -766,13 +827,14 @@ __rw_wlock_hard(volatile uintptr_t *c, uintptr_t tid, const char *file, int spintries = 0; int i; #endif - uintptr_t v, x; + uintptr_t x; #ifdef LOCK_PROFILING uint64_t waittime = 0; int contested = 0; #endif #if defined(ADAPTIVE_RWLOCKS) || defined(KDTRACE_HOOKS) struct lock_delay_arg lda; + struct lock_delay_arg lda_writer; #endif #ifdef KDTRACE_HOOKS uintptr_t state; @@ -780,18 +842,23 @@ __rw_wlock_hard(volatile uintptr_t *c, uintptr_t tid, const char *file, int64_t sleep_time = 0; int64_t all_time = 0; #endif + int totaltries = 0; + int spin_loops = 0; + int given_up = 0; + int reached_limit = 0; if (SCHEDULER_STOPPED()) return; #if defined(ADAPTIVE_RWLOCKS) lock_delay_arg_init(&lda, &rw_delay); + lock_delay_arg_init(&lda_writer, &rw_writer_delay); #elif defined(KDTRACE_HOOKS) lock_delay_arg_init(&lda, NULL); #endif rw = rwlock2rw(c); - if (rw_wlocked(rw)) { + if (__predict_false(lv_rw_wowner(v) == (struct thread *)tid)) { KASSERT(rw->lock_object.lo_flags & LO_RECURSABLE, ("%s: recursing but non-recursive rw %s @ %s:%d\n", __func__, rw->lock_object.lo_name, file, line)); @@ -807,11 +874,14 @@ __rw_wlock_hard(volatile uintptr_t *c, uintptr_t tid, const char *file, #ifdef KDTRACE_HOOKS all_time -= lockstat_nsecs(&rw->lock_object); - state = rw->rw_lock; + state = v; #endif for (;;) { - if (rw->rw_lock == RW_UNLOCKED && _rw_write_lock(rw, tid)) - break; + if (v == RW_UNLOCKED) { + if (_rw_write_lock_fetch(rw, &v, tid)) + break; + continue; + } #ifdef KDTRACE_HOOKS lda.spin_cnt++; #endif @@ -826,8 +896,7 @@ __rw_wlock_hard(volatile uintptr_t *c, uintptr_t tid, const char *file, * running on another CPU, spin until the owner stops * running or the state of the lock changes. */ - v = rw->rw_lock; - owner = (struct thread *)RW_OWNER(v); + owner = lv_rw_wowner(v); if (!(v & RW_LOCK_READ) && TD_IS_RUNNING(owner)) { if (LOCK_LOG_TEST(&rw->lock_object, 0)) CTR3(KTR_LOCK, "%s: spinning on %p held by %p", @@ -835,41 +904,72 @@ __rw_wlock_hard(volatile uintptr_t *c, uintptr_t tid, const char *file, KTR_STATE1(KTR_SCHED, "thread", sched_tdname(curthread), "spinning", "lockname:\"%s\"", rw->lock_object.lo_name); - while ((struct thread*)RW_OWNER(rw->rw_lock) == owner && - TD_IS_RUNNING(owner)) + do { lock_delay(&lda); + v = RW_READ_VALUE(rw); + owner = lv_rw_wowner(v); + } while (owner != NULL && TD_IS_RUNNING(owner)); KTR_STATE0(KTR_SCHED, "thread", sched_tdname(curthread), "running"); continue; } - if ((v & RW_LOCK_READ) && RW_READERS(v) && - spintries < rowner_retries) { - if (!(v & RW_LOCK_WRITE_SPINNER)) { - if (!atomic_cmpset_ptr(&rw->rw_lock, v, - v | RW_LOCK_WRITE_SPINNER)) { + if ((v & RW_LOCK_READ) && RW_READERS(v)) { + if (rw_delay_writer_enable) { + if (!(v & RW_LOCK_WRITE_SPINNER)) { + if (!atomic_cmpset_ptr(&rw->rw_lock, v, + v | RW_LOCK_WRITE_SPINNER)) { + v = RW_READ_VALUE(rw); + continue; + } + } + int reached_cap = 0; + for (;;) { + if (lock_delay_capped(&lda_writer) == 0) { + reached_cap = 1; + given_up++; + break; + } + v = RW_READ_VALUE(rw); + if ((v & RW_LOCK_WRITE_SPINNER) == 0) + break; + } + if (!reached_cap) continue; + } else if (spintries < rowner_retries) { + if (!(v & RW_LOCK_WRITE_SPINNER)) { + if (!atomic_cmpset_ptr(&rw->rw_lock, v, + v | RW_LOCK_WRITE_SPINNER)) { + v = RW_READ_VALUE(rw); + continue; + } } - } - spintries++; - KTR_STATE1(KTR_SCHED, "thread", sched_tdname(curthread), - "spinning", "lockname:\"%s\"", - rw->lock_object.lo_name); - for (i = 0; i < rowner_loops; i++) { - if ((rw->rw_lock & RW_LOCK_WRITE_SPINNER) == 0) - break; - cpu_spinwait(); - } - KTR_STATE0(KTR_SCHED, "thread", sched_tdname(curthread), - "running"); + spintries++; + KTR_STATE1(KTR_SCHED, "thread", sched_tdname(curthread), + "spinning", "lockname:\"%s\"", + rw->lock_object.lo_name); + for (i = 0; i < rowner_loops; i++) { + if ((rw->rw_lock & RW_LOCK_WRITE_SPINNER) == 0) + break; + cpu_spinwait(); + } + KTR_STATE0(KTR_SCHED, "thread", sched_tdname(curthread), + "running"); + v = RW_READ_VALUE(rw); + totaltries++; + spin_loops += i; #ifdef KDTRACE_HOOKS - lda.spin_cnt += rowner_loops - i; + lda.spin_cnt += i; #endif - if (i != rowner_loops) - continue; + if (i != rowner_loops) + continue; + reached_limit++; + if (spintries < rowner_retries) + given_up++; + } } #endif ts = turnstile_trywait(&rw->lock_object); - v = rw->rw_lock; + v = RW_READ_VALUE(rw); #ifdef ADAPTIVE_RWLOCKS /* @@ -905,6 +1005,7 @@ __rw_wlock_hard(volatile uintptr_t *c, uintptr_t tid, const char *file, break; } turnstile_cancel(ts); + v = RW_READ_VALUE(rw); continue; } /* @@ -916,6 +1017,7 @@ __rw_wlock_hard(volatile uintptr_t *c, uintptr_t tid, const char *file, if (!atomic_cmpset_ptr(&rw->rw_lock, v, v | RW_LOCK_WRITE_WAITERS)) { turnstile_cancel(ts); + v = RW_READ_VALUE(rw); continue; } if (LOCK_LOG_TEST(&rw->lock_object, 0)) @@ -943,7 +1045,9 @@ __rw_wlock_hard(volatile uintptr_t *c, uintptr_t tid, const char *file, #ifdef ADAPTIVE_RWLOCKS spintries = 0; #endif + v = RW_READ_VALUE(rw); } + SDT_PROBE6(lock, rwlock, obtain, write, sleep_cnt, lda.spin_cnt, spin_loops, totaltries, given_up, reached_limit); #ifdef KDTRACE_HOOKS all_time += lockstat_nsecs(&rw->lock_object); if (sleep_time) diff --git a/sys/kern/kern_sx.c b/sys/kern/kern_sx.c index 0036734..ec7c694 100644 --- a/sys/kern/kern_sx.c +++ b/sys/kern/kern_sx.c @@ -71,6 +71,10 @@ __FBSDID("$FreeBSD$"); CTASSERT((SX_NOADAPTIVE & LO_CLASSFLAGS) == SX_NOADAPTIVE); +SDT_PROVIDER_DECLARE(lock); +SDT_PROBE_DEFINE6(lock, sx, obtain, read, "u_int", "u_int", "u_int", "u_int", "u_int", "u_int"); +SDT_PROBE_DEFINE6(lock, sx, obtain, write, "u_int", "u_int", "u_int", "u_int", "u_int", "u_int"); + #ifdef HWPMC_HOOKS #include PMC_SOFT_DECLARE( , , lock, failed); @@ -150,30 +154,15 @@ SYSCTL_UINT(_debug_sx, OID_AUTO, loops, CTLFLAG_RW, &asx_loops, 0, ""); static struct lock_delay_config sx_delay = { .initial = 1000, - .step = 500, - .min = 100, .max = 5000, }; SYSCTL_INT(_debug_sx, OID_AUTO, delay_initial, CTLFLAG_RW, &sx_delay.initial, 0, ""); -SYSCTL_INT(_debug_sx, OID_AUTO, delay_step, CTLFLAG_RW, &sx_delay.step, - 0, ""); -SYSCTL_INT(_debug_sx, OID_AUTO, delay_min, CTLFLAG_RW, &sx_delay.min, - 0, ""); SYSCTL_INT(_debug_sx, OID_AUTO, delay_max, CTLFLAG_RW, &sx_delay.max, 0, ""); -static void -sx_delay_sysinit(void *dummy) -{ - - sx_delay.initial = mp_ncpus * 25; - sx_delay.step = (mp_ncpus * 25) / 2; - sx_delay.min = mp_ncpus * 5; - sx_delay.max = mp_ncpus * 25 * 10; -} -LOCK_DELAY_SYSINIT(sx_delay_sysinit); +LOCK_DELAY_SYSINIT_DEFAULT(sx_delay); #endif void @@ -530,15 +519,14 @@ sx_downgrade_(struct sx *sx, const char *file, int line) * accessible from at least sx.h. */ int -_sx_xlock_hard(struct sx *sx, uintptr_t tid, int opts, const char *file, - int line) +_sx_xlock_hard(struct sx *sx, uintptr_t x, uintptr_t tid, int opts, + const char *file, int line) { GIANT_DECLARE; #ifdef ADAPTIVE_SX volatile struct thread *owner; u_int i, spintries = 0; #endif - uintptr_t x; #ifdef LOCK_PROFILING uint64_t waittime = 0; int contested = 0; @@ -553,6 +541,9 @@ _sx_xlock_hard(struct sx *sx, uintptr_t tid, int opts, const char *file, int64_t sleep_time = 0; int64_t all_time = 0; #endif + int spin_loops = 0; + int given_up = 0; + int reached_limit = 0; if (SCHEDULER_STOPPED()) return (0); @@ -564,7 +555,7 @@ _sx_xlock_hard(struct sx *sx, uintptr_t tid, int opts, const char *file, #endif /* If we already hold an exclusive lock, then recurse. */ - if (sx_xlocked(sx)) { + if (__predict_false(lv_sx_owner(x) == (struct thread *)tid)) { KASSERT((sx->lock_object.lo_flags & LO_RECURSABLE) != 0, ("_sx_xlock_hard: recursed on non-recursive sx %s @ %s:%d\n", sx->lock_object.lo_name, file, line)); @@ -581,12 +572,14 @@ _sx_xlock_hard(struct sx *sx, uintptr_t tid, int opts, const char *file, #ifdef KDTRACE_HOOKS all_time -= lockstat_nsecs(&sx->lock_object); - state = sx->sx_lock; + state = x; #endif for (;;) { - if (sx->sx_lock == SX_LOCK_UNLOCKED && - atomic_cmpset_acq_ptr(&sx->sx_lock, SX_LOCK_UNLOCKED, tid)) - break; + if (x == SX_LOCK_UNLOCKED) { + if (atomic_fcmpset_acq_ptr(&sx->sx_lock, &x, tid)) + break; + continue; + } #ifdef KDTRACE_HOOKS lda.spin_cnt++; #endif @@ -601,11 +594,9 @@ _sx_xlock_hard(struct sx *sx, uintptr_t tid, int opts, const char *file, * running on another CPU, spin until the owner stops * running or the state of the lock changes. */ - x = sx->sx_lock; if ((sx->lock_object.lo_flags & SX_NOADAPTIVE) == 0) { if ((x & SX_LOCK_SHARED) == 0) { - x = SX_OWNER(x); - owner = (struct thread *)x; + owner = lv_sx_owner(x); if (TD_IS_RUNNING(owner)) { if (LOCK_LOG_TEST(&sx->lock_object, 0)) CTR3(KTR_LOCK, @@ -616,9 +607,12 @@ _sx_xlock_hard(struct sx *sx, uintptr_t tid, int opts, const char *file, "lockname:\"%s\"", sx->lock_object.lo_name); GIANT_SAVE(); - while (SX_OWNER(sx->sx_lock) == x && - TD_IS_RUNNING(owner)) + do { lock_delay(&lda); + x = SX_READ_VALUE(sx); + owner = lv_sx_owner(x); + } while (owner != NULL && + TD_IS_RUNNING(owner)); KTR_STATE0(KTR_SCHED, "thread", sched_tdname(curthread), "running"); continue; @@ -645,14 +639,19 @@ _sx_xlock_hard(struct sx *sx, uintptr_t tid, int opts, const char *file, } KTR_STATE0(KTR_SCHED, "thread", sched_tdname(curthread), "running"); + spin_loops += 1; + x = SX_READ_VALUE(sx); if (i != asx_loops) continue; + reached_limit++; + } else { + given_up++; } } #endif sleepq_lock(&sx->lock_object); - x = sx->sx_lock; + x = SX_READ_VALUE(sx); /* * If the lock was released while spinning on the @@ -701,6 +700,7 @@ _sx_xlock_hard(struct sx *sx, uintptr_t tid, int opts, const char *file, break; } sleepq_release(&sx->lock_object); + x = SX_READ_VALUE(sx); continue; } @@ -712,6 +712,7 @@ _sx_xlock_hard(struct sx *sx, uintptr_t tid, int opts, const char *file, if (!atomic_cmpset_ptr(&sx->sx_lock, x, x | SX_LOCK_EXCLUSIVE_WAITERS)) { sleepq_release(&sx->lock_object); + x = SX_READ_VALUE(sx); continue; } if (LOCK_LOG_TEST(&sx->lock_object, 0)) @@ -753,7 +754,9 @@ _sx_xlock_hard(struct sx *sx, uintptr_t tid, int opts, const char *file, if (LOCK_LOG_TEST(&sx->lock_object, 0)) CTR2(KTR_LOCK, "%s: %p resuming from sleep queue", __func__, sx); + x = SX_READ_VALUE(sx); } + SDT_PROBE6(lock, sx, obtain, write, sleep_cnt, lda.spin_cnt, spin_loops, spintries, given_up, reached_limit); #ifdef KDTRACE_HOOKS all_time += lockstat_nsecs(&sx->lock_object); if (sleep_time) @@ -872,20 +875,18 @@ _sx_slock_hard(struct sx *sx, int opts, const char *file, int line) lock_delay_arg_init(&lda, NULL); #endif #ifdef KDTRACE_HOOKS - state = sx->sx_lock; all_time -= lockstat_nsecs(&sx->lock_object); #endif + x = SX_READ_VALUE(sx); +#ifdef KDTRACE_HOOKS + state = x; +#endif /* * As with rwlocks, we don't make any attempt to try to block * shared locks once there is an exclusive waiter. */ for (;;) { -#ifdef KDTRACE_HOOKS - lda.spin_cnt++; -#endif - x = sx->sx_lock; - /* * If no other thread has an exclusive lock then try to bump up * the count of sharers. Since we have to preserve the state @@ -894,7 +895,7 @@ _sx_slock_hard(struct sx *sx, int opts, const char *file, int line) */ if (x & SX_LOCK_SHARED) { MPASS(!(x & SX_LOCK_SHARED_WAITERS)); - if (atomic_cmpset_acq_ptr(&sx->sx_lock, x, + if (atomic_fcmpset_acq_ptr(&sx->sx_lock, &x, x + SX_ONE_SHARER)) { if (LOCK_LOG_TEST(&sx->lock_object, 0)) CTR4(KTR_LOCK, @@ -905,6 +906,10 @@ _sx_slock_hard(struct sx *sx, int opts, const char *file, int line) } continue; } +#ifdef KDTRACE_HOOKS + lda.spin_cnt++; +#endif + #ifdef HWPMC_HOOKS PMC_SOFT_CALL( , , lock, failed); #endif @@ -918,8 +923,7 @@ _sx_slock_hard(struct sx *sx, int opts, const char *file, int line) * changes. */ if ((sx->lock_object.lo_flags & SX_NOADAPTIVE) == 0) { - x = SX_OWNER(x); - owner = (struct thread *)x; + owner = lv_sx_owner(x); if (TD_IS_RUNNING(owner)) { if (LOCK_LOG_TEST(&sx->lock_object, 0)) CTR3(KTR_LOCK, @@ -929,9 +933,11 @@ _sx_slock_hard(struct sx *sx, int opts, const char *file, int line) sched_tdname(curthread), "spinning", "lockname:\"%s\"", sx->lock_object.lo_name); GIANT_SAVE(); - while (SX_OWNER(sx->sx_lock) == x && - TD_IS_RUNNING(owner)) + do { lock_delay(&lda); + x = SX_READ_VALUE(sx); + owner = lv_sx_owner(x); + } while (owner != NULL && TD_IS_RUNNING(owner)); KTR_STATE0(KTR_SCHED, "thread", sched_tdname(curthread), "running"); continue; @@ -944,7 +950,7 @@ _sx_slock_hard(struct sx *sx, int opts, const char *file, int line) * start the process of blocking. */ sleepq_lock(&sx->lock_object); - x = sx->sx_lock; + x = SX_READ_VALUE(sx); /* * The lock could have been released while we spun. @@ -966,6 +972,7 @@ _sx_slock_hard(struct sx *sx, int opts, const char *file, int line) owner = (struct thread *)SX_OWNER(x); if (TD_IS_RUNNING(owner)) { sleepq_release(&sx->lock_object); + x = SX_READ_VALUE(sx); continue; } } @@ -980,6 +987,7 @@ _sx_slock_hard(struct sx *sx, int opts, const char *file, int line) if (!atomic_cmpset_ptr(&sx->sx_lock, x, x | SX_LOCK_SHARED_WAITERS)) { sleepq_release(&sx->lock_object); + x = SX_READ_VALUE(sx); continue; } if (LOCK_LOG_TEST(&sx->lock_object, 0)) @@ -1020,6 +1028,7 @@ _sx_slock_hard(struct sx *sx, int opts, const char *file, int line) if (LOCK_LOG_TEST(&sx->lock_object, 0)) CTR2(KTR_LOCK, "%s: %p resuming from sleep queue", __func__, sx); + x = SX_READ_VALUE(sx); } #ifdef KDTRACE_HOOKS all_time += lockstat_nsecs(&sx->lock_object); @@ -1054,9 +1063,8 @@ _sx_sunlock_hard(struct sx *sx, const char *file, int line) if (SCHEDULER_STOPPED()) return; + x = SX_READ_VALUE(sx); for (;;) { - x = sx->sx_lock; - /* * We should never have sharers while at least one thread * holds a shared lock. @@ -1069,7 +1077,7 @@ _sx_sunlock_hard(struct sx *sx, const char *file, int line) * so, just drop one and return. */ if (SX_SHARERS(x) > 1) { - if (atomic_cmpset_rel_ptr(&sx->sx_lock, x, + if (atomic_fcmpset_rel_ptr(&sx->sx_lock, &x, x - SX_ONE_SHARER)) { if (LOCK_LOG_TEST(&sx->lock_object, 0)) CTR4(KTR_LOCK, @@ -1087,8 +1095,9 @@ _sx_sunlock_hard(struct sx *sx, const char *file, int line) */ if (!(x & SX_LOCK_EXCLUSIVE_WAITERS)) { MPASS(x == SX_SHARERS_LOCK(1)); - if (atomic_cmpset_rel_ptr(&sx->sx_lock, - SX_SHARERS_LOCK(1), SX_LOCK_UNLOCKED)) { + x = SX_SHARERS_LOCK(1); + if (atomic_fcmpset_rel_ptr(&sx->sx_lock, + &x, SX_LOCK_UNLOCKED)) { if (LOCK_LOG_TEST(&sx->lock_object, 0)) CTR2(KTR_LOCK, "%s: %p last succeeded", __func__, sx); @@ -1115,6 +1124,7 @@ _sx_sunlock_hard(struct sx *sx, const char *file, int line) SX_SHARERS_LOCK(1) | SX_LOCK_EXCLUSIVE_WAITERS, SX_LOCK_UNLOCKED)) { sleepq_release(&sx->lock_object); + x = SX_READ_VALUE(sx); continue; } if (LOCK_LOG_TEST(&sx->lock_object, 0)) diff --git a/sys/kern/subr_lock.c b/sys/kern/subr_lock.c index bfe189d..ee137eb 100644 --- a/sys/kern/subr_lock.c +++ b/sys/kern/subr_lock.c @@ -56,6 +56,9 @@ __FBSDID("$FreeBSD$"); #include +SDT_PROVIDER_DEFINE(lock); +SDT_PROBE_DEFINE2(lock, , , starvation, "u_int", "u_int"); + CTASSERT(LOCK_CLASS_MAX == 15); struct lock_class *lock_classes[LOCK_CLASS_MAX + 1] = { @@ -103,32 +106,98 @@ lock_destroy(struct lock_object *lock) lock->lo_flags &= ~LO_INITIALIZED; } +static SYSCTL_NODE(_debug, OID_AUTO, lock, CTLFLAG_RD, NULL, "lock debugging"); +static SYSCTL_NODE(_debug_lock, OID_AUTO, delay, CTLFLAG_RD, NULL, + "lock delay"); + +static u_int backoff_shift = 1; +SYSCTL_INT(_debug_lock_delay, OID_AUTO, backoff_shift, CTLFLAG_RW, &backoff_shift, 0, "") +; + +static u_int starvation_limit = 131072; +SYSCTL_INT(_debug_lock_delay, OID_AUTO, starvation_limit, CTLFLAG_RW, &starvation_limit, 0, ""); + +static u_int restrict_starvation = 0; +SYSCTL_INT(_debug_lock_delay, OID_AUTO, restrict_starvation, CTLFLAG_RW, &restrict_starvation, 0, ""); + void lock_delay(struct lock_delay_arg *la) { - u_int i, delay, backoff, min, max; + u_int i, delay; struct lock_delay_config *lc = la->config; delay = la->delay; + for (i = 0; i < delay; i++) + cpu_spinwait(); - if (delay == 0) - delay = lc->initial; - else { - delay += lc->step; - max = lc->max; - if (delay > max) - delay = max; + la->spin_cnt += delay; + + delay <<= backoff_shift; + if (__predict_false(delay > lc->max)) + delay = lc->max; + if (__predict_false(la->spin_cnt > starvation_limit)) { + SDT_PROBE2(lock, , , starvation, delay, PCPU_GET(cpuid)); + if (restrict_starvation) + delay = lc->initial >> backoff_shift; } - backoff = cpu_ticks() % delay; - min = lc->min; - if (backoff < min) - backoff = min; - for (i = 0; i < backoff; i++) + la->delay = delay; +} + +int +lock_delay_capped(struct lock_delay_arg *la) +{ + u_int i, delay; + struct lock_delay_config *lc = la->config; + + if (la->spin_cnt > lc->cap) + return (0); + + delay = la->delay; + for (i = 0; i < delay; i++) cpu_spinwait(); + la->spin_cnt += delay; + + delay <<= backoff_shift; + if (__predict_false(delay > lc->max)) + delay = lc->max; + if (__predict_false(la->spin_cnt > starvation_limit)) { + SDT_PROBE2(lock, , , starvation, delay, PCPU_GET(cpuid)); + if (restrict_starvation) + delay = lc->initial >> backoff_shift; + } + la->delay = delay; - la->spin_cnt += backoff; + return (1); +} + +static u_int +lock_roundup_2(u_int val) +{ + u_int res; + + for (res = 1; res <= val; res <<= 1) + continue; + + return (res); +} + +void +lock_delay_default_init(struct lock_delay_config *lc) +{ + + lc->initial = lock_roundup_2(mp_ncpus) / 2; + lc->max = lc->initial * 512; +} + +void +lock_delay_cap_default_init(struct lock_delay_config *lc) +{ + + lc->initial = lock_roundup_2(mp_ncpus) / 2; + lc->max = lc->initial * 512; + lc->cap = lc->max * 32; } #ifdef DDB @@ -655,7 +724,6 @@ out: critical_exit(); } -static SYSCTL_NODE(_debug, OID_AUTO, lock, CTLFLAG_RD, NULL, "lock debugging"); static SYSCTL_NODE(_debug_lock, OID_AUTO, prof, CTLFLAG_RD, NULL, "lock profiling"); SYSCTL_INT(_debug_lock_prof, OID_AUTO, skipspin, CTLFLAG_RW, diff --git a/sys/sys/lock.h b/sys/sys/lock.h index dbe715a..b47ec87 100644 --- a/sys/sys/lock.h +++ b/sys/sys/lock.h @@ -203,9 +203,8 @@ extern struct lock_class *lock_classes[]; struct lock_delay_config { u_int initial; - u_int step; - u_int min; u_int max; + u_int cap; }; struct lock_delay_arg { @@ -215,19 +214,31 @@ struct lock_delay_arg { }; static inline void -lock_delay_arg_init(struct lock_delay_arg *la, struct lock_delay_config *lc) { +lock_delay_arg_init(struct lock_delay_arg *la, struct lock_delay_config *lc) +{ la->config = lc; - la->delay = 0; + la->delay = lc->initial; la->spin_cnt = 0; } #define LOCK_DELAY_SYSINIT(func) \ SYSINIT(func##_ld, SI_SUB_LOCK, SI_ORDER_ANY, func, NULL) +#define LOCK_DELAY_SYSINIT_DEFAULT(lc) \ + SYSINIT(lock_delay_##lc##_ld, SI_SUB_LOCK, SI_ORDER_ANY, \ + lock_delay_default_init, &lc) + +#define LOCK_DELAY_CAP_SYSINIT_DEFAULT(lc) \ + SYSINIT(lock_delay_cap_##lc##_ld, SI_SUB_LOCK, SI_ORDER_ANY, \ + lock_delay_cap_default_init, &lc) + void lock_init(struct lock_object *, struct lock_class *, const char *, const char *, int); void lock_destroy(struct lock_object *); void lock_delay(struct lock_delay_arg *); +int lock_delay_capped(struct lock_delay_arg *); +void lock_delay_default_init(struct lock_delay_config *); +void lock_delay_cap_default_init(struct lock_delay_config *); void spinlock_enter(void); void spinlock_exit(void); void witness_init(struct lock_object *, const char *); diff --git a/sys/sys/lockstat.h b/sys/sys/lockstat.h index e550353..3b235d2 100644 --- a/sys/sys/lockstat.h +++ b/sys/sys/lockstat.h @@ -107,6 +107,8 @@ extern int lockstat_enabled; LOCKSTAT_RECORD1(probe, lp, a); \ } while (0) +#define LOCKSTAT_PROFILE_ENABLED(probe) SDT_PROBE_ENABLED(lockstat, , , probe) + struct lock_object; uint64_t lockstat_nsecs(struct lock_object *); @@ -130,6 +132,8 @@ uint64_t lockstat_nsecs(struct lock_object *); #define LOCKSTAT_PROFILE_RELEASE_RWLOCK(probe, lp, a) \ LOCKSTAT_PROFILE_RELEASE_LOCK(probe, lp) +#define LOCKSTAT_PROFILE_ENABLED(probe) 0 + #endif /* !KDTRACE_HOOKS */ #endif /* _KERNEL */ #endif /* _SYS_LOCKSTAT_H */ diff --git a/sys/sys/mutex.h b/sys/sys/mutex.h index 11ed9d7..4d11481 100644 --- a/sys/sys/mutex.h +++ b/sys/sys/mutex.h @@ -98,20 +98,20 @@ void mtx_sysinit(void *arg); int _mtx_trylock_flags_(volatile uintptr_t *c, int opts, const char *file, int line); void mutex_init(void); -void __mtx_lock_sleep(volatile uintptr_t *c, uintptr_t tid, int opts, +void __mtx_lock_sleep(volatile uintptr_t *c, uintptr_t v, uintptr_t tid, + int opts, const char *file, int line); +void __mtx_unlock_sleep(volatile uintptr_t *c, uintptr_t v, int opts, const char *file, int line); -void __mtx_unlock_sleep(volatile uintptr_t *c, int opts, const char *file, - int line); #ifdef SMP -void _mtx_lock_spin_cookie(volatile uintptr_t *c, uintptr_t tid, int opts, - const char *file, int line); +void _mtx_lock_spin_cookie(volatile uintptr_t *c, uintptr_t v, + uintptr_t tid, int opts, const char *file, int line); #endif void __mtx_lock_flags(volatile uintptr_t *c, int opts, const char *file, int line); void __mtx_unlock_flags(volatile uintptr_t *c, int opts, const char *file, int line); -void __mtx_lock_spin_flags(volatile uintptr_t *c, int opts, const char *file, - int line); +void __mtx_lock_spin_flags(volatile uintptr_t *c, int opts, + const char *file, int line); int __mtx_trylock_spin_flags(volatile uintptr_t *c, int opts, const char *file, int line); void __mtx_unlock_spin_flags(volatile uintptr_t *c, int opts, @@ -140,13 +140,13 @@ void thread_lock_flags_(struct thread *, int, const char *, int); _mtx_destroy(&(m)->mtx_lock) #define mtx_trylock_flags_(m, o, f, l) \ _mtx_trylock_flags_(&(m)->mtx_lock, o, f, l) -#define _mtx_lock_sleep(m, t, o, f, l) \ - __mtx_lock_sleep(&(m)->mtx_lock, t, o, f, l) -#define _mtx_unlock_sleep(m, o, f, l) \ - __mtx_unlock_sleep(&(m)->mtx_lock, o, f, l) +#define _mtx_lock_sleep(m, v, t, o, f, l) \ + __mtx_lock_sleep(&(m)->mtx_lock, v, t, o, f, l) +#define _mtx_unlock_sleep(m, v, o, f, l) \ + __mtx_unlock_sleep(&(m)->mtx_lock, v, o, f, l) #ifdef SMP -#define _mtx_lock_spin(m, t, o, f, l) \ - _mtx_lock_spin_cookie(&(m)->mtx_lock, t, o, f, l) +#define _mtx_lock_spin(m, v, t, o, f, l) \ + _mtx_lock_spin_cookie(&(m)->mtx_lock, v, t, o, f, l) #endif #define _mtx_lock_flags(m, o, f, l) \ __mtx_lock_flags(&(m)->mtx_lock, o, f, l) @@ -171,10 +171,20 @@ void thread_lock_flags_(struct thread *, int, const char *, int); #define _mtx_obtain_lock(mp, tid) \ atomic_cmpset_acq_ptr(&(mp)->mtx_lock, MTX_UNOWNED, (tid)) +#define _mtx_obtain_lock_fetch(mp, vp, tid) ({ \ + *vp = MTX_UNOWNED; \ + atomic_fcmpset_acq_ptr(&(mp)->mtx_lock, vp, (tid)); \ +}) + /* Try to release mtx_lock if it is unrecursed and uncontested. */ #define _mtx_release_lock(mp, tid) \ atomic_cmpset_rel_ptr(&(mp)->mtx_lock, (tid), MTX_UNOWNED) +#define _mtx_release_lock_fetch(mp, vp, tid) ({ \ + *vp = tid; \ + atomic_fcmpset_rel_ptr(&(mp)->mtx_lock, vp, MTX_UNOWNED); \ +}) + /* Release mtx_lock quickly, assuming we own it. */ #define _mtx_release_lock_quick(mp) \ atomic_store_rel_ptr(&(mp)->mtx_lock, MTX_UNOWNED) @@ -188,9 +198,10 @@ void thread_lock_flags_(struct thread *, int, const char *, int); /* Lock a normal mutex. */ #define __mtx_lock(mp, tid, opts, file, line) do { \ uintptr_t _tid = (uintptr_t)(tid); \ + uintptr_t _v; \ \ - if (((mp)->mtx_lock != MTX_UNOWNED || !_mtx_obtain_lock((mp), _tid)))\ - _mtx_lock_sleep((mp), _tid, (opts), (file), (line)); \ + if (!_mtx_obtain_lock_fetch((mp), &_v, _tid)) \ + _mtx_lock_sleep((mp), _v, _tid, (opts), (file), (line));\ else \ LOCKSTAT_PROFILE_OBTAIN_LOCK_SUCCESS(adaptive__acquire, \ mp, 0, 0, file, line); \ @@ -205,13 +216,14 @@ void thread_lock_flags_(struct thread *, int, const char *, int); #ifdef SMP #define __mtx_lock_spin(mp, tid, opts, file, line) do { \ uintptr_t _tid = (uintptr_t)(tid); \ + uintptr_t _v; \ \ spinlock_enter(); \ - if (((mp)->mtx_lock != MTX_UNOWNED || !_mtx_obtain_lock((mp), _tid))) {\ - if ((mp)->mtx_lock == _tid) \ + if (!_mtx_obtain_lock_fetch((mp), &_v, _tid)) { \ + if (_v == _tid) \ (mp)->mtx_recurse++; \ else \ - _mtx_lock_spin((mp), _tid, (opts), (file), (line)); \ + _mtx_lock_spin((mp), _v, _tid, (opts), (file), (line));\ } else \ LOCKSTAT_PROFILE_OBTAIN_LOCK_SUCCESS(spin__acquire, \ mp, 0, 0, file, line); \ @@ -262,11 +274,12 @@ void thread_lock_flags_(struct thread *, int, const char *, int); /* Unlock a normal mutex. */ #define __mtx_unlock(mp, tid, opts, file, line) do { \ uintptr_t _tid = (uintptr_t)(tid); \ + uintptr_t _v; \ \ - if ((mp)->mtx_recurse == 0) \ + if (LOCKSTAT_PROFILE_ENABLED(adaptive__release) && (mp)->mtx_recurse == 0)\ LOCKSTAT_PROFILE_RELEASE_LOCK(adaptive__release, mp); \ - if ((mp)->mtx_lock != _tid || !_mtx_release_lock((mp), _tid)) \ - _mtx_unlock_sleep((mp), (opts), (file), (line)); \ + if (!_mtx_release_lock_fetch((mp), &_v, _tid)) \ + _mtx_unlock_sleep((mp), _v, (opts), (file), (line)); \ } while (0) /* diff --git a/sys/sys/rwlock.h b/sys/sys/rwlock.h index 6b4f505..82fa76c 100644 --- a/sys/sys/rwlock.h +++ b/sys/sys/rwlock.h @@ -76,9 +76,16 @@ #define rw_recurse lock_object.lo_data +#define RW_READ_VALUE(x) ((x)->rw_lock) + /* Very simple operations on rw_lock. */ /* Try to obtain a write lock once. */ +#define _rw_write_lock_fetch(rw, vp, tid) ({ \ + *vp = RW_UNLOCKED; \ + atomic_fcmpset_acq_ptr(&(rw)->rw_lock, vp, (tid)); \ +}) + #define _rw_write_lock(rw, tid) \ atomic_cmpset_acq_ptr(&(rw)->rw_lock, RW_UNLOCKED, (tid)) @@ -95,9 +102,10 @@ /* Acquire a write lock. */ #define __rw_wlock(rw, tid, file, line) do { \ uintptr_t _tid = (uintptr_t)(tid); \ + uintptr_t _v; \ \ - if ((rw)->rw_lock != RW_UNLOCKED || !_rw_write_lock((rw), _tid))\ - _rw_wlock_hard((rw), _tid, (file), (line)); \ + if (!_rw_write_lock_fetch((rw), &_v, _tid)) \ + _rw_wlock_hard((rw), _v, _tid, (file), (line)); \ else \ LOCKSTAT_PROFILE_OBTAIN_RWLOCK_SUCCESS(rw__acquire, rw, \ 0, 0, file, line, LOCKSTAT_WRITER); \ @@ -112,7 +120,7 @@ else { \ LOCKSTAT_PROFILE_RELEASE_RWLOCK(rw__release, rw, \ LOCKSTAT_WRITER); \ - if ((rw)->rw_lock != _tid || !_rw_write_unlock((rw), _tid))\ + if (!_rw_write_unlock((rw), _tid)) \ _rw_wunlock_hard((rw), _tid, (file), (line)); \ } \ } while (0) @@ -133,8 +141,8 @@ void _rw_wunlock_cookie(volatile uintptr_t *c, const char *file, int line); void __rw_rlock(volatile uintptr_t *c, const char *file, int line); int __rw_try_rlock(volatile uintptr_t *c, const char *file, int line); void _rw_runlock_cookie(volatile uintptr_t *c, const char *file, int line); -void __rw_wlock_hard(volatile uintptr_t *c, uintptr_t tid, const char *file, - int line); +void __rw_wlock_hard(volatile uintptr_t *c, uintptr_t v, uintptr_t tid, + const char *file, int line); void __rw_wunlock_hard(volatile uintptr_t *c, uintptr_t tid, const char *file, int line); int __rw_try_upgrade(volatile uintptr_t *c, const char *file, int line); @@ -169,8 +177,8 @@ void __rw_assert(const volatile uintptr_t *c, int what, const char *file, __rw_try_rlock(&(rw)->rw_lock, f, l) #define _rw_runlock(rw, f, l) \ _rw_runlock_cookie(&(rw)->rw_lock, f, l) -#define _rw_wlock_hard(rw, t, f, l) \ - __rw_wlock_hard(&(rw)->rw_lock, t, f, l) +#define _rw_wlock_hard(rw, v, t, f, l) \ + __rw_wlock_hard(&(rw)->rw_lock, v, t, f, l) #define _rw_wunlock_hard(rw, t, f, l) \ __rw_wunlock_hard(&(rw)->rw_lock, t, f, l) #define _rw_try_upgrade(rw, f, l) \ diff --git a/sys/sys/sdt.h b/sys/sys/sdt.h index 25423d7..ed699bc 100644 --- a/sys/sys/sdt.h +++ b/sys/sys/sdt.h @@ -160,6 +160,9 @@ SET_DECLARE(sdt_argtypes_set, struct sdt_argtype); #define SDT_PROBE_DECLARE(prov, mod, func, name) \ extern struct sdt_probe sdt_##prov##_##mod##_##func##_##name[1] +#define SDT_PROBE_ENABLED(prov, mod, func, name) \ + (__predict_false(sdt_##prov##_##mod##_##func##_##name->id)) + #define SDT_PROBE(prov, mod, func, name, arg0, arg1, arg2, arg3, arg4) do { \ if (__predict_false(sdt_##prov##_##mod##_##func##_##name->id)) \ (*sdt_probe_func)(sdt_##prov##_##mod##_##func##_##name->id, \ diff --git a/sys/sys/sx.h b/sys/sys/sx.h index 57a31d9..bee74ef 100644 --- a/sys/sys/sx.h +++ b/sys/sys/sx.h @@ -104,7 +104,7 @@ int _sx_slock(struct sx *sx, int opts, const char *file, int line); int _sx_xlock(struct sx *sx, int opts, const char *file, int line); void _sx_sunlock(struct sx *sx, const char *file, int line); void _sx_xunlock(struct sx *sx, const char *file, int line); -int _sx_xlock_hard(struct sx *sx, uintptr_t tid, int opts, +int _sx_xlock_hard(struct sx *sx, uintptr_t v, uintptr_t tid, int opts, const char *file, int line); int _sx_slock_hard(struct sx *sx, int opts, const char *file, int line); void _sx_xunlock_hard(struct sx *sx, uintptr_t tid, const char *file, int @@ -136,6 +136,11 @@ struct sx_args { #define SX_SYSINIT(name, sxa, desc) SX_SYSINIT_FLAGS(name, sxa, desc, 0) +#define SX_READ_VALUE(sx) ((sx)->sx_lock) + +#define lv_sx_owner(v) \ + ((v & SX_LOCK_SHARED) ? NULL : (struct thread *)SX_OWNER(v)) + /* * Full lock operations that are suitable to be inlined in non-debug kernels. * If the lock can't be acquired or released trivially then the work is @@ -148,11 +153,12 @@ __sx_xlock(struct sx *sx, struct thread *td, int opts, const char *file, int line) { uintptr_t tid = (uintptr_t)td; + uintptr_t v; int error = 0; - if (sx->sx_lock != SX_LOCK_UNLOCKED || - !atomic_cmpset_acq_ptr(&sx->sx_lock, SX_LOCK_UNLOCKED, tid)) - error = _sx_xlock_hard(sx, tid, opts, file, line); + v = SX_LOCK_UNLOCKED; + if (!atomic_fcmpset_acq_ptr(&sx->sx_lock, &v, tid)) + error = _sx_xlock_hard(sx, v, tid, opts, file, line); else LOCKSTAT_PROFILE_OBTAIN_RWLOCK_SUCCESS(sx__acquire, sx, 0, 0, file, line, LOCKSTAT_WRITER); @@ -169,11 +175,11 @@ __sx_xunlock(struct sx *sx, struct thread *td, const char *file, int line) if (sx->sx_recurse == 0) LOCKSTAT_PROFILE_RELEASE_RWLOCK(sx__release, sx, LOCKSTAT_WRITER); - if (sx->sx_lock != tid || - !atomic_cmpset_rel_ptr(&sx->sx_lock, tid, SX_LOCK_UNLOCKED)) + if (!atomic_cmpset_rel_ptr(&sx->sx_lock, tid, SX_LOCK_UNLOCKED)) _sx_xunlock_hard(sx, tid, file, line); } +#if 0 /* Acquire a shared lock. */ static __inline int __sx_slock(struct sx *sx, int opts, const char *file, int line) @@ -182,8 +188,8 @@ __sx_slock(struct sx *sx, int opts, const char *file, int line) int error = 0; if (!(x & SX_LOCK_SHARED) || - !atomic_cmpset_acq_ptr(&sx->sx_lock, x, x + SX_ONE_SHARER)) - error = _sx_slock_hard(sx, opts, file, line); + !atomic_fcmpset_acq_ptr(&sx->sx_lock, &x, x + SX_ONE_SHARER)) + error = _sx_slock_hard(sx, x, opts, file, line); else LOCKSTAT_PROFILE_OBTAIN_RWLOCK_SUCCESS(sx__acquire, sx, 0, 0, file, line, LOCKSTAT_READER); @@ -208,6 +214,21 @@ __sx_sunlock(struct sx *sx, const char *file, int line) !atomic_cmpset_rel_ptr(&sx->sx_lock, x, x - SX_ONE_SHARER)) _sx_sunlock_hard(sx, file, line); } +#endif + +static __inline int +__sx_slock(struct sx *sx, int opts, const char *file, int line) +{ + + return (_sx_slock_hard(sx, opts, file, line)); +} + +static __inline void +__sx_sunlock(struct sx *sx, const char *file, int line) +{ + + _sx_sunlock_hard(sx, file, line); +} /* * Public interface for lock operations.