Index: sys/sys/_rwlock.h =================================================================== --- sys/sys/_rwlock.h (revisione 242483) +++ sys/sys/_rwlock.h (copia locale) @@ -32,12 +32,35 @@ #ifndef _SYS__RWLOCK_H_ #define _SYS__RWLOCK_H_ +#include + /* * Reader/writer lock. + * + * The layout of the first 2 members of struct rwlock* is considered fixed. + * More specifically, it is assumed that there is a member called rw_lock + * for every struct rwlock* and that other locking primitive structures are + * not allowed to use such name for their members. + * If this needs to change, the bits in the rwlock implementation might be + * modified appropriately. */ struct rwlock { struct lock_object lock_object; volatile uintptr_t rw_lock; }; +/* + * Members of struct rwlock_padalign must mirror members of struct rwlock. + * rwlock_padalign rwlocks can use rwlock(9) KPI transparently, without + * modifies. + * When using pad-aligned rwlocks within structures, they should generally + * stay as the first member of the struct. This is because otherwise the + * compiler can generate ever more padding for the struct to keep a correct + * alignment for the rwlock. + */ +struct rwlock_padalign { + struct lock_object lock_object; + volatile uintptr_t rw_lock; +} __aligned(CACHE_LINE_SIZE); + #endif /* !_SYS__RWLOCK_H_ */ Index: sys/sys/rwlock.h =================================================================== --- sys/sys/rwlock.h (revisione 242483) +++ sys/sys/rwlock.h (copia locale) @@ -121,29 +121,67 @@ * external API and should not be called directly. Wrapper macros should * be used instead. */ - -#define rw_init(rw, name) rw_init_flags((rw), (name), 0) -void rw_init_flags(struct rwlock *rw, const char *name, int opts); -void rw_destroy(struct rwlock *rw); +void _rw_init_flags(volatile uintptr_t *c, const char *name, int opts); +void _rw_destroy(volatile uintptr_t *c); void rw_sysinit(void *arg); void rw_sysinit_flags(void *arg); -int rw_wowned(const struct rwlock *rw); -void _rw_wlock(struct rwlock *rw, const char *file, int line); -int _rw_try_wlock(struct rwlock *rw, const char *file, int line); -void _rw_wunlock(struct rwlock *rw, const char *file, int line); -void _rw_rlock(struct rwlock *rw, const char *file, int line); -int _rw_try_rlock(struct rwlock *rw, const char *file, int line); -void _rw_runlock(struct rwlock *rw, const char *file, int line); -void _rw_wlock_hard(struct rwlock *rw, uintptr_t tid, const char *file, +int _rw_wowned(const volatile uintptr_t *c); +void _rw_wlock_cookie(volatile uintptr_t *c, const char *file, int line); +int __rw_try_wlock(volatile uintptr_t *c, const char *file, int line); +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_wunlock_hard(struct rwlock *rw, uintptr_t tid, const char *file, +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); +void __rw_downgrade(volatile uintptr_t *c, const char *file, int line); +#if defined(INVARIANTS) || defined(INVARIANT_SUPPORT) +void __rw_assert(const volatile uintptr_t *c, int what, const char *file, int line); -int _rw_try_upgrade(struct rwlock *rw, const char *file, int line); -void _rw_downgrade(struct rwlock *rw, const char *file, int line); +#endif + +/* + * Top-level macros to provide lock cookie once the actual rwlock is passed. + * They will also prevent passing a malformed object to the rwlock KPI by + * failing compilation as the rw_lock reserved member will not be found. + */ +#define rw_init(rw, n) \ + _rw_init_flags(&(rw)->rw_lock, n, 0) +#define rw_init_flags(rw, n, o) \ + _rw_init_flags(&(rw)->rw_lock, n, o) +#define rw_destroy(rw) \ + _rw_destroy(&(rw)->rw_lock) +#define rw_wowned(rw) \ + _rw_wowned(&(rw)->rw_lock) +#define _rw_wlock(rw, f, l) \ + _rw_wlock_cookie(&(rw)->rw_lock, f, l) +#define _rw_try_wlock(rw, f, l) \ + __rw_try_wlock(&(rw)->rw_lock, f, l) +#define _rw_wunlock(rw, f, l) \ + _rw_wunlock_cookie(&(rw)->rw_lock, f, l) +#define _rw_rlock(rw, f, l) \ + __rw_rlock(&(rw)->rw_lock, f, l) +#define _rw_try_rlock(rw, f, l) \ + __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_wunlock_hard(rw, t, f, l) \ + __rw_wunlock_hard(&(rw)->rw_lock, t, f, l) +#define _rw_try_upgrade(rw, f, l) \ + __rw_try_upgrade(&(rw)->rw_lock, f, l) +#define _rw_downgrade(rw, f, l) \ + __rw_downgrade(&(rw)->rw_lock, f, l) #if defined(INVARIANTS) || defined(INVARIANT_SUPPORT) -void _rw_assert(const struct rwlock *rw, int what, const char *file, int line); +#define _rw_assert(rw, w, f, l) \ + __rw_assert(&(rw)->rw_lock, w, f, l) #endif + /* * Public interface for lock operations. */ @@ -178,12 +216,12 @@ void rw_sysinit_flags(void *arg); #define rw_initialized(rw) lock_initalized(&(rw)->lock_object) struct rw_args { - struct rwlock *ra_rw; + void *ra_rw; const char *ra_desc; }; struct rw_args_flags { - struct rwlock *ra_rw; + void *ra_rw; const char *ra_desc; int ra_flags; }; @@ -196,7 +234,7 @@ struct rw_args_flags { SYSINIT(name##_rw_sysinit, SI_SUB_LOCK, SI_ORDER_MIDDLE, \ rw_sysinit, &name##_args); \ SYSUNINIT(name##_rw_sysuninit, SI_SUB_LOCK, SI_ORDER_MIDDLE, \ - rw_destroy, (rw)) + _rw_destroy, __DEVOLATILE(void *, &(rw)->rw_lock)) #define RW_SYSINIT_FLAGS(name, rw, desc, flags) \ @@ -208,7 +246,7 @@ struct rw_args_flags { SYSINIT(name##_rw_sysinit, SI_SUB_LOCK, SI_ORDER_MIDDLE, \ rw_sysinit_flags, &name##_args); \ SYSUNINIT(name##_rw_sysuninit, SI_SUB_LOCK, SI_ORDER_MIDDLE, \ - rw_destroy, (rw)) + _rw_destroy, __DEVOLATILE(void *, &(rw)->rw_lock)) /* * Options passed to rw_init_flags(). Index: sys/kern/kern_rwlock.c =================================================================== --- sys/kern/kern_rwlock.c (revisione 242483) +++ sys/kern/kern_rwlock.c (copia locale) @@ -61,6 +61,12 @@ __FBSDID("$FreeBSD$"); PMC_SOFT_DECLARE( , , lock, failed); #endif +/* + * Return the rwlock address when the lock cookie address is provided. + * This functionality assumes that struct rwlock* have a member named rw_lock. + */ +#define rwlock2rw(c) (__containerof(c, struct rwlock, rw_lock)) + #ifdef ADAPTIVE_RWLOCKS static int rowner_retries = 10; static int rowner_loops = 10000; @@ -123,7 +129,7 @@ struct lock_class lock_class_rw = { #define rw_owner(rw) rw_wowner(rw) #ifndef INVARIANTS -#define _rw_assert(rw, what, file, line) +#define __rw_assert(c, what, file, line) #endif void @@ -175,10 +181,13 @@ owner_rw(const struct lock_object *lock, struct th #endif void -rw_init_flags(struct rwlock *rw, const char *name, int opts) +_rw_init_flags(volatile uintptr_t *c, const char *name, int opts) { + struct rwlock *rw; int flags; + rw = rwlock2rw(c); + MPASS((opts & ~(RW_DUPOK | RW_NOPROFILE | RW_NOWITNESS | RW_QUIET | RW_RECURSE)) == 0); ASSERT_ATOMIC_LOAD_PTR(rw->rw_lock, @@ -203,9 +212,12 @@ void } void -rw_destroy(struct rwlock *rw) +_rw_destroy(volatile uintptr_t *c) { + struct rwlock *rw; + rw = rwlock2rw(c); + KASSERT(rw->rw_lock == RW_UNLOCKED, ("rw lock %p not unlocked", rw)); KASSERT(rw->rw_recurse == 0, ("rw lock %p still recursed", rw)); rw->rw_lock = RW_DESTROYED; @@ -217,7 +229,7 @@ rw_sysinit(void *arg) { struct rw_args *args = arg; - rw_init(args->ra_rw, args->ra_desc); + rw_init((struct rwlock *)args->ra_rw, args->ra_desc); } void @@ -225,22 +237,27 @@ rw_sysinit_flags(void *arg) { struct rw_args_flags *args = arg; - rw_init_flags(args->ra_rw, args->ra_desc, args->ra_flags); + rw_init_flags((struct rwlock *)args->ra_rw, args->ra_desc, + args->ra_flags); } int -rw_wowned(const struct rwlock *rw) +_rw_wowned(const volatile uintptr_t *c) { - return (rw_wowner(rw) == curthread); + return (rw_wowner(rwlock2rw(c)) == curthread); } void -_rw_wlock(struct rwlock *rw, const char *file, int line) +_rw_wlock_cookie(volatile uintptr_t *c, const char *file, int line) { + struct rwlock *rw; if (SCHEDULER_STOPPED()) return; + + rw = rwlock2rw(c); + KASSERT(!TD_IS_IDLETHREAD(curthread), ("rw_wlock() by idle thread %p on rwlock %s @ %s:%d", curthread, rw->lock_object.lo_name, file, line)); @@ -255,13 +272,16 @@ void } int -_rw_try_wlock(struct rwlock *rw, const char *file, int line) +__rw_try_wlock(volatile uintptr_t *c, const char *file, int line) { + struct rwlock *rw; int rval; if (SCHEDULER_STOPPED()) return (1); + rw = rwlock2rw(c); + KASSERT(!TD_IS_IDLETHREAD(curthread), ("rw_try_wlock() by idle thread %p on rwlock %s @ %s:%d", curthread, rw->lock_object.lo_name, file, line)); @@ -286,14 +306,18 @@ int } void -_rw_wunlock(struct rwlock *rw, const char *file, int line) +_rw_wunlock_cookie(volatile uintptr_t *c, const char *file, int line) { + struct rwlock *rw; if (SCHEDULER_STOPPED()) return; + + rw = rwlock2rw(c); + KASSERT(rw->rw_lock != RW_DESTROYED, ("rw_wunlock() of destroyed rwlock @ %s:%d", file, line)); - _rw_assert(rw, RA_WLOCKED, file, line); + __rw_assert(c, RA_WLOCKED, file, line); curthread->td_locks--; WITNESS_UNLOCK(&rw->lock_object, LOP_EXCLUSIVE, file, line); LOCK_LOG_LOCK("WUNLOCK", &rw->lock_object, 0, rw->rw_recurse, file, @@ -315,8 +339,9 @@ void RW_LOCK_READ) void -_rw_rlock(struct rwlock *rw, const char *file, int line) +__rw_rlock(volatile uintptr_t *c, const char *file, int line) { + struct rwlock *rw; struct turnstile *ts; #ifdef ADAPTIVE_RWLOCKS volatile struct thread *owner; @@ -337,6 +362,8 @@ void if (SCHEDULER_STOPPED()) return; + rw = rwlock2rw(c); + KASSERT(!TD_IS_IDLETHREAD(curthread), ("rw_rlock() by idle thread %p on rwlock %s @ %s:%d", curthread, rw->lock_object.lo_name, file, line)); @@ -521,13 +548,16 @@ void } int -_rw_try_rlock(struct rwlock *rw, const char *file, int line) +__rw_try_rlock(volatile uintptr_t *c, const char *file, int line) { + struct rwlock *rw; uintptr_t x; if (SCHEDULER_STOPPED()) return (1); + rw = rwlock2rw(c); + KASSERT(!TD_IS_IDLETHREAD(curthread), ("rw_try_rlock() by idle thread %p on rwlock %s @ %s:%d", curthread, rw->lock_object.lo_name, file, line)); @@ -553,17 +583,20 @@ int } void -_rw_runlock(struct rwlock *rw, const char *file, int line) +_rw_runlock_cookie(volatile uintptr_t *c, const char *file, int line) { + struct rwlock *rw; struct turnstile *ts; uintptr_t x, v, queue; if (SCHEDULER_STOPPED()) return; + rw = rwlock2rw(c); + KASSERT(rw->rw_lock != RW_DESTROYED, ("rw_runlock() of destroyed rwlock @ %s:%d", file, line)); - _rw_assert(rw, RA_RLOCKED, file, line); + __rw_assert(c, RA_RLOCKED, file, line); curthread->td_locks--; curthread->td_rw_rlocks--; WITNESS_UNLOCK(&rw->lock_object, 0, file, line); @@ -667,8 +700,10 @@ void * read or write lock. */ void -_rw_wlock_hard(struct rwlock *rw, uintptr_t tid, const char *file, int line) +__rw_wlock_hard(volatile uintptr_t *c, uintptr_t tid, const char *file, + int line) { + struct rwlock *rw; struct turnstile *ts; #ifdef ADAPTIVE_RWLOCKS volatile struct thread *owner; @@ -689,6 +724,8 @@ void if (SCHEDULER_STOPPED()) return; + rw = rwlock2rw(c); + if (rw_wlocked(rw)) { KASSERT(rw->lock_object.lo_flags & LO_RECURSABLE, ("%s: recursing but non-recursive rw %s @ %s:%d\n", @@ -850,8 +887,10 @@ void * least one thread is waiting on this lock. */ void -_rw_wunlock_hard(struct rwlock *rw, uintptr_t tid, const char *file, int line) +__rw_wunlock_hard(volatile uintptr_t *c, uintptr_t tid, const char *file, + int line) { + struct rwlock *rw; struct turnstile *ts; uintptr_t v; int queue; @@ -859,6 +898,8 @@ void if (SCHEDULER_STOPPED()) return; + rw = rwlock2rw(c); + if (rw_wlocked(rw) && rw_recursed(rw)) { rw->rw_recurse--; if (LOCK_LOG_TEST(&rw->lock_object, 0)) @@ -915,8 +956,9 @@ void * lock. Returns true if the upgrade succeeded and false otherwise. */ int -_rw_try_upgrade(struct rwlock *rw, const char *file, int line) +__rw_try_upgrade(volatile uintptr_t *c, const char *file, int line) { + struct rwlock *rw; uintptr_t v, x, tid; struct turnstile *ts; int success; @@ -924,9 +966,11 @@ int if (SCHEDULER_STOPPED()) return (1); + rw = rwlock2rw(c); + KASSERT(rw->rw_lock != RW_DESTROYED, ("rw_try_upgrade() of destroyed rwlock @ %s:%d", file, line)); - _rw_assert(rw, RA_RLOCKED, file, line); + __rw_assert(c, RA_RLOCKED, file, line); /* * Attempt to switch from one reader to a writer. If there @@ -988,8 +1032,9 @@ int * Downgrade a write lock into a single read lock. */ void -_rw_downgrade(struct rwlock *rw, const char *file, int line) +__rw_downgrade(volatile uintptr_t *c, const char *file, int line) { + struct rwlock *rw; struct turnstile *ts; uintptr_t tid, v; int rwait, wwait; @@ -997,9 +1042,11 @@ void if (SCHEDULER_STOPPED()) return; + rw = rwlock2rw(c); + KASSERT(rw->rw_lock != RW_DESTROYED, ("rw_downgrade() of destroyed rwlock @ %s:%d", file, line)); - _rw_assert(rw, RA_WLOCKED | RA_NOTRECURSED, file, line); + __rw_assert(c, RA_WLOCKED | RA_NOTRECURSED, file, line); #ifndef INVARIANTS if (rw_recursed(rw)) panic("downgrade of a recursed lock"); @@ -1053,7 +1100,7 @@ out: #ifdef INVARIANT_SUPPORT #ifndef INVARIANTS -#undef _rw_assert +#undef __rw_assert #endif /* @@ -1062,11 +1109,15 @@ out: * thread owns an rlock. */ void -_rw_assert(const struct rwlock *rw, int what, const char *file, int line) +__rw_assert(const volatile uintptr_t *c, int what, const char *file, int line) { + const struct rwlock *rw; if (panicstr != NULL) return; + + rw = rwlock2rw(c); + switch (what) { case RA_LOCKED: case RA_LOCKED | RA_RECURSED: