sx: implement slock/sunlock fast path
See r313454.
This commit is contained in:
parent
b0a61642d4
commit
834f70f32f
@ -799,8 +799,32 @@ _sx_xunlock_hard(struct sx *sx, uintptr_t tid, const char *file, int line)
|
||||
kick_proc0();
|
||||
}
|
||||
|
||||
int
|
||||
_sx_slock(struct sx *sx, int opts, const char *file, int line)
|
||||
static bool __always_inline
|
||||
__sx_slock_try(struct sx *sx, uintptr_t *xp, const char *file, int line)
|
||||
{
|
||||
|
||||
/*
|
||||
* If no other thread has an exclusive lock then try to bump up
|
||||
* the count of sharers. Since we have to preserve the state
|
||||
* of SX_LOCK_EXCLUSIVE_WAITERS, if we fail to acquire the
|
||||
* shared lock loop back and retry.
|
||||
*/
|
||||
while (*xp & SX_LOCK_SHARED) {
|
||||
MPASS(!(*xp & SX_LOCK_SHARED_WAITERS));
|
||||
if (atomic_fcmpset_acq_ptr(&sx->sx_lock, xp,
|
||||
*xp + SX_ONE_SHARER)) {
|
||||
if (LOCK_LOG_TEST(&sx->lock_object, 0))
|
||||
CTR4(KTR_LOCK, "%s: %p succeed %p -> %p",
|
||||
__func__, sx, (void *)*xp,
|
||||
(void *)(*xp + SX_ONE_SHARER));
|
||||
return (true);
|
||||
}
|
||||
}
|
||||
return (false);
|
||||
}
|
||||
|
||||
static int __noinline
|
||||
_sx_slock_hard(struct sx *sx, int opts, const char *file, int line, uintptr_t x)
|
||||
{
|
||||
GIANT_DECLARE;
|
||||
#ifdef ADAPTIVE_SX
|
||||
@ -810,7 +834,6 @@ _sx_slock(struct sx *sx, int opts, const char *file, int line)
|
||||
uint64_t waittime = 0;
|
||||
int contested = 0;
|
||||
#endif
|
||||
uintptr_t x;
|
||||
int error = 0;
|
||||
#if defined(ADAPTIVE_SX) || defined(KDTRACE_HOOKS)
|
||||
struct lock_delay_arg lda;
|
||||
@ -830,17 +853,8 @@ _sx_slock(struct sx *sx, int opts, const char *file, int line)
|
||||
#elif defined(KDTRACE_HOOKS)
|
||||
lock_delay_arg_init(&lda, NULL);
|
||||
#endif
|
||||
KASSERT(kdb_active != 0 || !TD_IS_IDLETHREAD(curthread),
|
||||
("sx_slock() by idle thread %p on sx %s @ %s:%d",
|
||||
curthread, sx->lock_object.lo_name, file, line));
|
||||
KASSERT(sx->sx_lock != SX_LOCK_DESTROYED,
|
||||
("sx_slock() of destroyed sx @ %s:%d", file, line));
|
||||
WITNESS_CHECKORDER(&sx->lock_object, LOP_NEWORDER, file, line, NULL);
|
||||
#ifdef KDTRACE_HOOKS
|
||||
all_time -= lockstat_nsecs(&sx->lock_object);
|
||||
#endif
|
||||
x = SX_READ_VALUE(sx);
|
||||
#ifdef KDTRACE_HOOKS
|
||||
state = x;
|
||||
#endif
|
||||
|
||||
@ -849,25 +863,8 @@ _sx_slock(struct sx *sx, int opts, const char *file, int line)
|
||||
* shared locks once there is an exclusive waiter.
|
||||
*/
|
||||
for (;;) {
|
||||
/*
|
||||
* If no other thread has an exclusive lock then try to bump up
|
||||
* the count of sharers. Since we have to preserve the state
|
||||
* of SX_LOCK_EXCLUSIVE_WAITERS, if we fail to acquire the
|
||||
* shared lock loop back and retry.
|
||||
*/
|
||||
if (x & SX_LOCK_SHARED) {
|
||||
MPASS(!(x & SX_LOCK_SHARED_WAITERS));
|
||||
if (atomic_fcmpset_acq_ptr(&sx->sx_lock, &x,
|
||||
x + SX_ONE_SHARER)) {
|
||||
if (LOCK_LOG_TEST(&sx->lock_object, 0))
|
||||
CTR4(KTR_LOCK,
|
||||
"%s: %p succeed %p -> %p", __func__,
|
||||
sx, (void *)x,
|
||||
(void *)(x + SX_ONE_SHARER));
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (__sx_slock_try(sx, &x, file, line))
|
||||
break;
|
||||
#ifdef KDTRACE_HOOKS
|
||||
lda.spin_cnt++;
|
||||
#endif
|
||||
@ -1006,51 +1003,62 @@ _sx_slock(struct sx *sx, int opts, const char *file, int line)
|
||||
if (error == 0) {
|
||||
LOCKSTAT_PROFILE_OBTAIN_RWLOCK_SUCCESS(sx__acquire, sx,
|
||||
contested, waittime, file, line, LOCKSTAT_READER);
|
||||
LOCK_LOG_LOCK("SLOCK", &sx->lock_object, 0, 0, file, line);
|
||||
WITNESS_LOCK(&sx->lock_object, 0, file, line);
|
||||
TD_LOCKS_INC(curthread);
|
||||
}
|
||||
GIANT_RESTORE();
|
||||
return (error);
|
||||
}
|
||||
|
||||
void
|
||||
_sx_sunlock(struct sx *sx, const char *file, int line)
|
||||
int
|
||||
_sx_slock(struct sx *sx, int opts, const char *file, int line)
|
||||
{
|
||||
uintptr_t x;
|
||||
int wakeup_swapper;
|
||||
|
||||
if (SCHEDULER_STOPPED())
|
||||
return;
|
||||
int error;
|
||||
|
||||
KASSERT(kdb_active != 0 || !TD_IS_IDLETHREAD(curthread),
|
||||
("sx_slock() by idle thread %p on sx %s @ %s:%d",
|
||||
curthread, sx->lock_object.lo_name, file, line));
|
||||
KASSERT(sx->sx_lock != SX_LOCK_DESTROYED,
|
||||
("sx_sunlock() of destroyed sx @ %s:%d", file, line));
|
||||
_sx_assert(sx, SA_SLOCKED, file, line);
|
||||
WITNESS_UNLOCK(&sx->lock_object, 0, file, line);
|
||||
LOCK_LOG_LOCK("SUNLOCK", &sx->lock_object, 0, 0, file, line);
|
||||
LOCKSTAT_PROFILE_RELEASE_RWLOCK(sx__release, sx, LOCKSTAT_READER);
|
||||
("sx_slock() of destroyed sx @ %s:%d", file, line));
|
||||
WITNESS_CHECKORDER(&sx->lock_object, LOP_NEWORDER, file, line, NULL);
|
||||
|
||||
error = 0;
|
||||
x = SX_READ_VALUE(sx);
|
||||
if (__predict_false(LOCKSTAT_OOL_PROFILE_ENABLED(sx__acquire) ||
|
||||
!__sx_slock_try(sx, &x, file, line)))
|
||||
error = _sx_slock_hard(sx, opts, file, line, x);
|
||||
if (error == 0) {
|
||||
LOCK_LOG_LOCK("SLOCK", &sx->lock_object, 0, 0, file, line);
|
||||
WITNESS_LOCK(&sx->lock_object, 0, file, line);
|
||||
TD_LOCKS_INC(curthread);
|
||||
}
|
||||
return (error);
|
||||
}
|
||||
|
||||
static bool __always_inline
|
||||
_sx_sunlock_try(struct sx *sx, uintptr_t *xp)
|
||||
{
|
||||
|
||||
for (;;) {
|
||||
/*
|
||||
* We should never have sharers while at least one thread
|
||||
* holds a shared lock.
|
||||
*/
|
||||
KASSERT(!(x & SX_LOCK_SHARED_WAITERS),
|
||||
KASSERT(!(*xp & SX_LOCK_SHARED_WAITERS),
|
||||
("%s: waiting sharers", __func__));
|
||||
|
||||
/*
|
||||
* See if there is more than one shared lock held. If
|
||||
* so, just drop one and return.
|
||||
*/
|
||||
if (SX_SHARERS(x) > 1) {
|
||||
if (atomic_fcmpset_rel_ptr(&sx->sx_lock, &x,
|
||||
x - SX_ONE_SHARER)) {
|
||||
if (SX_SHARERS(*xp) > 1) {
|
||||
if (atomic_fcmpset_rel_ptr(&sx->sx_lock, xp,
|
||||
*xp - SX_ONE_SHARER)) {
|
||||
if (LOCK_LOG_TEST(&sx->lock_object, 0))
|
||||
CTR4(KTR_LOCK,
|
||||
"%s: %p succeeded %p -> %p",
|
||||
__func__, sx, (void *)x,
|
||||
(void *)(x - SX_ONE_SHARER));
|
||||
break;
|
||||
__func__, sx, (void *)*xp,
|
||||
(void *)(*xp - SX_ONE_SHARER));
|
||||
return (true);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@ -1059,18 +1067,36 @@ _sx_sunlock(struct sx *sx, const char *file, int line)
|
||||
* If there aren't any waiters for an exclusive lock,
|
||||
* then try to drop it quickly.
|
||||
*/
|
||||
if (!(x & SX_LOCK_EXCLUSIVE_WAITERS)) {
|
||||
MPASS(x == SX_SHARERS_LOCK(1));
|
||||
x = SX_SHARERS_LOCK(1);
|
||||
if (!(*xp & SX_LOCK_EXCLUSIVE_WAITERS)) {
|
||||
MPASS(*xp == SX_SHARERS_LOCK(1));
|
||||
*xp = SX_SHARERS_LOCK(1);
|
||||
if (atomic_fcmpset_rel_ptr(&sx->sx_lock,
|
||||
&x, SX_LOCK_UNLOCKED)) {
|
||||
xp, SX_LOCK_UNLOCKED)) {
|
||||
if (LOCK_LOG_TEST(&sx->lock_object, 0))
|
||||
CTR2(KTR_LOCK, "%s: %p last succeeded",
|
||||
__func__, sx);
|
||||
break;
|
||||
return (true);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return (false);
|
||||
}
|
||||
|
||||
static void __noinline
|
||||
_sx_sunlock_hard(struct sx *sx, uintptr_t x, const char *file, int line)
|
||||
{
|
||||
int wakeup_swapper;
|
||||
|
||||
if (SCHEDULER_STOPPED())
|
||||
return;
|
||||
|
||||
LOCKSTAT_PROFILE_RELEASE_RWLOCK(sx__release, sx, LOCKSTAT_READER);
|
||||
|
||||
for (;;) {
|
||||
if (_sx_sunlock_try(sx, &x))
|
||||
break;
|
||||
|
||||
/*
|
||||
* At this point, there should just be one sharer with
|
||||
@ -1103,6 +1129,24 @@ _sx_sunlock(struct sx *sx, const char *file, int line)
|
||||
kick_proc0();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
_sx_sunlock(struct sx *sx, const char *file, int line)
|
||||
{
|
||||
uintptr_t x;
|
||||
|
||||
KASSERT(sx->sx_lock != SX_LOCK_DESTROYED,
|
||||
("sx_sunlock() of destroyed sx @ %s:%d", file, line));
|
||||
_sx_assert(sx, SA_SLOCKED, file, line);
|
||||
WITNESS_UNLOCK(&sx->lock_object, 0, file, line);
|
||||
LOCK_LOG_LOCK("SUNLOCK", &sx->lock_object, 0, 0, file, line);
|
||||
|
||||
x = SX_READ_VALUE(sx);
|
||||
if (__predict_false(LOCKSTAT_OOL_PROFILE_ENABLED(sx__release) ||
|
||||
!_sx_sunlock_try(sx, &x)))
|
||||
_sx_sunlock_hard(sx, x, file, line);
|
||||
|
||||
TD_LOCKS_DEC(curthread);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user