#ifndef _SYS_SPINLOCK2_H_
#define _SYS_SPINLOCK2_H_
#ifndef _KERNEL
#error "This file should not be included by userland programs."
#endif
#ifndef _SYS_SYSTM_H_
#include <sys/systm.h>
#endif
#ifndef _SYS_THREAD2_H_
#include <sys/thread2.h>
#endif
#ifndef _SYS_GLOBALDATA_H_
#include <sys/globaldata.h>
#endif
#include <machine/atomic.h>
#include <machine/cpufunc.h>
extern struct spinlock pmap_spin;
int spin_trylock_contested(struct spinlock *spin);
void _spin_lock_contested(struct spinlock *spin, const char *ident, int count);
void _spin_lock_shared_contested(struct spinlock *spin, const char *ident);
#define spin_lock(spin) _spin_lock(spin, __func__)
#define spin_lock_quick(spin) _spin_lock_quick(spin, __func__)
#define spin_lock_shared(spin) _spin_lock_shared(spin, __func__)
#define spin_lock_shared_quick(spin) _spin_lock_shared_quick(spin, __func__)
static __inline boolean_t
spin_trylock(struct spinlock *spin)
{
globaldata_t gd = mycpu;
crit_enter_raw(gd->gd_curthread);
++gd->gd_spinlocks;
cpu_ccfence();
if (atomic_cmpset_int(&spin->lock, 0, 1) == 0)
return (spin_trylock_contested(spin));
#ifdef DEBUG_LOCKS
int i;
for (i = 0; i < SPINLOCK_DEBUG_ARRAY_SIZE; i++) {
if (gd->gd_curthread->td_spinlock_stack_id[i] == 0) {
gd->gd_curthread->td_spinlock_stack_id[i] = 1;
gd->gd_curthread->td_spinlock_stack[i] = spin;
gd->gd_curthread->td_spinlock_caller_pc[i] =
__builtin_return_address(0);
break;
}
}
#endif
return (TRUE);
}
static __inline int
spin_held(struct spinlock *spin)
{
return((spin->lock & ~SPINLOCK_SHARED) != 0);
}
static __always_inline void
_spin_lock_quick(globaldata_t gd, struct spinlock *spin, const char *ident)
{
int count;
crit_enter_raw(gd->gd_curthread);
++gd->gd_spinlocks;
cpu_ccfence();
count = atomic_fetchadd_int(&spin->lock, 1);
if (__predict_false(count != 0)) {
_spin_lock_contested(spin, ident, count);
}
#ifdef DEBUG_LOCKS
int i;
for (i = 0; i < SPINLOCK_DEBUG_ARRAY_SIZE; i++) {
if (gd->gd_curthread->td_spinlock_stack_id[i] == 0) {
gd->gd_curthread->td_spinlock_stack_id[i] = 1;
gd->gd_curthread->td_spinlock_stack[i] = spin;
gd->gd_curthread->td_spinlock_caller_pc[i] =
__builtin_return_address(0);
break;
}
}
#endif
}
static __inline void
_spin_lock(struct spinlock *spin, const char *ident)
{
_spin_lock_quick(mycpu, spin, ident);
}
static __always_inline void
spin_unlock_quick(globaldata_t gd, struct spinlock *spin)
{
#ifdef DEBUG_LOCKS
int i;
for (i = 0; i < SPINLOCK_DEBUG_ARRAY_SIZE; i++) {
if ((gd->gd_curthread->td_spinlock_stack_id[i] == 1) &&
(gd->gd_curthread->td_spinlock_stack[i] == spin)) {
gd->gd_curthread->td_spinlock_stack_id[i] = 0;
gd->gd_curthread->td_spinlock_stack[i] = NULL;
gd->gd_curthread->td_spinlock_caller_pc[i] = NULL;
break;
}
}
#endif
#ifdef DEBUG_LOCKS
KKASSERT(spin->lock != 0);
#endif
cpu_sfence();
atomic_add_int(&spin->lock, -1);
cpu_sfence();
#ifdef DEBUG_LOCKS
KKASSERT(gd->gd_spinlocks > 0);
#endif
cpu_ccfence();
--gd->gd_spinlocks;
crit_exit_quick(gd->gd_curthread);
}
static __inline void
spin_unlock(struct spinlock *spin)
{
spin_unlock_quick(mycpu, spin);
}
static __inline void
spin_unlock_any(struct spinlock *spin)
{
spin_unlock_quick(mycpu, spin);
}
static __always_inline void
_spin_lock_shared_quick(globaldata_t gd, struct spinlock *spin,
const char *ident)
{
int lock;
crit_enter_raw(gd->gd_curthread);
++gd->gd_spinlocks;
cpu_ccfence();
lock = atomic_fetchadd_int(&spin->lock, 1);
if (__predict_false((lock & SPINLOCK_SHARED) == 0)) {
if (lock != 0 ||
!atomic_cmpset_int(&spin->lock, 1, SPINLOCK_SHARED | 1)) {
_spin_lock_shared_contested(spin, ident);
}
}
#ifdef DEBUG_LOCKS
int i;
for (i = 0; i < SPINLOCK_DEBUG_ARRAY_SIZE; i++) {
if (gd->gd_curthread->td_spinlock_stack_id[i] == 0) {
gd->gd_curthread->td_spinlock_stack_id[i] = 1;
gd->gd_curthread->td_spinlock_stack[i] = spin;
gd->gd_curthread->td_spinlock_caller_pc[i] =
__builtin_return_address(0);
break;
}
}
#endif
}
static __always_inline void
spin_unlock_shared_quick(globaldata_t gd, struct spinlock *spin)
{
#ifdef DEBUG_LOCKS
int i;
for (i = 0; i < SPINLOCK_DEBUG_ARRAY_SIZE; i++) {
if ((gd->gd_curthread->td_spinlock_stack_id[i] == 1) &&
(gd->gd_curthread->td_spinlock_stack[i] == spin)) {
gd->gd_curthread->td_spinlock_stack_id[i] = 0;
gd->gd_curthread->td_spinlock_stack[i] = NULL;
gd->gd_curthread->td_spinlock_caller_pc[i] = NULL;
break;
}
}
#endif
#ifdef DEBUG_LOCKS
KKASSERT(spin->lock != 0);
#endif
cpu_sfence();
atomic_add_int(&spin->lock, -1);
#ifdef DEBUG_LOCKS
KKASSERT(gd->gd_spinlocks > 0);
#endif
cpu_ccfence();
--gd->gd_spinlocks;
crit_exit_quick(gd->gd_curthread);
}
static __inline void
_spin_lock_shared(struct spinlock *spin, const char *ident)
{
_spin_lock_shared_quick(mycpu, spin, ident);
}
static __inline void
spin_unlock_shared(struct spinlock *spin)
{
spin_unlock_shared_quick(mycpu, spin);
}
static __inline int
spin_lock_upgrade_try(struct spinlock *spin)
{
if (atomic_cmpset_int(&spin->lock, SPINLOCK_SHARED|1, 1))
return 1;
else
return 0;
}
static __inline void
spin_init(struct spinlock *spin, const char *descr __unused)
{
spin->lock = 0;
spin->update = 0;
#if 0
spin->descr = descr;
#endif
}
static __inline void
spin_uninit(struct spinlock *spin)
{
}
static __inline int
spin_access_start(struct spinlock *spin)
{
int v;
v = *(volatile int *)&spin->update;
cpu_lfence();
if (__predict_false(v & 1))
spin_lock_shared(spin);
return v;
}
static __inline int
spin_access_end(struct spinlock *spin, int v)
{
if (__predict_false(v & 1)) {
spin_unlock_shared(spin);
return 0;
}
cpu_lfence();
return(*(volatile int *)&spin->update != v);
}
static __inline void
spin_lock_update(struct spinlock *spin)
{
spin_lock(spin);
atomic_add_int_nonlocked(&spin->update, 1);
cpu_sfence();
KKASSERT_UNSPIN((spin->update & 1), spin);
}
static __inline void
spin_unlock_update(struct spinlock *spin)
{
cpu_sfence();
atomic_add_int_nonlocked(&spin->update, 1);
KKASSERT_UNSPIN(((spin->update & 1) == 0), spin);
spin_unlock(spin);
}
static __inline int
spin_access_start_only(struct spinlock *spin)
{
int v;
v = *(volatile int *)&spin->update;
cpu_lfence();
return v;
}
static __inline int
spin_access_check_inprog(int v)
{
return (v & 1);
}
static __inline int
spin_access_end_only(struct spinlock *spin, int v)
{
cpu_lfence();
return(*(volatile int *)&spin->update != v);
}
static __inline void
spin_lock_update_only(struct spinlock *spin)
{
atomic_add_int_nonlocked(&spin->update, 1);
cpu_sfence();
KKASSERT(spin->update & 1);
}
static __inline void
spin_unlock_update_only(struct spinlock *spin)
{
cpu_sfence();
atomic_add_int_nonlocked(&spin->update, 1);
KKASSERT((spin->update & 1) == 0);
}
#endif