Do adaptive spinning for rwlocks, but only if the lock is write held and

there are no waiters. This gives a major boost to build.sh on larger
systems as directory vnode locks are exclusive for lookup, but are often
only held for a very short period of time.

This change has the potential to more readily expose lock order reversals
and other types of deadlock.
This commit is contained in:
ad 2008-04-04 17:25:09 +00:00
parent 61a0a96054
commit 15efd9ad99
2 changed files with 36 additions and 5 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: kern_rwlock.c,v 1.18 2008/01/28 19:58:32 ad Exp $ */
/* $NetBSD: kern_rwlock.c,v 1.19 2008/04/04 17:25:09 ad Exp $ */
/*-
* Copyright (c) 2002, 2006, 2007, 2008 The NetBSD Foundation, Inc.
@ -45,7 +45,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: kern_rwlock.c,v 1.18 2008/01/28 19:58:32 ad Exp $");
__KERNEL_RCSID(0, "$NetBSD: kern_rwlock.c,v 1.19 2008/04/04 17:25:09 ad Exp $");
#include "opt_multiprocessor.h"
@ -160,6 +160,7 @@ __strong_alias(rw_tryenter,rw_vector_tryenter);
static void rw_dump(volatile void *);
static lwp_t *rw_owner(wchan_t);
extern int mutex_onproc(uintptr_t, struct cpu_info **); /* XXX */
lockops_t rwlock_lockops = {
"Reader / writer lock",
@ -248,10 +249,13 @@ void
rw_vector_enter(krwlock_t *rw, const krw_t op)
{
uintptr_t owner, incr, need_wait, set_wait, curthread;
struct cpu_info *ci;
turnstile_t *ts;
int queue;
lwp_t *l;
LOCKSTAT_TIMER(slptime);
LOCKSTAT_TIMER(spintime);
LOCKSTAT_COUNTER(spincnt);
LOCKSTAT_FLAG(lsflag);
l = curlwp;
@ -289,7 +293,7 @@ rw_vector_enter(krwlock_t *rw, const krw_t op)
LOCKSTAT_ENTER(lsflag);
for (;;) {
for (ci = NULL;;) {
/*
* Read the lock owner field. If the need-to-wait
* indicator is clear, then try to acquire the lock.
@ -313,6 +317,31 @@ rw_vector_enter(krwlock_t *rw, const krw_t op)
if (RW_OWNER(rw) == curthread)
rw_abort(rw, __func__, "locking against myself");
#ifdef MULTIPROCESSOR
/*
* If the lock owner is running on another CPU, and
* there are no existing waiters, then spin.
*/
if ((owner & (RW_WRITE_LOCKED|RW_HAS_WAITERS)) ==
RW_WRITE_LOCKED && mutex_onproc(owner, &ci)) {
LOCKSTAT_START_TIMER(lsflag, spintime);
u_int count = SPINLOCK_BACKOFF_MIN;
for (;;) {
owner = rw->rw_owner;
if ((owner & (RW_WRITE_LOCKED|RW_HAS_WAITERS))
!= RW_WRITE_LOCKED)
break;
if (!mutex_onproc(owner, &ci))
break;
SPINLOCK_BACKOFF(count);
}
LOCKSTAT_STOP_TIMER(lsflag, spintime);
LOCKSTAT_COUNT(spincnt, 1);
if ((owner & need_wait) == 0)
continue;
}
#endif
/*
* Grab the turnstile chain lock. Once we have that, we
* can adjust the waiter bits and sleep queue.
@ -343,6 +372,7 @@ rw_vector_enter(krwlock_t *rw, const krw_t op)
break;
}
LOCKSTAT_EVENT(lsflag, rw, LB_RWLOCK | LB_SPIN, spincnt, spintime);
LOCKSTAT_EXIT(lsflag);
RW_DASSERT(rw, (op != RW_READER && RW_OWNER(rw) == curthread) ||

View File

@ -1,4 +1,4 @@
/* $NetBSD: main.c,v 1.11 2008/01/26 14:29:31 ad Exp $ */
/* $NetBSD: main.c,v 1.12 2008/04/04 17:25:09 ad Exp $ */
/*-
* Copyright (c) 2006, 2007 The NetBSD Foundation, Inc.
@ -49,7 +49,7 @@
#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: main.c,v 1.11 2008/01/26 14:29:31 ad Exp $");
__RCSID("$NetBSD: main.c,v 1.12 2008/04/04 17:25:09 ad Exp $");
#endif /* not lint */
#include <sys/types.h>
@ -127,6 +127,7 @@ const name_t alltypes[] = {
{ "Spin mutex spin", LB_SPIN_MUTEX | LB_SPIN },
{ "RW lock sleep (writer)", LB_RWLOCK | LB_SLEEP1 },
{ "RW lock sleep (reader)", LB_RWLOCK | LB_SLEEP2 },
{ "RW lock spin", LB_RWLOCK | LB_SPIN },
{ "Kernel lock spin", LB_KERNEL_LOCK | LB_SPIN },
{ NULL, 0 }
};