Sorry I have note sent this e-mail to you. Ryo ONODERA <r...@tetera.org> writes:
> Hi, > > After this commit, the kernel stalls just before root file system > will be found on my NetBSD/amd64 laptop. > > Reverting > src/sys/kern/kern_rwlock.c to r1.60 > and > src/sys/sys/rwlock.h to r1.12 > in latest -current tree and I can get the kernel that works like > before. > > And on another laptop, the problematic kernel stalls before root file > system detection like my laptop. > > It may be universal problem. > > Could you take a look at this problem? > > Thank you. > > "Andrew Doran" <a...@netbsd.org> writes: > >> Module Name: src >> Committed By: ad >> Date: Sun Jan 19 18:34:24 UTC 2020 >> >> Modified Files: >> src/sys/kern: kern_rwlock.c >> src/sys/sys: rwlock.h >> >> Log Message: >> Tidy rwlocks a bit, no functional change intended. Mainly: >> >> - rw_downgrade(): do it in a for () loop like all the others. >> - Explicitly carry around RW_NODEBUG - don't be lazy. >> - Remove pointless macros. >> - Don't make assertions conditional on LOCKDEBUG, there's no reason. >> - Make space for a new flag bit (not added yet). >> >> >> To generate a diff of this commit: >> cvs rdiff -u -r1.60 -r1.61 src/sys/kern/kern_rwlock.c >> cvs rdiff -u -r1.12 -r1.13 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/kern/kern_rwlock.c >> diff -u src/sys/kern/kern_rwlock.c:1.60 src/sys/kern/kern_rwlock.c:1.61 >> --- src/sys/kern/kern_rwlock.c:1.60 Sun Jan 12 18:37:10 2020 >> +++ src/sys/kern/kern_rwlock.c Sun Jan 19 18:34:24 2020 >> @@ -1,4 +1,4 @@ >> -/* $NetBSD: kern_rwlock.c,v 1.60 2020/01/12 18:37:10 ad Exp $ */ >> +/* $NetBSD: kern_rwlock.c,v 1.61 2020/01/19 18:34:24 ad Exp $ */ >> >> /*- >> * Copyright (c) 2002, 2006, 2007, 2008, 2009, 2019, 2020 >> @@ -39,7 +39,9 @@ >> */ >> >> #include <sys/cdefs.h> >> -__KERNEL_RCSID(0, "$NetBSD: kern_rwlock.c,v 1.60 2020/01/12 18:37:10 ad Exp >> $"); >> +__KERNEL_RCSID(0, "$NetBSD: kern_rwlock.c,v 1.61 2020/01/19 18:34:24 ad Exp >> $"); >> + >> +#include "opt_lockdebug.h" >> >> #define __RWLOCK_PRIVATE >> >> @@ -63,58 +65,32 @@ __KERNEL_RCSID(0, "$NetBSD: kern_rwlock. >> * LOCKDEBUG >> */ >> >> -#if defined(LOCKDEBUG) >> - >> -#define RW_WANTLOCK(rw, op) >> \ >> - LOCKDEBUG_WANTLOCK(RW_DEBUG_P(rw), (rw), \ >> - (uintptr_t)__builtin_return_address(0), op == RW_READER); >> -#define RW_LOCKED(rw, op) >> \ >> - LOCKDEBUG_LOCKED(RW_DEBUG_P(rw), (rw), NULL, \ >> - (uintptr_t)__builtin_return_address(0), op == RW_READER); >> -#define RW_UNLOCKED(rw, op) >> \ >> - LOCKDEBUG_UNLOCKED(RW_DEBUG_P(rw), (rw), \ >> - (uintptr_t)__builtin_return_address(0), op == RW_READER); >> -#define RW_DASSERT(rw, cond) >> \ >> -do { >> \ >> - if (__predict_false(!(cond))) \ >> - rw_abort(__func__, __LINE__, rw, "assertion failed: " #cond);\ >> -} while (/* CONSTCOND */ 0); >> - >> -#else /* LOCKDEBUG */ >> - >> -#define RW_WANTLOCK(rw, op) /* nothing */ >> -#define RW_LOCKED(rw, op) /* nothing */ >> -#define RW_UNLOCKED(rw, op) /* nothing */ >> -#define RW_DASSERT(rw, cond) /* nothing */ >> +#define RW_DEBUG_P(rw) (((rw)->rw_owner & RW_NODEBUG) == 0) >> >> -#endif /* LOCKDEBUG */ >> +#define RW_WANTLOCK(rw, op) \ >> + LOCKDEBUG_WANTLOCK(RW_DEBUG_P(rw), (rw), \ >> + (uintptr_t)__builtin_return_address(0), op == RW_READER); >> +#define RW_LOCKED(rw, op) \ >> + LOCKDEBUG_LOCKED(RW_DEBUG_P(rw), (rw), NULL, \ >> + (uintptr_t)__builtin_return_address(0), op == RW_READER); >> +#define RW_UNLOCKED(rw, op) \ >> + LOCKDEBUG_UNLOCKED(RW_DEBUG_P(rw), (rw), \ >> + (uintptr_t)__builtin_return_address(0), op == RW_READER); >> >> /* >> * DIAGNOSTIC >> */ >> >> #if defined(DIAGNOSTIC) >> - >> -#define RW_ASSERT(rw, cond) >> \ >> -do { >> \ >> - if (__predict_false(!(cond))) \ >> +#define RW_ASSERT(rw, cond) \ >> +do { \ >> + if (__predict_false(!(cond))) \ >> rw_abort(__func__, __LINE__, rw, "assertion failed: " #cond);\ >> } while (/* CONSTCOND */ 0) >> - >> #else >> - >> #define RW_ASSERT(rw, cond) /* nothing */ >> - >> #endif /* DIAGNOSTIC */ >> >> -#define RW_SETDEBUG(rw, on) ((rw)->rw_owner |= (on) ? 0 : >> RW_NODEBUG) >> -#define RW_DEBUG_P(rw) (((rw)->rw_owner & RW_NODEBUG) >> == 0) >> -#if defined(LOCKDEBUG) >> -#define RW_INHERITDEBUG(n, o) (n) |= (o) & RW_NODEBUG >> -#else /* defined(LOCKDEBUG) */ >> -#define RW_INHERITDEBUG(n, o) /* nothing */ >> -#endif /* defined(LOCKDEBUG) */ >> - >> /* >> * Memory barriers. >> */ >> @@ -128,29 +104,6 @@ do { >> \ >> #define RW_MEMBAR_PRODUCER() membar_producer() >> #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); >> - >> -static inline uintptr_t >> -rw_cas(krwlock_t *rw, uintptr_t o, uintptr_t n) >> -{ >> - >> - RW_INHERITDEBUG(n, o); >> - return (uintptr_t)atomic_cas_ptr((volatile void *)&rw->rw_owner, >> - (void *)o, (void *)n); >> -} >> - >> -static inline void >> -rw_swap(krwlock_t *rw, uintptr_t o, uintptr_t n) >> -{ >> - >> - RW_INHERITDEBUG(n, o); >> - n = (uintptr_t)atomic_swap_ptr((volatile void *)&rw->rw_owner, >> - (void *)n); >> - RW_DASSERT(rw, n == o); >> -} >> - >> /* >> * For platforms that do not provide stubs, or for the LOCKDEBUG case. >> */ >> @@ -164,6 +117,10 @@ __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); >> + >> lockops_t rwlock_lockops = { >> .lo_name = "Reader / writer lock", >> .lo_type = LOCKOPS_SLEEP, >> @@ -179,6 +136,37 @@ syncobj_t rw_syncobj = { >> }; >> >> /* >> + * rw_cas: >> + * >> + * Do an atomic compare-and-swap on the lock word. >> + */ >> +static inline uintptr_t >> +rw_cas(krwlock_t *rw, uintptr_t o, uintptr_t n) >> +{ >> + >> + return (uintptr_t)atomic_cas_ptr((volatile void *)&rw->rw_owner, >> + (void *)o, (void *)n); >> +} >> + >> +/* >> + * rw_swap: >> + * >> + * Do an atomic swap of the lock word. This is used only when it's >> + * known that the lock word is set up such that it can't be changed >> + * behind us (assert this), so there's no point considering the result. >> + */ >> +static inline void >> +rw_swap(krwlock_t *rw, uintptr_t o, uintptr_t n) >> +{ >> + >> + n = (uintptr_t)atomic_swap_ptr((volatile void *)&rw->rw_owner, >> + (void *)n); >> + >> + RW_ASSERT(rw, n == o); >> + RW_ASSERT(rw, (o & RW_HAS_WAITERS) != 0); >> +} >> + >> +/* >> * rw_dump: >> * >> * Dump the contents of a rwlock structure. >> @@ -214,16 +202,14 @@ rw_abort(const char *func, size_t line, >> * >> * Initialize a rwlock for use. >> */ >> -void _rw_init(krwlock_t *, uintptr_t); >> void >> _rw_init(krwlock_t *rw, uintptr_t return_address) >> { >> - bool dodebug; >> >> - memset(rw, 0, sizeof(*rw)); >> - >> - dodebug = LOCKDEBUG_ALLOC(rw, &rwlock_lockops, return_address); >> - RW_SETDEBUG(rw, dodebug); >> + if (LOCKDEBUG_ALLOC(rw, &rwlock_lockops, return_address)) >> + rw->rw_owner = 0; >> + else >> + rw->rw_owner = RW_NODEBUG; >> } >> >> void >> @@ -243,7 +229,7 @@ rw_destroy(krwlock_t *rw) >> { >> >> RW_ASSERT(rw, (rw->rw_owner & ~RW_NODEBUG) == 0); >> - LOCKDEBUG_FREE(RW_DEBUG_P(rw), rw); >> + LOCKDEBUG_FREE((rw->rw_owner & RW_NODEBUG) == 0, rw); >> } >> >> /* >> @@ -327,7 +313,7 @@ rw_vector_enter(krwlock_t *rw, const krw >> need_wait = RW_WRITE_LOCKED | RW_WRITE_WANTED; >> queue = TS_READER_Q; >> } else { >> - RW_DASSERT(rw, op == RW_WRITER); >> + RW_ASSERT(rw, op == RW_WRITER); >> incr = curthread | RW_WRITE_LOCKED; >> set_wait = RW_HAS_WAITERS | RW_WRITE_WANTED; >> need_wait = RW_WRITE_LOCKED | RW_THREAD; >> @@ -430,7 +416,7 @@ rw_vector_enter(krwlock_t *rw, const krw >> (uintptr_t)__builtin_return_address(0))); >> LOCKSTAT_EXIT(lsflag); >> >> - RW_DASSERT(rw, (op != RW_READER && RW_OWNER(rw) == curthread) || >> + RW_ASSERT(rw, (op != RW_READER && RW_OWNER(rw) == curthread) || >> (op == RW_READER && RW_COUNT(rw) != 0)); >> RW_LOCKED(rw, op); >> } >> @@ -448,7 +434,8 @@ rw_vector_exit(krwlock_t *rw) >> int rcnt, wcnt; >> lwp_t *l; >> >> - curthread = (uintptr_t)curlwp; >> + l = curlwp; >> + curthread = (uintptr_t)l; >> RW_ASSERT(rw, curthread != 0); >> >> /* >> @@ -491,8 +478,8 @@ rw_vector_exit(krwlock_t *rw) >> */ >> ts = turnstile_lookup(rw); >> owner = rw->rw_owner; >> - RW_DASSERT(rw, ts != NULL); >> - RW_DASSERT(rw, (owner & RW_HAS_WAITERS) != 0); >> + RW_ASSERT(rw, ts != NULL); >> + RW_ASSERT(rw, (owner & RW_HAS_WAITERS) != 0); >> >> wcnt = TS_WAITERS(ts, TS_WRITER_Q); >> rcnt = TS_WAITERS(ts, TS_READER_Q); >> @@ -509,31 +496,35 @@ rw_vector_exit(krwlock_t *rw) >> * do the work of acquiring the lock in rw_vector_enter(). >> */ >> if (rcnt == 0 || decr == RW_READ_INCR) { >> - RW_DASSERT(rw, wcnt != 0); >> - RW_DASSERT(rw, (owner & RW_WRITE_WANTED) != 0); >> + RW_ASSERT(rw, wcnt != 0); >> + RW_ASSERT(rw, (owner & RW_WRITE_WANTED) != 0); >> >> if (rcnt != 0) { >> /* Give the lock to the longest waiting writer. */ >> l = TS_FIRST(ts, TS_WRITER_Q); >> - newown = (uintptr_t)l | RW_WRITE_LOCKED | >> RW_HAS_WAITERS; >> + newown = (uintptr_t)l | (owner & RW_NODEBUG); >> + newown |= RW_WRITE_LOCKED | RW_HAS_WAITERS; >> if (wcnt > 1) >> newown |= RW_WRITE_WANTED; >> rw_swap(rw, owner, newown); >> turnstile_wakeup(ts, TS_WRITER_Q, 1, l); >> } else { >> /* Wake all writers and let them fight it out. */ >> - rw_swap(rw, owner, RW_WRITE_WANTED); >> + newown = owner & RW_NODEBUG; >> + newown |= RW_WRITE_WANTED; >> + rw_swap(rw, owner, newown); >> turnstile_wakeup(ts, TS_WRITER_Q, wcnt, NULL); >> } >> } else { >> - RW_DASSERT(rw, rcnt != 0); >> + RW_ASSERT(rw, rcnt != 0); >> >> /* >> * Give the lock to all blocked readers. If there >> * is a writer waiting, new readers that arrive >> * after the release will be blocked out. >> */ >> - newown = rcnt << RW_READ_COUNT_SHIFT; >> + newown = owner & RW_NODEBUG; >> + newown += rcnt << RW_READ_COUNT_SHIFT; >> if (wcnt != 0) >> newown |= RW_HAS_WAITERS | RW_WRITE_WANTED; >> >> @@ -552,8 +543,10 @@ int >> rw_vector_tryenter(krwlock_t *rw, const krw_t op) >> { >> uintptr_t curthread, owner, incr, need_wait, next; >> + lwp_t *l; >> >> - curthread = (uintptr_t)curlwp; >> + l = curlwp; >> + curthread = (uintptr_t)l; >> >> RW_ASSERT(rw, curthread != 0); >> >> @@ -561,7 +554,7 @@ rw_vector_tryenter(krwlock_t *rw, const >> incr = RW_READ_INCR; >> need_wait = RW_WRITE_LOCKED | RW_WRITE_WANTED; >> } else { >> - RW_DASSERT(rw, op == RW_WRITER); >> + RW_ASSERT(rw, op == RW_WRITER); >> incr = curthread | RW_WRITE_LOCKED; >> need_wait = RW_WRITE_LOCKED | RW_THREAD; >> } >> @@ -572,24 +565,23 @@ rw_vector_tryenter(krwlock_t *rw, const >> next = rw_cas(rw, owner, owner + incr); >> if (__predict_true(next == owner)) { >> /* Got it! */ >> - RW_MEMBAR_ENTER(); >> break; >> } >> } >> >> RW_WANTLOCK(rw, op); >> RW_LOCKED(rw, op); >> - RW_DASSERT(rw, (op != RW_READER && RW_OWNER(rw) == curthread) || >> + RW_ASSERT(rw, (op != RW_READER && RW_OWNER(rw) == curthread) || >> (op == RW_READER && RW_COUNT(rw) != 0)); >> >> + RW_MEMBAR_ENTER(); >> return 1; >> } >> >> /* >> * rw_downgrade: >> * >> - * Downgrade a write lock to a read lock. Optimise memory accesses for >> - * the uncontended case. >> + * Downgrade a write lock to a read lock. >> */ >> void >> rw_downgrade(krwlock_t *rw) >> @@ -597,55 +589,64 @@ rw_downgrade(krwlock_t *rw) >> uintptr_t owner, curthread, newown, next; >> turnstile_t *ts; >> int rcnt, wcnt; >> + lwp_t *l; >> >> - curthread = (uintptr_t)curlwp; >> + l = curlwp; >> + curthread = (uintptr_t)l; >> RW_ASSERT(rw, curthread != 0); >> - RW_DASSERT(rw, (rw->rw_owner & RW_WRITE_LOCKED) != 0); >> + RW_ASSERT(rw, (rw->rw_owner & RW_WRITE_LOCKED) != 0); >> RW_ASSERT(rw, RW_OWNER(rw) == curthread); >> RW_UNLOCKED(rw, RW_WRITER); >> #if !defined(DIAGNOSTIC) >> __USE(curthread); >> #endif >> >> - /* >> - * If there are no waiters, so we can do this the easy way. >> - * Try swapping us down to one read hold. If it fails, the >> - * lock condition has changed and we most likely now have >> - * waiters. >> - */ >> RW_MEMBAR_PRODUCER(); >> - owner = curthread | RW_WRITE_LOCKED; >> - next = rw_cas(rw, owner, RW_READ_INCR); >> - if (__predict_true(next == owner)) { >> - RW_LOCKED(rw, RW_READER); >> - RW_DASSERT(rw, (rw->rw_owner & RW_WRITE_LOCKED) == 0); >> - RW_DASSERT(rw, RW_COUNT(rw) != 0); >> - return; >> - } >> >> - /* >> - * Grab the turnstile chain lock. This gets the interlock >> - * on the sleep queue. Once we have that, we can adjust the >> - * waiter bits. >> - */ >> - for (;;) { >> - owner = next; >> + for (owner = rw->rw_owner;; owner = next) { >> + /* >> + * If there are no waiters we can do this the easy way. Try >> + * swapping us down to one read hold. If it fails, the lock >> + * condition has changed and we most likely now have >> + * waiters. >> + */ >> + if ((owner & RW_HAS_WAITERS) == 0) { >> + newown = (owner & RW_NODEBUG); >> + next = rw_cas(rw, owner, newown + RW_READ_INCR); >> + if (__predict_true(next == owner)) { >> + RW_LOCKED(rw, RW_READER); >> + RW_ASSERT(rw, >> + (rw->rw_owner & RW_WRITE_LOCKED) == 0); >> + RW_ASSERT(rw, RW_COUNT(rw) != 0); >> + return; >> + } >> + continue; >> + } >> + >> + /* >> + * Grab the turnstile chain lock. This gets the interlock >> + * on the sleep queue. Once we have that, we can adjust the >> + * waiter bits. >> + */ >> ts = turnstile_lookup(rw); >> - RW_DASSERT(rw, ts != NULL); >> + RW_ASSERT(rw, ts != NULL); >> >> rcnt = TS_WAITERS(ts, TS_READER_Q); >> wcnt = TS_WAITERS(ts, TS_WRITER_Q); >> >> - /* >> - * If there are no readers, just preserve the waiters >> - * bits, swap us down to one read hold and return. >> - */ >> if (rcnt == 0) { >> - RW_DASSERT(rw, wcnt != 0); >> - RW_DASSERT(rw, (rw->rw_owner & RW_WRITE_WANTED) != 0); >> - RW_DASSERT(rw, (rw->rw_owner & RW_HAS_WAITERS) != 0); >> - >> - newown = RW_READ_INCR | RW_HAS_WAITERS | >> RW_WRITE_WANTED; >> + /* >> + * If there are no readers, just preserve the >> + * waiters bits, swap us down to one read hold and >> + * 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 | >> + RW_WRITE_WANTED; >> next = rw_cas(rw, owner, newown); >> turnstile_exit(rw); >> if (__predict_true(next == owner)) >> @@ -653,11 +654,12 @@ rw_downgrade(krwlock_t *rw) >> } else { >> /* >> * 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 >> + * retain one read hold if downgrading. If there is >> + * a writer waiting, new readers will be blocked >> * out. >> */ >> - newown = (rcnt << RW_READ_COUNT_SHIFT) + RW_READ_INCR; >> + newown = owner & RW_NODEBUG; >> + newown += (rcnt << RW_READ_COUNT_SHIFT) + RW_READ_INCR; >> if (wcnt != 0) >> newown |= RW_HAS_WAITERS | RW_WRITE_WANTED; >> >> @@ -673,22 +675,24 @@ rw_downgrade(krwlock_t *rw) >> >> RW_WANTLOCK(rw, RW_READER); >> RW_LOCKED(rw, RW_READER); >> - RW_DASSERT(rw, (rw->rw_owner & RW_WRITE_LOCKED) == 0); >> - RW_DASSERT(rw, RW_COUNT(rw) != 0); >> + RW_ASSERT(rw, (rw->rw_owner & RW_WRITE_LOCKED) == 0); >> + RW_ASSERT(rw, RW_COUNT(rw) != 0); >> } >> >> /* >> * rw_tryupgrade: >> * >> * Try to upgrade a read lock to a write lock. We must be the only >> - * reader. Optimise memory accesses for the uncontended case. >> + * reader. >> */ >> int >> rw_tryupgrade(krwlock_t *rw) >> { >> uintptr_t owner, curthread, newown, next; >> + struct lwp *l; >> >> - curthread = (uintptr_t)curlwp; >> + l = curlwp; >> + curthread = (uintptr_t)l; >> RW_ASSERT(rw, curthread != 0); >> RW_ASSERT(rw, rw_read_held(rw)); >> >> @@ -709,8 +713,8 @@ rw_tryupgrade(krwlock_t *rw) >> RW_UNLOCKED(rw, RW_READER); >> RW_WANTLOCK(rw, RW_WRITER); >> RW_LOCKED(rw, RW_WRITER); >> - RW_DASSERT(rw, rw->rw_owner & RW_WRITE_LOCKED); >> - RW_DASSERT(rw, RW_OWNER(rw) == curthread); >> + RW_ASSERT(rw, rw->rw_owner & RW_WRITE_LOCKED); >> + RW_ASSERT(rw, RW_OWNER(rw) == curthread); >> >> return 1; >> } >> >> Index: src/sys/sys/rwlock.h >> diff -u src/sys/sys/rwlock.h:1.12 src/sys/sys/rwlock.h:1.13 >> --- src/sys/sys/rwlock.h:1.12 Wed Jan 1 21:34:39 2020 >> +++ src/sys/sys/rwlock.h Sun Jan 19 18:34:24 2020 >> @@ -1,7 +1,7 @@ >> -/* $NetBSD: rwlock.h,v 1.12 2020/01/01 21:34:39 ad Exp $ */ >> +/* $NetBSD: rwlock.h,v 1.13 2020/01/19 18:34:24 ad Exp $ */ >> >> /*- >> - * Copyright (c) 2002, 2006, 2007, 2008, 2019 The NetBSD Foundation, Inc. >> + * Copyright (c) 2002, 2006, 2007, 2008, 2019, 2020 The NetBSD Foundation, >> Inc. >> * All rights reserved. >> * >> * This code is derived from software contributed to The NetBSD Foundation >> @@ -45,10 +45,6 @@ >> * rw_tryenter() >> */ >> >> -#if defined(_KERNEL_OPT) >> -#include "opt_lockdebug.h" >> -#endif >> - >> #if !defined(_KERNEL) >> #include <sys/types.h> >> #include <sys/inttypes.h> >> @@ -75,13 +71,9 @@ typedef struct krwlock krwlock_t; >> #define RW_HAS_WAITERS 0x01UL /* lock has waiters */ >> #define RW_WRITE_WANTED 0x02UL /* >= 1 waiter is a writer */ >> #define RW_WRITE_LOCKED 0x04UL /* lock is currently write >> locked */ >> -#if defined(LOCKDEBUG) >> -#define RW_NODEBUG 0x08UL /* LOCKDEBUG disabled */ >> -#else >> -#define RW_NODEBUG 0x00UL /* do nothing */ >> -#endif /* LOCKDEBUG */ >> +#define RW_NODEBUG 0x10UL /* LOCKDEBUG disabled */ >> >> -#define RW_READ_COUNT_SHIFT 4 >> +#define RW_READ_COUNT_SHIFT 5 >> #define RW_READ_INCR (1UL << RW_READ_COUNT_SHIFT) >> #define RW_THREAD ((uintptr_t)-RW_READ_INCR) >> #define RW_OWNER(rw) ((rw)->rw_owner & RW_THREAD) >> @@ -91,6 +83,7 @@ typedef struct krwlock krwlock_t; >> void rw_vector_enter(krwlock_t *, const krw_t); >> void rw_vector_exit(krwlock_t *); >> int rw_vector_tryenter(krwlock_t *, const krw_t); >> +void _rw_init(krwlock_t *, uintptr_t); >> #endif /* __RWLOCK_PRIVATE */ >> >> struct krwlock { >> > > -- > Ryo ONODERA // r...@tetera.org > PGP fingerprint = 82A2 DC91 76E0 A10A 8ABB FD1B F404 27FA C7D1 15F3 -- Ryo ONODERA // r...@tetera.org PGP fingerprint = 82A2 DC91 76E0 A10A 8ABB FD1B F404 27FA C7D1 15F3