237 lines
7.3 KiB
C
237 lines
7.3 KiB
C
/* $OpenBSD: rwlock.h,v 1.28 2021/01/11 18:49:38 mpi Exp $ */
|
|
/*
|
|
* Copyright (c) 2002 Artur Grabowski <art@openbsd.org>
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
/*
|
|
* Multiple readers, single writer lock.
|
|
*
|
|
* Simplistic implementation modelled after rw locks in Solaris.
|
|
*
|
|
* The rwl_owner has the following layout:
|
|
* [ owner or count of readers | wrlock | wrwant | wait ]
|
|
*
|
|
* When the WAIT bit is set (bit 0), the lock has waiters sleeping on it.
|
|
* When the WRWANT bit is set (bit 1), at least one waiter wants a write lock.
|
|
* When the WRLOCK bit is set (bit 2) the lock is currently write-locked.
|
|
*
|
|
* When write locked, the upper bits contain the struct proc * pointer to
|
|
* the writer, otherwise they count the number of readers.
|
|
*
|
|
* We provide a simple machine independent implementation:
|
|
*
|
|
* void rw_enter_read(struct rwlock *)
|
|
* atomically test for RWLOCK_WRLOCK and if not set, increment the lock
|
|
* by RWLOCK_READ_INCR. While RWLOCK_WRLOCK is set, loop into rw_enter_wait.
|
|
*
|
|
* void rw_enter_write(struct rwlock *);
|
|
* atomically test for the lock being 0 (it's not possible to have
|
|
* owner/read count unset and waiter bits set) and if 0 set the owner to
|
|
* the proc and RWLOCK_WRLOCK. While not zero, loop into rw_enter_wait.
|
|
*
|
|
* void rw_exit_read(struct rwlock *);
|
|
* atomically decrement lock by RWLOCK_READ_INCR and unset RWLOCK_WAIT and
|
|
* RWLOCK_WRWANT remembering the old value of lock and if RWLOCK_WAIT was set,
|
|
* call rw_exit_waiters with the old contents of the lock.
|
|
*
|
|
* void rw_exit_write(struct rwlock *);
|
|
* atomically swap the contents of the lock with 0 and if RWLOCK_WAIT was
|
|
* set, call rw_exit_waiters with the old contents of the lock.
|
|
*/
|
|
|
|
#ifndef _SYS_RWLOCK_H
|
|
#define _SYS_RWLOCK_H
|
|
|
|
#include <sys/_lock.h>
|
|
|
|
struct proc;
|
|
|
|
struct rwlock {
|
|
volatile unsigned long rwl_owner;
|
|
const char *rwl_name;
|
|
#ifdef WITNESS
|
|
struct lock_object rwl_lock_obj;
|
|
#endif
|
|
};
|
|
|
|
#define RWLOCK_LO_FLAGS(flags) \
|
|
((ISSET(flags, RWL_DUPOK) ? LO_DUPOK : 0) | \
|
|
(ISSET(flags, RWL_NOWITNESS) ? 0 : LO_WITNESS) | \
|
|
(ISSET(flags, RWL_IS_VNODE) ? LO_IS_VNODE : 0) | \
|
|
LO_INITIALIZED | LO_SLEEPABLE | LO_UPGRADABLE | \
|
|
(LO_CLASS_RWLOCK << LO_CLASSSHIFT))
|
|
|
|
#define RRWLOCK_LO_FLAGS(flags) \
|
|
((ISSET(flags, RWL_DUPOK) ? LO_DUPOK : 0) | \
|
|
(ISSET(flags, RWL_NOWITNESS) ? 0 : LO_WITNESS) | \
|
|
(ISSET(flags, RWL_IS_VNODE) ? LO_IS_VNODE : 0) | \
|
|
LO_INITIALIZED | LO_RECURSABLE | LO_SLEEPABLE | LO_UPGRADABLE | \
|
|
(LO_CLASS_RRWLOCK << LO_CLASSSHIFT))
|
|
|
|
#define RWLOCK_LO_INITIALIZER(name, flags) \
|
|
{ .lo_type = &(const struct lock_type){ .lt_name = name }, \
|
|
.lo_name = (name), \
|
|
.lo_flags = RWLOCK_LO_FLAGS(flags) }
|
|
|
|
#define RWL_DUPOK 0x01
|
|
#define RWL_NOWITNESS 0x02
|
|
#define RWL_IS_VNODE 0x04
|
|
|
|
#ifdef WITNESS
|
|
#define RWLOCK_INITIALIZER(name) \
|
|
{ 0, name, .rwl_lock_obj = RWLOCK_LO_INITIALIZER(name, 0) }
|
|
#else
|
|
#define RWLOCK_INITIALIZER(name) \
|
|
{ 0, name }
|
|
#endif
|
|
|
|
#define RWLOCK_WAIT 0x01UL
|
|
#define RWLOCK_WRWANT 0x02UL
|
|
#define RWLOCK_WRLOCK 0x04UL
|
|
#define RWLOCK_MASK 0x07UL
|
|
|
|
#define RWLOCK_OWNER(rwl) ((struct proc *)((rwl)->rwl_owner & ~RWLOCK_MASK))
|
|
|
|
#define RWLOCK_READER_SHIFT 3UL
|
|
#define RWLOCK_READ_INCR (1UL << RWLOCK_READER_SHIFT)
|
|
|
|
#define RW_WRITE 0x0001UL /* exclusive lock */
|
|
#define RW_READ 0x0002UL /* shared lock */
|
|
#define RW_DOWNGRADE 0x0004UL /* downgrade exclusive to shared */
|
|
#define RW_OPMASK 0x0007UL
|
|
|
|
#define RW_INTR 0x0010UL /* interruptible sleep */
|
|
#define RW_SLEEPFAIL 0x0020UL /* fail if we slept for the lock */
|
|
#define RW_NOSLEEP 0x0040UL /* don't wait for the lock */
|
|
#define RW_RECURSEFAIL 0x0080UL /* Fail on recursion for RRW locks. */
|
|
#define RW_DUPOK 0x0100UL /* Permit duplicate lock */
|
|
|
|
/*
|
|
* for rw_status() and rrw_status() only: exclusive lock held by
|
|
* some other thread
|
|
*/
|
|
#define RW_WRITE_OTHER 0x0100UL
|
|
|
|
/* recursive rwlocks; */
|
|
struct rrwlock {
|
|
struct rwlock rrwl_lock;
|
|
uint32_t rrwl_wcnt; /* # writers. */
|
|
};
|
|
|
|
#ifdef _KERNEL
|
|
|
|
void _rw_init_flags(struct rwlock *, const char *, int,
|
|
const struct lock_type *);
|
|
|
|
#ifdef WITNESS
|
|
#define rw_init_flags(rwl, name, flags) do { \
|
|
static const struct lock_type __lock_type = { .lt_name = #rwl };\
|
|
_rw_init_flags(rwl, name, flags, &__lock_type); \
|
|
} while (0)
|
|
#define rw_init(rwl, name) rw_init_flags(rwl, name, 0)
|
|
#else /* WITNESS */
|
|
#define rw_init_flags(rwl, name, flags) \
|
|
_rw_init_flags(rwl, name, flags, NULL)
|
|
#define rw_init(rwl, name) _rw_init_flags(rwl, name, 0, NULL)
|
|
#endif /* WITNESS */
|
|
|
|
void rw_enter_read(struct rwlock *);
|
|
void rw_enter_write(struct rwlock *);
|
|
void rw_exit_read(struct rwlock *);
|
|
void rw_exit_write(struct rwlock *);
|
|
|
|
#ifdef DIAGNOSTIC
|
|
void rw_assert_wrlock(struct rwlock *);
|
|
void rw_assert_rdlock(struct rwlock *);
|
|
void rw_assert_anylock(struct rwlock *);
|
|
void rw_assert_unlocked(struct rwlock *);
|
|
#else
|
|
#define rw_assert_wrlock(rwl) ((void)0)
|
|
#define rw_assert_rdlock(rwl) ((void)0)
|
|
#define rw_assert_anylock(rwl) ((void)0)
|
|
#define rw_assert_unlocked(rwl) ((void)0)
|
|
#endif
|
|
|
|
int rw_enter(struct rwlock *, int);
|
|
void rw_exit(struct rwlock *);
|
|
int rw_status(struct rwlock *);
|
|
|
|
static inline int
|
|
rw_read_held(struct rwlock *rwl)
|
|
{
|
|
return (rw_status(rwl) == RW_READ);
|
|
}
|
|
|
|
static inline int
|
|
rw_write_held(struct rwlock *rwl)
|
|
{
|
|
return (rw_status(rwl) == RW_WRITE);
|
|
}
|
|
|
|
static inline int
|
|
rw_lock_held(struct rwlock *rwl)
|
|
{
|
|
int status;
|
|
|
|
status = rw_status(rwl);
|
|
|
|
return (status == RW_READ || status == RW_WRITE);
|
|
}
|
|
|
|
|
|
void _rrw_init_flags(struct rrwlock *, const char *, int,
|
|
const struct lock_type *);
|
|
int rrw_enter(struct rrwlock *, int);
|
|
void rrw_exit(struct rrwlock *);
|
|
int rrw_status(struct rrwlock *);
|
|
|
|
#ifdef WITNESS
|
|
#define rrw_init_flags(rrwl, name, flags) do { \
|
|
static const struct lock_type __lock_type = { .lt_name = #rrwl };\
|
|
_rrw_init_flags(rrwl, name, flags, &__lock_type); \
|
|
} while (0)
|
|
#define rrw_init(rrwl, name) rrw_init_flags(rrwl, name, 0)
|
|
#else /* WITNESS */
|
|
#define rrw_init_flags(rrwl, name, flags) \
|
|
_rrw_init_flags(rrwl, name, 0, NULL)
|
|
#define rrw_init(rrwl, name) _rrw_init_flags(rrwl, name, 0, NULL)
|
|
#endif /* WITNESS */
|
|
|
|
|
|
/*
|
|
* Allocated, reference-counted rwlocks
|
|
*/
|
|
|
|
#ifdef WITNESS
|
|
#define rw_obj_alloc_flags(rwl, name, flags) do { \
|
|
static struct lock_type __lock_type = { .lt_name = #rwl }; \
|
|
_rw_obj_alloc_flags(rwl, name, flags, &__lock_type); \
|
|
} while (0)
|
|
#else
|
|
#define rw_obj_alloc_flags(rwl, name, flags) \
|
|
_rw_obj_alloc_flags(rwl, name, flags, NULL)
|
|
#endif
|
|
#define rw_obj_alloc(rwl, name) rw_obj_alloc_flags(rwl, name, 0)
|
|
|
|
void rw_obj_init(void);
|
|
void _rw_obj_alloc_flags(struct rwlock **, const char *, int,
|
|
struct lock_type *);
|
|
void rw_obj_hold(struct rwlock *);
|
|
int rw_obj_free(struct rwlock *);
|
|
|
|
#endif /* _KERNEL */
|
|
|
|
#endif /* _SYS_RWLOCK_H */
|