Module Name: src
Committed By: ad
Date: Wed Jan 22 11:40:17 UTC 2020
Modified Files:
src/sys/arch/amd64/amd64 [ad-namecache]: genassym.cf lock_stubs.S
src/sys/arch/amd64/include [ad-namecache]: rwlock.h
src/sys/arch/i386/i386 [ad-namecache]: genassym.cf lock_stubs.S
src/sys/arch/x86/include [ad-namecache]: rwlock.h
src/sys/kern [ad-namecache]: kern_rwlock.c
src/sys/sys [ad-namecache]: lwp.h rwlock.h
Log Message:
Back out previous.
To generate a diff of this commit:
cvs rdiff -u -r1.80.2.2 -r1.80.2.3 src/sys/arch/amd64/amd64/genassym.cf
cvs rdiff -u -r1.35.2.1 -r1.35.2.2 src/sys/arch/amd64/amd64/lock_stubs.S
cvs rdiff -u -r1.2.144.1 -r1.2.144.2 src/sys/arch/amd64/include/rwlock.h
cvs rdiff -u -r1.117.2.2 -r1.117.2.3 src/sys/arch/i386/i386/genassym.cf
cvs rdiff -u -r1.32.2.1 -r1.32.2.2 src/sys/arch/i386/i386/lock_stubs.S
cvs rdiff -u -r1.6.2.1 -r1.6.2.2 src/sys/arch/x86/include/rwlock.h
cvs rdiff -u -r1.59.2.3 -r1.59.2.4 src/sys/kern/kern_rwlock.c
cvs rdiff -u -r1.192.2.2 -r1.192.2.3 src/sys/sys/lwp.h
cvs rdiff -u -r1.12.2.1 -r1.12.2.2 src/sys/sys/rwlock.h
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/sys/arch/amd64/amd64/genassym.cf
diff -u src/sys/arch/amd64/amd64/genassym.cf:1.80.2.2 src/sys/arch/amd64/amd64/genassym.cf:1.80.2.3
--- src/sys/arch/amd64/amd64/genassym.cf:1.80.2.2 Sun Jan 19 21:08:29 2020
+++ src/sys/arch/amd64/amd64/genassym.cf Wed Jan 22 11:40:16 2020
@@ -1,4 +1,4 @@
-# $NetBSD: genassym.cf,v 1.80.2.2 2020/01/19 21:08:29 ad Exp $
+# $NetBSD: genassym.cf,v 1.80.2.3 2020/01/22 11:40:16 ad Exp $
#
# Copyright (c) 1998, 2006, 2007, 2008 The NetBSD Foundation, Inc.
@@ -78,6 +78,7 @@ include <sys/resourcevar.h>
include <sys/device.h>
include <sys/mbuf.h>
include <sys/mutex.h>
+include <sys/rwlock.h>
include <sys/cpu_data.h>
include <sys/evcnt.h>
include <sys/cpu.h>
@@ -345,6 +346,15 @@ define MTX_IPL offsetof(struct kmutex,
define MTX_LOCK offsetof(struct kmutex, u.s.mtxs_lock)
define MTX_OWNER offsetof(struct kmutex, u.mtxa_owner)
+define RW_OWNER offsetof(struct krwlock, rw_owner)
+define RW_WRITE_LOCKED RW_WRITE_LOCKED
+define RW_WRITE_WANTED RW_WRITE_WANTED
+define RW_READ_INCR RW_READ_INCR
+define RW_HAS_WAITERS RW_HAS_WAITERS
+define RW_THREAD RW_THREAD
+define RW_READER RW_READER
+define RW_WRITER RW_WRITER
+
define EV_COUNT offsetof(struct evcnt, ev_count)
define OPTERON_MSR_PASSCODE OPTERON_MSR_PASSCODE
Index: src/sys/arch/amd64/amd64/lock_stubs.S
diff -u src/sys/arch/amd64/amd64/lock_stubs.S:1.35.2.1 src/sys/arch/amd64/amd64/lock_stubs.S:1.35.2.2
--- src/sys/arch/amd64/amd64/lock_stubs.S:1.35.2.1 Sun Jan 19 21:08:29 2020
+++ src/sys/arch/amd64/amd64/lock_stubs.S Wed Jan 22 11:40:16 2020
@@ -1,4 +1,4 @@
-/* $NetBSD: lock_stubs.S,v 1.35.2.1 2020/01/19 21:08:29 ad Exp $ */
+/* $NetBSD: lock_stubs.S,v 1.35.2.2 2020/01/22 11:40:16 ad Exp $ */
/*
* Copyright (c) 2006, 2007, 2008, 2009 The NetBSD Foundation, Inc.
@@ -185,6 +185,126 @@ ENTRY(mutex_spin_exit)
END(mutex_spin_exit)
+/*
+ * void rw_enter(krwlock_t *rwl, krw_t op);
+ *
+ * Acquire one hold on a RW lock.
+ */
+ENTRY(rw_enter)
+ cmpl $RW_READER, %esi
+ jne 2f
+
+ /*
+ * Reader: this is the most common case.
+ */
+ movq (%rdi), %rax
+0:
+ testb $(RW_WRITE_LOCKED|RW_WRITE_WANTED), %al
+ jnz 3f
+ leaq RW_READ_INCR(%rax), %rdx
+ LOCK
+ cmpxchgq %rdx, (%rdi)
+ jnz 1f
+ RET
+1:
+ jmp 0b
+
+ /*
+ * Writer: if the compare-and-set fails, don't bother retrying.
+ */
+2: movq CPUVAR(CURLWP), %rcx
+ xorq %rax, %rax
+ orq $RW_WRITE_LOCKED, %rcx
+ LOCK
+ cmpxchgq %rcx, (%rdi)
+ jnz 3f
+ RET
+3:
+ jmp _C_LABEL(rw_vector_enter)
+END(rw_enter)
+
+/*
+ * void rw_exit(krwlock_t *rwl);
+ *
+ * Release one hold on a RW lock.
+ */
+ENTRY(rw_exit)
+ movq (%rdi), %rax
+ testb $RW_WRITE_LOCKED, %al
+ jnz 2f
+
+ /*
+ * Reader
+ */
+0: testb $RW_HAS_WAITERS, %al
+ jnz 3f
+ cmpq $RW_READ_INCR, %rax
+ jb 3f
+ leaq -RW_READ_INCR(%rax), %rdx
+ LOCK
+ cmpxchgq %rdx, (%rdi)
+ jnz 1f
+ ret
+1:
+ jmp 0b
+
+ /*
+ * Writer
+ */
+2: leaq -RW_WRITE_LOCKED(%rax), %rdx
+ subq CPUVAR(CURLWP), %rdx
+ jnz 3f
+ LOCK
+ cmpxchgq %rdx, (%rdi)
+ jnz 3f
+ ret
+
+3: jmp _C_LABEL(rw_vector_exit)
+END(rw_exit)
+
+/*
+ * int rw_tryenter(krwlock_t *rwl, krw_t op);
+ *
+ * Try to acquire one hold on a RW lock.
+ */
+ENTRY(rw_tryenter)
+ cmpl $RW_READER, %esi
+ jne 2f
+
+ /*
+ * Reader: this is the most common case.
+ */
+ movq (%rdi), %rax
+0:
+ testb $(RW_WRITE_LOCKED|RW_WRITE_WANTED), %al
+ jnz 4f
+ leaq RW_READ_INCR(%rax), %rdx
+ LOCK
+ cmpxchgq %rdx, (%rdi)
+ jnz 1f
+ movl %edx, %eax /* nonzero */
+ RET
+1:
+ jmp 0b
+
+ /*
+ * Writer: if the compare-and-set fails, don't bother retrying.
+ */
+2: movq CPUVAR(CURLWP), %rcx
+ xorq %rax, %rax
+ orq $RW_WRITE_LOCKED, %rcx
+ LOCK
+ cmpxchgq %rcx, (%rdi)
+ movl $0, %eax
+ setz %al
+3:
+ RET
+ ret
+4:
+ xorl %eax, %eax
+ jmp 3b
+END(rw_tryenter)
+
#endif /* LOCKDEBUG */
/*
Index: src/sys/arch/amd64/include/rwlock.h
diff -u src/sys/arch/amd64/include/rwlock.h:1.2.144.1 src/sys/arch/amd64/include/rwlock.h:1.2.144.2
--- src/sys/arch/amd64/include/rwlock.h:1.2.144.1 Sun Jan 19 21:11:00 2020
+++ src/sys/arch/amd64/include/rwlock.h Wed Jan 22 11:40:16 2020
@@ -0,0 +1,3 @@
+/* $NetBSD: rwlock.h,v 1.2.144.2 2020/01/22 11:40:16 ad Exp $ */
+
+#include <x86/rwlock.h>
Index: src/sys/arch/i386/i386/genassym.cf
diff -u src/sys/arch/i386/i386/genassym.cf:1.117.2.2 src/sys/arch/i386/i386/genassym.cf:1.117.2.3
--- src/sys/arch/i386/i386/genassym.cf:1.117.2.2 Sun Jan 19 21:08:29 2020
+++ src/sys/arch/i386/i386/genassym.cf Wed Jan 22 11:40:16 2020
@@ -1,4 +1,4 @@
-# $NetBSD: genassym.cf,v 1.117.2.2 2020/01/19 21:08:29 ad Exp $
+# $NetBSD: genassym.cf,v 1.117.2.3 2020/01/22 11:40:16 ad Exp $
#
# Copyright (c) 1998, 2006, 2007, 2008 The NetBSD Foundation, Inc.
@@ -79,6 +79,7 @@ include <sys/resourcevar.h>
include <sys/device.h>
include <sys/mbuf.h>
include <sys/mutex.h>
+include <sys/rwlock.h>
include <sys/cpu.h>
include <netinet/in.h>
@@ -353,6 +354,15 @@ define MTX_IPL offsetof(struct kmutex,
define MTX_LOCK offsetof(struct kmutex, mtx_lock)
define MTX_OWNER offsetof(struct kmutex, u.mtxa_owner)
+define RW_OWNER offsetof(struct krwlock, rw_owner)
+define RW_WRITE_LOCKED RW_WRITE_LOCKED
+define RW_WRITE_WANTED RW_WRITE_WANTED
+define RW_READ_INCR RW_READ_INCR
+define RW_HAS_WAITERS RW_HAS_WAITERS
+define RW_THREAD RW_THREAD
+define RW_READER RW_READER
+define RW_WRITER RW_WRITER
+
define EV_COUNT offsetof(struct evcnt, ev_count)
define OPTERON_MSR_PASSCODE OPTERON_MSR_PASSCODE
Index: src/sys/arch/i386/i386/lock_stubs.S
diff -u src/sys/arch/i386/i386/lock_stubs.S:1.32.2.1 src/sys/arch/i386/i386/lock_stubs.S:1.32.2.2
--- src/sys/arch/i386/i386/lock_stubs.S:1.32.2.1 Sun Jan 19 21:08:29 2020
+++ src/sys/arch/i386/i386/lock_stubs.S Wed Jan 22 11:40:16 2020
@@ -1,4 +1,4 @@
-/* $NetBSD: lock_stubs.S,v 1.32.2.1 2020/01/19 21:08:29 ad Exp $ */
+/* $NetBSD: lock_stubs.S,v 1.32.2.2 2020/01/22 11:40:16 ad Exp $ */
/*-
* Copyright (c) 2006, 2007, 2008, 2009 The NetBSD Foundation, Inc.
@@ -35,7 +35,7 @@
*/
#include <machine/asm.h>
-__KERNEL_RCSID(0, "$NetBSD: lock_stubs.S,v 1.32.2.1 2020/01/19 21:08:29 ad Exp $");
+__KERNEL_RCSID(0, "$NetBSD: lock_stubs.S,v 1.32.2.2 2020/01/22 11:40:16 ad Exp $");
#include "opt_lockdebug.h"
@@ -97,6 +97,132 @@ ENTRY(mutex_exit)
jmp _C_LABEL(mutex_vector_exit)
END(mutex_exit)
+/*
+ * void rw_enter(krwlock_t *rwl, krw_t op);
+ *
+ * Acquire one hold on a RW lock.
+ */
+ENTRY(rw_enter)
+ movl 4(%esp), %edx
+ cmpl $RW_READER, 8(%esp)
+ jne 2f
+
+ /*
+ * Reader
+ */
+ movl (%edx), %eax
+0:
+ testb $(RW_WRITE_LOCKED|RW_WRITE_WANTED), %al
+ jnz 3f
+ leal RW_READ_INCR(%eax), %ecx
+ LOCK(2)
+ cmpxchgl %ecx, (%edx)
+ jnz 1f
+ RET(2)
+1:
+ jmp 0b
+
+ /*
+ * Writer
+ */
+2: xorl %eax, %eax
+ movl %fs:CPU_INFO_CURLWP(%eax), %ecx
+ orl $RW_WRITE_LOCKED, %ecx
+ LOCK(3)
+ cmpxchgl %ecx, (%edx)
+ jnz 3f
+ RET(3)
+3:
+ jmp _C_LABEL(rw_vector_enter)
+END(rw_enter)
+
+/*
+ * void rw_exit(krwlock_t *rwl);
+ *
+ * Release one hold on a RW lock.
+ */
+ENTRY(rw_exit)
+ movl 4(%esp), %edx
+ movl (%edx), %eax
+ testb $RW_WRITE_LOCKED, %al
+ jnz 2f
+
+ /*
+ * Reader
+ */
+0: testb $RW_HAS_WAITERS, %al
+ jnz 3f
+ cmpl $RW_READ_INCR, %eax
+ jb 3f
+ leal -RW_READ_INCR(%eax), %ecx
+ LOCK(4)
+ cmpxchgl %ecx, (%edx)
+ jnz 1f
+ ret
+1:
+ jmp 0b
+
+ /*
+ * Writer
+ */
+2: leal -RW_WRITE_LOCKED(%eax), %ecx
+ subl CPUVAR(CURLWP), %ecx
+ jnz 3f
+ LOCK(5)
+ cmpxchgl %ecx, (%edx)
+ jnz 3f
+ ret
+
+ /*
+ * Slow path.
+ */
+3: jmp _C_LABEL(rw_vector_exit)
+END(rw_exit)
+
+/*
+ * int rw_tryenter(krwlock_t *rwl, krw_t op);
+ *
+ * Try to acquire one hold on a RW lock.
+ */
+ENTRY(rw_tryenter)
+ movl 4(%esp), %edx
+ cmpl $RW_READER, 8(%esp)
+ jne 2f
+
+ /*
+ * Reader
+ */
+ movl (%edx), %eax
+0:
+ testb $(RW_WRITE_LOCKED|RW_WRITE_WANTED), %al
+ jnz 4f
+ leal RW_READ_INCR(%eax), %ecx
+ LOCK(12)
+ cmpxchgl %ecx, (%edx)
+ jnz 1f
+ movl %edx, %eax /* nonzero */
+ RET(4)
+1:
+ jmp 0b
+
+ /*
+ * Writer
+ */
+2:
+ xorl %eax, %eax
+ movl %fs:CPU_INFO_CURLWP(%eax), %ecx
+ orl $RW_WRITE_LOCKED, %ecx
+ LOCK(13)
+ cmpxchgl %ecx, (%edx)
+ movl $0, %eax
+ setz %al
+3:
+ RET(5)
+4:
+ xorl %eax, %eax
+ jmp 3b
+END(rw_tryenter)
+
#ifndef XENPV
/*
Index: src/sys/arch/x86/include/rwlock.h
diff -u src/sys/arch/x86/include/rwlock.h:1.6.2.1 src/sys/arch/x86/include/rwlock.h:1.6.2.2
--- src/sys/arch/x86/include/rwlock.h:1.6.2.1 Sun Jan 19 21:11:00 2020
+++ src/sys/arch/x86/include/rwlock.h Wed Jan 22 11:40:17 2020
@@ -0,0 +1,37 @@
+/* $NetBSD: rwlock.h,v 1.6.2.2 2020/01/22 11:40:17 ad Exp $ */
+
+/*-
+ * Copyright (c) 2002, 2006 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jason R. Thorpe and Andrew Doran.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _X86_RWLOCK_H_
+#define _X86_RWLOCK_H_
+
+#define __HAVE_RW_STUBS 1
+
+#endif /* _X86_RWLOCK_H_ */
Index: src/sys/kern/kern_rwlock.c
diff -u src/sys/kern/kern_rwlock.c:1.59.2.3 src/sys/kern/kern_rwlock.c:1.59.2.4
--- src/sys/kern/kern_rwlock.c:1.59.2.3 Sun Jan 19 21:08:29 2020
+++ src/sys/kern/kern_rwlock.c Wed Jan 22 11:40:17 2020
@@ -1,4 +1,4 @@
-/* $NetBSD: kern_rwlock.c,v 1.59.2.3 2020/01/19 21:08:29 ad Exp $ */
+/* $NetBSD: kern_rwlock.c,v 1.59.2.4 2020/01/22 11:40:17 ad Exp $ */
/*-
* Copyright (c) 2002, 2006, 2007, 2008, 2009, 2019, 2020
@@ -36,23 +36,10 @@
*
* Solaris Internals: Core Kernel Architecture, Jim Mauro and
* Richard McDougall.
- *
- * The NetBSD implementation is different from that described in the book,
- * in that the locks are adaptive. Lock waiters spin wait while the lock
- * holders are on CPU (if the holds can be tracked: up to N per-thread).
- *
- * While spin waiting, threads compete for the lock without the assistance
- * of turnstiles. If a lock holder sleeps for any reason, the lock waiters
- * will also sleep in response and at that point turnstiles, priority
- * inheritance and strong efforts at ensuring fairness come into play.
- *
- * The adaptive behaviour is controlled by the RW_SPIN flag bit, which is
- * cleared by a lock owner that is going off the CPU, and set again by the
- * lock owner that releases the last hold on the lock.
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: kern_rwlock.c,v 1.59.2.3 2020/01/19 21:08:29 ad Exp $");
+__KERNEL_RCSID(0, "$NetBSD: kern_rwlock.c,v 1.59.2.4 2020/01/22 11:40:17 ad Exp $");
#include "opt_lockdebug.h"
@@ -72,6 +59,8 @@ __KERNEL_RCSID(0, "$NetBSD: kern_rwlock.
#include <dev/lockstat.h>
+#include <machine/rwlock.h>
+
/*
* LOCKDEBUG
*/
@@ -115,6 +104,19 @@ do { \
#define RW_MEMBAR_PRODUCER() membar_producer()
#endif
+/*
+ * For platforms that do not provide stubs, or for the LOCKDEBUG case.
+ */
+#ifdef LOCKDEBUG
+#undef __HAVE_RW_STUBS
+#endif
+
+#ifndef __HAVE_RW_STUBS
+__strong_alias(rw_enter,rw_vector_enter);
+__strong_alias(rw_exit,rw_vector_exit);
+__strong_alias(rw_tryenter,rw_vector_tryenter);
+#endif
+
static void rw_abort(const char *, size_t, krwlock_t *, const char *);
static void rw_dump(const volatile void *, lockop_printer_t);
static lwp_t *rw_owner(wchan_t);
@@ -147,22 +149,6 @@ rw_cas(krwlock_t *rw, uintptr_t o, uintp
}
/*
- * rw_and:
- *
- * Do an atomic AND on the lock word.
- */
-static inline void
-rw_and(krwlock_t *rw, uintptr_t m)
-{
-
-#ifdef _LP64
- atomic_and_64(&rw->rw_owner, m);
-#else
- atomic_and_32(&rw->rw_owner, m);
-#endif
-}
-
-/*
* rw_swap:
*
* Do an atomic swap of the lock word. This is used only when it's
@@ -181,75 +167,6 @@ rw_swap(krwlock_t *rw, uintptr_t o, uint
}
/*
- * rw_hold_remember:
- *
- * Helper - when acquring a lock, record the new hold.
- */
-static inline uintptr_t
-rw_hold_remember(krwlock_t *rw, lwp_t *l)
-{
- int i;
-
- KASSERT(kpreempt_disabled());
-
- for (i = 0; i < __arraycount(l->l_rwlocks); i++) {
- if (__predict_true(l->l_rwlocks[i] == NULL)) {
- l->l_rwlocks[i] = rw;
- /*
- * Clear the write wanted flag on every acquire to
- * give readers a chance once again.
- */
- return ~RW_WRITE_WANTED;
- }
- }
-
- /*
- * Nowhere to track the hold so we lose: temporarily disable
- * spinning on the lock.
- */
- return ~(RW_WRITE_WANTED | RW_SPIN);
-}
-
-/*
- * rw_hold_forget:
- *
- * Helper - when releasing a lock, stop tracking the hold.
- */
-static inline void
-rw_hold_forget(krwlock_t *rw, lwp_t *l)
-{
- int i;
-
- KASSERT(kpreempt_disabled());
-
- for (i = 0; i < __arraycount(l->l_rwlocks); i++) {
- if (__predict_true(l->l_rwlocks[i] == rw)) {
- l->l_rwlocks[i] = NULL;
- return;
- }
- }
-}
-
-/*
- * rw_switch:
- *
- * Called by mi_switch() to indicate that an LWP is going off the CPU.
- */
-void
-rw_switch(void)
-{
- lwp_t *l = curlwp;
- int i;
-
- for (i = 0; i < __arraycount(l->l_rwlocks); i++) {
- if (l->l_rwlocks[i] != NULL) {
- rw_and(l->l_rwlocks[i], ~RW_SPIN);
- /* Leave in place for exit to clear. */
- }
- }
-}
-
-/*
* rw_dump:
*
* Dump the contents of a rwlock structure.
@@ -289,10 +206,15 @@ void
_rw_init(krwlock_t *rw, uintptr_t return_address)
{
+#ifdef LOCKDEBUG
+ /* XXX only because the assembly stubs can't handle RW_NODEBUG */
if (LOCKDEBUG_ALLOC(rw, &rwlock_lockops, return_address))
- rw->rw_owner = RW_SPIN;
+ rw->rw_owner = 0;
else
- rw->rw_owner = RW_SPIN | RW_NODEBUG;
+ rw->rw_owner = RW_NODEBUG;
+#else
+ rw->rw_owner = 0;
+#endif
}
void
@@ -311,18 +233,53 @@ void
rw_destroy(krwlock_t *rw)
{
- RW_ASSERT(rw, (rw->rw_owner & ~(RW_NODEBUG | RW_SPIN)) == 0);
+ RW_ASSERT(rw, (rw->rw_owner & ~RW_NODEBUG) == 0);
LOCKDEBUG_FREE((rw->rw_owner & RW_NODEBUG) == 0, rw);
}
/*
+ * rw_oncpu:
+ *
+ * Return true if an rwlock owner is running on a CPU in the system.
+ * If the target is waiting on the kernel big lock, then we must
+ * release it. This is necessary to avoid deadlock.
+ */
+static bool
+rw_oncpu(uintptr_t owner)
+{
+#ifdef MULTIPROCESSOR
+ struct cpu_info *ci;
+ lwp_t *l;
+
+ KASSERT(kpreempt_disabled());
+
+ if ((owner & (RW_WRITE_LOCKED|RW_HAS_WAITERS)) != RW_WRITE_LOCKED) {
+ return false;
+ }
+
+ /*
+ * See lwp_dtor() why dereference of the LWP pointer is safe.
+ * We must have kernel preemption disabled for that.
+ */
+ l = (lwp_t *)(owner & RW_THREAD);
+ ci = l->l_cpu;
+
+ if (ci && ci->ci_curlwp == l) {
+ /* Target is running; do we need to block? */
+ return (ci->ci_biglock_wanted != l);
+ }
+#endif
+ /* Not running. It may be safe to block now. */
+ return false;
+}
+
+/*
* rw_vector_enter:
*
- * The slow path for acquiring a rwlock, that considers all conditions.
- * Marked __noinline to prevent the compiler pulling it into rw_enter().
+ * Acquire a rwlock.
*/
-static void __noinline
-rw_vector_enter(krwlock_t *rw, const krw_t op, uintptr_t mask, uintptr_t ra)
+void
+rw_vector_enter(krwlock_t *rw, const krw_t op)
{
uintptr_t owner, incr, need_wait, set_wait, curthread, next;
turnstile_t *ts;
@@ -339,7 +296,6 @@ rw_vector_enter(krwlock_t *rw, const krw
RW_ASSERT(rw, !cpu_intr_p());
RW_ASSERT(rw, curthread != 0);
- RW_ASSERT(rw, kpreempt_disabled());
RW_WANTLOCK(rw, op);
if (panicstr == NULL) {
@@ -371,13 +327,15 @@ rw_vector_enter(krwlock_t *rw, const krw
LOCKSTAT_ENTER(lsflag);
+ KPREEMPT_DISABLE(curlwp);
for (owner = rw->rw_owner;;) {
/*
* Read the lock owner field. If the need-to-wait
* indicator is clear, then try to acquire the lock.
*/
if ((owner & need_wait) == 0) {
- next = rw_cas(rw, owner, (owner + incr) & mask);
+ next = rw_cas(rw, owner, (owner + incr) &
+ ~RW_WRITE_WANTED);
if (__predict_true(next == owner)) {
/* Got it! */
RW_MEMBAR_ENTER();
@@ -395,36 +353,11 @@ rw_vector_enter(krwlock_t *rw, const krw
rw_abort(__func__, __LINE__, rw,
"locking against myself");
}
-
/*
- * If the lock owner is running on another CPU, and there
- * are no existing waiters, then spin. Notes:
- *
- * 1) If an LWP on this CPU (possibly curlwp, or an LWP that
- * curlwp has interupted) holds kernel_lock, we can't spin
- * without a deadlock. The CPU that holds the rwlock may be
- * blocked trying to acquire kernel_lock, or there may be an
- * unseen chain of dependant locks. To defeat the potential
- * deadlock, this LWP needs to sleep (and thereby directly
- * drop the kernel_lock, or permit the interrupted LWP that
- * holds kernel_lock to complete its work).
- *
- * 2) If trying to acquire a write lock, and the lock is
- * currently read held, after a brief wait set the write
- * wanted bit to block out new readers and try to avoid
- * starvation. When the hold is acquired, we'll clear the
- * WRITE_WANTED flag to give readers a chance again. With
- * luck this should nudge things in the direction of
- * interleaving readers and writers when there is high
- * contention.
- *
- * 3) The spin wait can't be done in soft interrupt context,
- * because a lock holder could be pinned down underneath the
- * soft interrupt LWP (i.e. curlwp) on the same CPU. For
- * the lock holder to make progress and release the lock,
- * the soft interrupt needs to sleep.
+ * If the lock owner is running on another CPU, and
+ * there are no existing waiters, then spin.
*/
- if ((owner & RW_SPIN) != 0 && !cpu_softintr_p()) {
+ if (rw_oncpu(owner)) {
LOCKSTAT_START_TIMER(lsflag, spintime);
u_int count = SPINLOCK_BACKOFF_MIN;
do {
@@ -432,19 +365,7 @@ rw_vector_enter(krwlock_t *rw, const krw
SPINLOCK_BACKOFF(count);
KPREEMPT_DISABLE(curlwp);
owner = rw->rw_owner;
- if ((owner & need_wait) == 0)
- break;
- if (count != SPINLOCK_BACKOFF_MAX)
- continue;
- if (curcpu()->ci_biglock_count != 0)
- break;
- if (op == RW_WRITER &&
- (owner & RW_WRITE_LOCKED) == 0 &&
- (owner & RW_WRITE_WANTED) == 0) {
- (void)rw_cas(rw, owner,
- owner | RW_WRITE_WANTED);
- }
- } while ((owner & RW_SPIN) != 0);
+ } while (rw_oncpu(owner));
LOCKSTAT_STOP_TIMER(lsflag, spintime);
LOCKSTAT_COUNT(spincnt, 1);
if ((owner & need_wait) == 0)
@@ -458,18 +379,17 @@ rw_vector_enter(krwlock_t *rw, const krw
ts = turnstile_lookup(rw);
/*
- * Mark the rwlock as having waiters, and disable spinning.
- * If the set fails, then we may not need to sleep and
- * should spin again. Reload rw_owner now that we own
- * the turnstile chain lock.
+ * Mark the rwlock as having waiters. If the set fails,
+ * then we may not need to sleep and should spin again.
+ * Reload rw_owner because turnstile_lookup() may have
+ * spun on the turnstile chain lock.
*/
owner = rw->rw_owner;
- if ((owner & need_wait) == 0 ||
- ((owner & RW_SPIN) != 0 && !cpu_softintr_p())) {
+ if ((owner & need_wait) == 0 || rw_oncpu(owner)) {
turnstile_exit(rw);
continue;
}
- next = rw_cas(rw, owner, (owner | set_wait) & ~RW_SPIN);
+ next = rw_cas(rw, owner, owner | set_wait);
if (__predict_false(next != owner)) {
turnstile_exit(rw);
owner = next;
@@ -494,9 +414,11 @@ rw_vector_enter(krwlock_t *rw, const krw
LOCKSTAT_EVENT_RA(lsflag, rw, LB_RWLOCK |
(op == RW_WRITER ? LB_SLEEP1 : LB_SLEEP2), slpcnt, slptime,
- (l->l_rwcallsite != 0 ? l->l_rwcallsite : ra));
+ (l->l_rwcallsite != 0 ? l->l_rwcallsite :
+ (uintptr_t)__builtin_return_address(0)));
LOCKSTAT_EVENT_RA(lsflag, rw, LB_RWLOCK | LB_SPIN, spincnt, spintime,
- (l->l_rwcallsite != 0 ? l->l_rwcallsite : ra));
+ (l->l_rwcallsite != 0 ? l->l_rwcallsite :
+ (uintptr_t)__builtin_return_address(0)));
LOCKSTAT_EXIT(lsflag);
RW_ASSERT(rw, (op != RW_READER && RW_OWNER(rw) == curthread) ||
@@ -505,70 +427,11 @@ rw_vector_enter(krwlock_t *rw, const krw
}
/*
- * rw_enter:
- *
- * The fast path for acquiring a lock that considers only the
- * uncontended case. Falls back to rw_vector_enter().
- */
-void
-rw_enter(krwlock_t *rw, const krw_t op)
-{
- uintptr_t owner, incr, need_wait, curthread, next, mask;
- lwp_t *l;
-
- l = curlwp;
- curthread = (uintptr_t)l;
-
- RW_ASSERT(rw, !cpu_intr_p());
- RW_ASSERT(rw, curthread != 0);
- RW_WANTLOCK(rw, op);
-
- KPREEMPT_DISABLE(l);
- mask = rw_hold_remember(rw, l);
-
- /*
- * We play a slight trick here. If we're a reader, we want
- * increment the read count. If we're a writer, we want to
- * set the owner field and the WRITE_LOCKED bit.
- *
- * In the latter case, we expect those bits to be zero,
- * therefore we can use an add operation to set them, which
- * means an add operation for both cases.
- */
- if (__predict_true(op == RW_READER)) {
- incr = RW_READ_INCR;
- need_wait = RW_WRITE_LOCKED | RW_WRITE_WANTED;
- } else {
- RW_ASSERT(rw, op == RW_WRITER);
- incr = curthread | RW_WRITE_LOCKED;
- need_wait = RW_WRITE_LOCKED | RW_THREAD;
- }
-
- /*
- * Read the lock owner field. If the need-to-wait
- * indicator is clear, then try to acquire the lock.
- */
- owner = rw->rw_owner;
- if ((owner & need_wait) == 0) {
- next = rw_cas(rw, owner, (owner + incr) & mask);
- if (__predict_true(next == owner)) {
- /* Got it! */
- KPREEMPT_ENABLE(l);
- RW_MEMBAR_ENTER();
- return;
- }
- }
-
- rw_vector_enter(rw, op, mask, (uintptr_t)__builtin_return_address(0));
-}
-
-/*
* rw_vector_exit:
*
- * The slow path for releasing a rwlock, that considers all conditions.
- * Marked __noinline to prevent the compiler pulling it into rw_enter().
+ * Release a rwlock.
*/
-static void __noinline
+void
rw_vector_exit(krwlock_t *rw)
{
uintptr_t curthread, owner, decr, newown, next;
@@ -579,7 +442,6 @@ rw_vector_exit(krwlock_t *rw)
l = curlwp;
curthread = (uintptr_t)l;
RW_ASSERT(rw, curthread != 0);
- RW_ASSERT(rw, kpreempt_disabled());
/*
* Again, we use a trick. Since we used an add operation to
@@ -608,15 +470,9 @@ rw_vector_exit(krwlock_t *rw)
newown = (owner - decr);
if ((newown & (RW_THREAD | RW_HAS_WAITERS)) == RW_HAS_WAITERS)
break;
- /* Want spinning enabled if lock is becoming free. */
- if ((newown & RW_THREAD) == 0)
- newown |= RW_SPIN;
next = rw_cas(rw, owner, newown);
- if (__predict_true(next == owner)) {
- rw_hold_forget(rw, l);
- kpreempt_enable();
+ if (__predict_true(next == owner))
return;
- }
owner = next;
}
@@ -656,14 +512,12 @@ rw_vector_exit(krwlock_t *rw)
if (wcnt > 1)
newown |= RW_WRITE_WANTED;
rw_swap(rw, owner, newown);
- rw_hold_forget(rw, l);
turnstile_wakeup(ts, TS_WRITER_Q, 1, l);
} else {
/* Wake all writers and let them fight it out. */
newown = owner & RW_NODEBUG;
newown |= RW_WRITE_WANTED;
rw_swap(rw, owner, newown);
- rw_hold_forget(rw, l);
turnstile_wakeup(ts, TS_WRITER_Q, wcnt, NULL);
}
} else {
@@ -681,73 +535,19 @@ rw_vector_exit(krwlock_t *rw)
/* Wake up all sleeping readers. */
rw_swap(rw, owner, newown);
- rw_hold_forget(rw, l);
turnstile_wakeup(ts, TS_READER_Q, rcnt, NULL);
}
- kpreempt_enable();
}
/*
- * rw_exit:
- *
- * The fast path for releasing a lock that considers only the
- * uncontended case. Falls back to rw_vector_exit().
- */
-void
-rw_exit(krwlock_t *rw)
-{
- uintptr_t curthread, owner, decr, newown, next;
- lwp_t *l;
-
- l = curlwp;
- curthread = (uintptr_t)l;
- RW_ASSERT(rw, curthread != 0);
-
- /*
- * Again, we use a trick. Since we used an add operation to
- * set the required lock bits, we can use a subtract to clear
- * them, which makes the read-release and write-release path
- * the same.
- */
- owner = rw->rw_owner;
- if (__predict_false((owner & RW_WRITE_LOCKED) != 0)) {
- RW_UNLOCKED(rw, RW_WRITER);
- RW_ASSERT(rw, RW_OWNER(rw) == curthread);
- decr = curthread | RW_WRITE_LOCKED;
- } else {
- RW_UNLOCKED(rw, RW_READER);
- RW_ASSERT(rw, RW_COUNT(rw) != 0);
- decr = RW_READ_INCR;
- }
-
- /* Now try to release it. */
- RW_MEMBAR_EXIT();
- KPREEMPT_DISABLE(l);
- newown = (owner - decr);
- if (__predict_true((newown & (RW_THREAD | RW_HAS_WAITERS)) !=
- RW_HAS_WAITERS)) {
- /* Want spinning (re-)enabled if lock is becoming free. */
- if ((newown & RW_THREAD) == 0)
- newown |= RW_SPIN;
- next = rw_cas(rw, owner, newown);
- if (__predict_true(next == owner)) {
- rw_hold_forget(rw, l);
- KPREEMPT_ENABLE(l);
- return;
- }
- }
- rw_vector_exit(rw);
-}
-
-/*
- * rw_tryenter:
+ * rw_vector_tryenter:
*
* Try to acquire a rwlock.
*/
int
-rw_tryenter(krwlock_t *rw, const krw_t op)
+rw_vector_tryenter(krwlock_t *rw, const krw_t op)
{
- uintptr_t curthread, owner, incr, need_wait, next, mask;
+ uintptr_t curthread, owner, incr, need_wait, next;
lwp_t *l;
l = curlwp;
@@ -755,9 +555,6 @@ rw_tryenter(krwlock_t *rw, const krw_t o
RW_ASSERT(rw, curthread != 0);
- KPREEMPT_DISABLE(l);
- mask = rw_hold_remember(rw, l);
-
if (op == RW_READER) {
incr = RW_READ_INCR;
need_wait = RW_WRITE_LOCKED | RW_WRITE_WANTED;
@@ -768,12 +565,9 @@ rw_tryenter(krwlock_t *rw, const krw_t o
}
for (owner = rw->rw_owner;; owner = next) {
- if (__predict_false((owner & need_wait) != 0)) {
- rw_hold_forget(rw, l);
- KPREEMPT_ENABLE(l);
+ if (__predict_false((owner & need_wait) != 0))
return 0;
- }
- next = rw_cas(rw, owner, (owner + incr) & mask);
+ next = rw_cas(rw, owner, owner + incr);
if (__predict_true(next == owner)) {
/* Got it! */
break;
@@ -785,7 +579,6 @@ rw_tryenter(krwlock_t *rw, const krw_t o
RW_ASSERT(rw, (op != RW_READER && RW_OWNER(rw) == curthread) ||
(op == RW_READER && RW_COUNT(rw) != 0));
- KPREEMPT_ENABLE(l);
RW_MEMBAR_ENTER();
return 1;
}
@@ -823,7 +616,7 @@ rw_downgrade(krwlock_t *rw)
* waiters.
*/
if ((owner & RW_HAS_WAITERS) == 0) {
- newown = (owner & RW_NODEBUG) | RW_SPIN;
+ newown = (owner & RW_NODEBUG);
next = rw_cas(rw, owner, newown + RW_READ_INCR);
if (__predict_true(next == owner)) {
RW_LOCKED(rw, RW_READER);
@@ -850,15 +643,14 @@ rw_downgrade(krwlock_t *rw)
/*
* If there are no readers, just preserve the
* waiters bits, swap us down to one read hold and
- * return. Don't set the spin bit as nobody's
- * running yet.
+ * return.
*/
RW_ASSERT(rw, wcnt != 0);
RW_ASSERT(rw, (rw->rw_owner & RW_WRITE_WANTED) != 0);
RW_ASSERT(rw, (rw->rw_owner & RW_HAS_WAITERS) != 0);
newown = owner & RW_NODEBUG;
- newown = RW_READ_INCR | RW_HAS_WAITERS |
+ newown |= RW_READ_INCR | RW_HAS_WAITERS |
RW_WRITE_WANTED;
next = rw_cas(rw, owner, newown);
turnstile_exit(rw);
@@ -869,8 +661,7 @@ rw_downgrade(krwlock_t *rw)
* Give the lock to all blocked readers. We may
* retain one read hold if downgrading. If there is
* a writer waiting, new readers will be blocked
- * out. Don't set the spin bit as nobody's running
- * yet.
+ * out.
*/
newown = owner & RW_NODEBUG;
newown += (rcnt << RW_READ_COUNT_SHIFT) + RW_READ_INCR;
@@ -1005,14 +796,22 @@ rw_owner(wchan_t obj)
/*
* rw_owner_running:
*
- * Return true if a RW lock is unheld, or held and the owner is running
- * on a CPU. For the pagedaemon only - do not document or use in other
- * code.
+ * Return true if a RW lock is unheld, or write held and the owner is
+ * running on a CPU. For the pagedaemon.
*/
bool
rw_owner_running(const krwlock_t *rw)
{
- uintptr_t owner = rw->rw_owner;
+#ifdef MULTIPROCESSOR
+ uintptr_t owner;
+ bool rv;
- return (owner & RW_THREAD) == 0 || (owner & RW_SPIN) != 0;
+ kpreempt_disable();
+ owner = rw->rw_owner;
+ rv = (owner & RW_THREAD) == 0 || rw_oncpu(owner);
+ kpreempt_enable();
+ return rv;
+#else
+ return rw_owner(rw) == curlwp;
+#endif
}
Index: src/sys/sys/lwp.h
diff -u src/sys/sys/lwp.h:1.192.2.2 src/sys/sys/lwp.h:1.192.2.3
--- src/sys/sys/lwp.h:1.192.2.2 Sun Jan 19 21:08:30 2020
+++ src/sys/sys/lwp.h Wed Jan 22 11:40:17 2020
@@ -1,4 +1,4 @@
-/* $NetBSD: lwp.h,v 1.192.2.2 2020/01/19 21:08:30 ad Exp $ */
+/* $NetBSD: lwp.h,v 1.192.2.3 2020/01/22 11:40:17 ad Exp $ */
/*
* Copyright (c) 2001, 2006, 2007, 2008, 2009, 2010, 2019
@@ -186,7 +186,6 @@ struct lwp {
u_short l_exlocks; /* !: lockdebug: excl. locks held */
u_short l_psrefs; /* !: count of psref held */
u_short l_blcnt; /* !: count of kernel_lock held */
- struct krwlock *l_rwlocks[4]; /* !: tracks first N held rwlocks */
int l_nopreempt; /* !: don't preempt me! */
u_int l_dopreempt; /* s: kernel preemption pending */
int l_pflag; /* !: LWP private flags */
Index: src/sys/sys/rwlock.h
diff -u src/sys/sys/rwlock.h:1.12.2.1 src/sys/sys/rwlock.h:1.12.2.2
--- src/sys/sys/rwlock.h:1.12.2.1 Sun Jan 19 19:44:34 2020
+++ src/sys/sys/rwlock.h Wed Jan 22 11:40:17 2020
@@ -1,4 +1,4 @@
-/* $NetBSD: rwlock.h,v 1.12.2.1 2020/01/19 19:44:34 ad Exp $ */
+/* $NetBSD: rwlock.h,v 1.12.2.2 2020/01/22 11:40:17 ad Exp $ */
/*-
* Copyright (c) 2002, 2006, 2007, 2008, 2019, 2020 The NetBSD Foundation, Inc.
@@ -63,10 +63,10 @@ typedef struct krwlock krwlock_t;
* WRITE_LOCKED bit is clear, then the owner field is actually a count of
* the number of readers. The rw_owner field is laid out like so:
*
- * N 4 3 2 1 0
- * +---------------------------------------------------------+
- * | owner or read count | nodbug | wrlock | wrwant | wait |
- * +---------------------------------------------------------+
+ * N 5 4 3 2 1 0
+ * +------------------------------------------------------------------+
+ * | owner or read count | nodbug | <free> | wrlock | wrwant | wait |
+ * +------------------------------------------------------------------+
*/
#define RW_HAS_WAITERS 0x01UL /* lock has waiters */
#define RW_WRITE_WANTED 0x02UL /* >= 1 waiter is a writer */
@@ -84,6 +84,7 @@ void rw_vector_enter(krwlock_t *, const
void rw_vector_exit(krwlock_t *);
int rw_vector_tryenter(krwlock_t *, const krw_t);
void _rw_init(krwlock_t *, uintptr_t);
+bool rw_owner_running(const krwlock_t *);
#endif /* __RWLOCK_PRIVATE */
struct krwlock {