#include "assym.h"
#include <sys/t_lock.h>
#include <sys/mutex.h>
#include <sys/mutex_impl.h>
#include <sys/rwlock_impl.h>
#include <sys/asm_linkage.h>
#include <sys/machlock.h>
#include <sys/machthread.h>
#include <sys/lockstat.h>
#ifdef DEBUG
#include <sys/machparam.h>
#endif
ENTRY(ldstub)
retl
ldstub [%o0], %o0
SET_SIZE(ldstub)
#ifdef SF_ERRATA_51
.align 32
ENTRY(membar_return)
retl
nop
SET_SIZE(membar_return)
#define MEMBAR_RETURN ba,pt %icc, membar_return
#else
#define MEMBAR_RETURN retl
#endif
ENTRY(membar_enter)
MEMBAR_RETURN
membar #StoreLoad|#StoreStore
SET_SIZE(membar_enter)
ENTRY(membar_exit)
MEMBAR_RETURN
membar #LoadStore|#StoreStore
SET_SIZE(membar_exit)
ENTRY(membar_producer)
MEMBAR_RETURN
membar #StoreStore
SET_SIZE(membar_producer)
ENTRY(membar_consumer)
MEMBAR_RETURN
membar #LoadLoad
SET_SIZE(membar_consumer)
.align 32
ENTRY(lock_try)
ldstub [%o0], %o1 ! try to set lock, get value in %o1
brnz,pn %o1, 1f
membar #LoadLoad
.lock_try_lockstat_patch_point:
retl
or %o0, 1, %o0 ! ensure lo32 != 0
1:
retl
clr %o0
SET_SIZE(lock_try)
.align 32
ENTRY(lock_spin_try)
ldstub [%o0], %o1 ! try to set lock, get value in %o1
brnz,pn %o1, 1f
membar #LoadLoad
retl
or %o0, 1, %o0 ! ensure lo32 != 0
1:
retl
clr %o0
SET_SIZE(lock_spin_try)
.align 32
ENTRY(lock_set)
ldstub [%o0], %o1
brnz,pn %o1, 1f ! go to C for the hard case
membar #LoadLoad
.lock_set_lockstat_patch_point:
retl
nop
1:
sethi %hi(lock_set_spin), %o2 ! load up for jump to C
jmp %o2 + %lo(lock_set_spin)
nop ! delay: do nothing
SET_SIZE(lock_set)
ENTRY(lock_clear)
membar #LoadStore|#StoreStore
.lock_clear_lockstat_patch_point:
retl
clrb [%o0]
SET_SIZE(lock_clear)
.align 32
ENTRY(ulock_try)
ldstuba [%o0]ASI_USER, %o1 ! try to set lock, get value in %o1
xor %o1, 0xff, %o0 ! delay - return non-zero if success
retl
membar #LoadLoad
SET_SIZE(ulock_try)
ENTRY(ulock_clear)
membar #LoadStore|#StoreStore
retl
stba %g0, [%o0]ASI_USER ! clear lock
SET_SIZE(ulock_clear)
ENTRY(lock_set_spl)
rdpr %pil, %o3 ! %o3 = current pil
cmp %o3, %o1 ! is current pil high enough?
bl,a,pt %icc, 1f ! if not, write %pil in delay
wrpr %g0, %o1, %pil
1:
ldstub [%o0], %o4 ! try the lock
brnz,pn %o4, 2f ! go to C for the miss case
membar #LoadLoad
.lock_set_spl_lockstat_patch_point:
retl
sth %o3, [%o2] ! delay - save original pil
2:
sethi %hi(lock_set_spl_spin), %o5 ! load up jmp to C
jmp %o5 + %lo(lock_set_spl_spin) ! jmp to lock_set_spl_spin
nop ! delay: do nothing
SET_SIZE(lock_set_spl)
ENTRY(lock_clear_splx)
ldn [THREAD_REG + T_CPU], %o2 ! get CPU pointer
membar #LoadStore|#StoreStore
ld [%o2 + CPU_BASE_SPL], %o2
clrb [%o0] ! clear lock
cmp %o2, %o1 ! compare new to base
movl %xcc, %o1, %o2 ! use new pri if base is less
.lock_clear_splx_lockstat_patch_point:
retl
wrpr %g0, %o2, %pil
SET_SIZE(lock_clear_splx)
.align 32
ENTRY(mutex_enter)
mov THREAD_REG, %o1
casx [%o0], %g0, %o1 ! try to acquire as adaptive
brnz,pn %o1, 1f ! locked or wrong type
membar #LoadLoad
.mutex_enter_lockstat_patch_point:
retl
nop
1:
sethi %hi(mutex_vector_enter), %o2 ! load up for jump to C
jmp %o2 + %lo(mutex_vector_enter)
nop
SET_SIZE(mutex_enter)
ENTRY(mutex_tryenter)
mov THREAD_REG, %o1
casx [%o0], %g0, %o1 ! try to acquire as adaptive
brnz,pn %o1, 1f ! locked or wrong type continue
membar #LoadLoad
.mutex_tryenter_lockstat_patch_point:
retl
or %o0, 1, %o0 ! ensure lo32 != 0
1:
sethi %hi(mutex_vector_tryenter), %o2 ! hi bits
jmp %o2 + %lo(mutex_vector_tryenter) ! go to C
nop
SET_SIZE(mutex_tryenter)
ENTRY(mutex_adaptive_tryenter)
mov THREAD_REG, %o1
casx [%o0], %g0, %o1 ! try to acquire as adaptive
brnz,pn %o1, 0f ! locked or wrong type
membar #LoadLoad
retl
or %o0, 1, %o0 ! ensure lo32 != 0
0:
retl
mov %g0, %o0
SET_SIZE(mutex_adaptive_tryenter)
! these need to be together and cache aligned for performance.
.align 64
.global mutex_exit_critical_size
.global mutex_exit_critical_start
.global mutex_owner_running_critical_size
.global mutex_owner_running_critical_start
mutex_exit_critical_size = .mutex_exit_critical_end - mutex_exit_critical_start
.align 32
ENTRY(mutex_exit)
mutex_exit_critical_start: ! If we are interrupted, restart here
ldn [%o0], %o1 ! get the owner field
membar #LoadStore|#StoreStore
cmp THREAD_REG, %o1 ! do we own lock with no waiters?
be,a,pt %ncc, 1f ! if so, drive on ...
stn %g0, [%o0] ! delay: clear lock if we owned it
.mutex_exit_critical_end: ! for pil_interrupt() hook
ba,a,pt %xcc, mutex_vector_exit ! go to C for the hard cases
1:
.mutex_exit_lockstat_patch_point:
retl
nop
SET_SIZE(mutex_exit)
mutex_owner_running_critical_size = .mutex_owner_running_critical_end - mutex_owner_running_critical_start
.align 32
ENTRY(mutex_owner_running)
mutex_owner_running_critical_start: ! If interrupted restart here
ldn [%o0], %o1 ! get the owner field
and %o1, MUTEX_THREAD, %o1 ! remove the waiters bit if any
brz,pn %o1, 1f ! if so, drive on ...
nop
ldn [%o1+T_CPU], %o2 ! get owner->t_cpu
ldn [%o2+CPU_THREAD], %o3 ! get owner->t_cpu->cpu_thread
.mutex_owner_running_critical_end: ! for pil_interrupt() hook
cmp %o1, %o3 ! owner == running thread?
be,a,pt %xcc, 2f ! yes, go return cpu
nop
1:
retl
mov %g0, %o0 ! return 0 (owner not running)
2:
retl
mov %o2, %o0 ! owner running, return cpu
SET_SIZE(mutex_owner_running)
.align 16
ENTRY(rw_enter)
cmp %o1, RW_WRITER ! entering as writer?
be,a,pn %icc, 2f ! if so, go do it ...
or THREAD_REG, RW_WRITE_LOCKED, %o5 ! delay: %o5 = owner
ldn [%o0], %o4 ! %o4 = old lock value
1:
andcc %o4, RW_WRITE_CLAIMED, %g0 ! write-locked or write-wanted?
bz,pt %xcc, 3f ! if so, prepare to block
add %o4, RW_READ_LOCK, %o5 ! delay: increment hold count
sethi %hi(rw_enter_sleep), %o2 ! load up jump
jmp %o2 + %lo(rw_enter_sleep) ! jmp to rw_enter_sleep
nop ! delay: do nothing
3:
casx [%o0], %o4, %o5 ! try to grab read lock
cmp %o4, %o5 ! did we get it?
#ifdef sun4v
be,a,pt %xcc, 0f
membar #LoadLoad
sethi %hi(rw_enter_sleep), %o2 ! load up jump
jmp %o2 + %lo(rw_enter_sleep) ! jmp to rw_enter_sleep
nop ! delay: do nothing
0:
#else
bne,pn %xcc, 1b ! if not, try again
mov %o5, %o4 ! delay: %o4 = old lock value
membar #LoadLoad
#endif
.rw_read_enter_lockstat_patch_point:
retl
nop
2:
casx [%o0], %g0, %o5 ! try to grab write lock
brz,pt %o5, 4f ! branch around if we got it
membar #LoadLoad ! done regardless of where we go
sethi %hi(rw_enter_sleep), %o2
jmp %o2 + %lo(rw_enter_sleep) ! jump to rw_enter_sleep if not
nop ! delay: do nothing
4:
.rw_write_enter_lockstat_patch_point:
retl
nop
SET_SIZE(rw_enter)
.align 16
ENTRY(rw_exit)
ldn [%o0], %o4 ! %o4 = old lock value
membar #LoadStore|#StoreStore ! membar_exit()
subcc %o4, RW_READ_LOCK, %o5 ! %o5 = new lock value if reader
bnz,pn %xcc, 2f ! single reader, no waiters?
clr %o1
1:
srl %o4, RW_HOLD_COUNT_SHIFT, %o3 ! %o3 = hold count (lockstat)
casx [%o0], %o4, %o5 ! try to drop lock
cmp %o4, %o5 ! did we succeed?
bne,pn %xcc, rw_exit_wakeup ! if not, go to C
nop ! delay: do nothing
.rw_read_exit_lockstat_patch_point:
retl
nop ! delay: do nothing
2:
andcc %o4, RW_WRITE_LOCKED, %g0 ! are we a writer?
bnz,a,pt %xcc, 3f
or THREAD_REG, RW_WRITE_LOCKED, %o4 ! delay: %o4 = owner
cmp %o5, RW_READ_LOCK ! would lock still be held?
bge,pt %xcc, 1b ! if so, go ahead and drop it
nop
ba,pt %xcc, rw_exit_wakeup ! otherwise, wake waiters
nop
3:
casx [%o0], %o4, %o1 ! try to drop write lock
cmp %o4, %o1 ! did we succeed?
bne,pn %xcc, rw_exit_wakeup ! if not, go to C
nop
.rw_write_exit_lockstat_patch_point:
retl
nop
SET_SIZE(rw_exit)
#define RETL 0x81c3e008
#define NOP 0x01000000
#define BA 0x10800000
#define DISP22 ((1 << 22) - 1)
#define ANNUL 0x20000000
#define HOT_PATCH_COMMON(addr, event, normal_instr, annul, rs) \
ba 1f; \
rd %pc, %o0; \
save %sp, -SA(MINFRAME), %sp; \
set lockstat_probemap, %l1; \
ld [%l1 + (event * DTRACE_IDSIZE)], %o0; \
brz,pn %o0, 0f; \
ldub [THREAD_REG + T_LOCKSTAT], %l0; \
add %l0, 1, %l2; \
stub %l2, [THREAD_REG + T_LOCKSTAT]; \
set lockstat_probe, %g1; \
ld [%l1 + (event * DTRACE_IDSIZE)], %o0; \
brz,a,pn %o0, 0f; \
stub %l0, [THREAD_REG + T_LOCKSTAT]; \
ldn [%g1], %g2; \
mov rs, %o2; \
jmpl %g2, %o7; \
mov %i0, %o1; \
stub %l0, [THREAD_REG + T_LOCKSTAT]; \
0: ret; \
restore %g0, 1, %o0; \
1: set addr, %o1; \
sub %o0, %o1, %o0; \
srl %o0, 2, %o0; \
inc %o0; \
set DISP22, %o1; \
and %o1, %o0, %o0; \
set BA, %o1; \
or %o1, %o0, %o0; \
sethi %hi(annul), %o2; \
add %o0, %o2, %o2; \
set addr, %o0; \
set normal_instr, %o1; \
ld [%i0 + (event * DTRACE_IDSIZE)], %o3; \
tst %o3; \
movnz %icc, %o2, %o1; \
call hot_patch_kernel_text; \
mov 4, %o2; \
membar #Sync
#define HOT_PATCH(addr, event, normal_instr) \
HOT_PATCH_COMMON(addr, event, normal_instr, 0, %i1)
#define HOT_PATCH_ARG(addr, event, normal_instr, arg) \
HOT_PATCH_COMMON(addr, event, normal_instr, 0, arg)
#define HOT_PATCH_ANNULLED(addr, event, normal_instr) \
HOT_PATCH_COMMON(addr, event, normal_instr, ANNUL, %i1)
ENTRY(lockstat_hot_patch)
save %sp, -SA(MINFRAME), %sp
set lockstat_probemap, %i0
HOT_PATCH(.mutex_enter_lockstat_patch_point,
LS_MUTEX_ENTER_ACQUIRE, RETL)
HOT_PATCH_ANNULLED(.mutex_tryenter_lockstat_patch_point,
LS_MUTEX_TRYENTER_ACQUIRE, RETL)
HOT_PATCH(.mutex_exit_lockstat_patch_point,
LS_MUTEX_EXIT_RELEASE, RETL)
HOT_PATCH(.rw_write_enter_lockstat_patch_point,
LS_RW_ENTER_ACQUIRE, RETL)
HOT_PATCH(.rw_read_enter_lockstat_patch_point,
LS_RW_ENTER_ACQUIRE, RETL)
HOT_PATCH_ARG(.rw_write_exit_lockstat_patch_point,
LS_RW_EXIT_RELEASE, RETL, RW_WRITER)
HOT_PATCH_ARG(.rw_read_exit_lockstat_patch_point,
LS_RW_EXIT_RELEASE, RETL, RW_READER)
HOT_PATCH(.lock_set_lockstat_patch_point,
LS_LOCK_SET_ACQUIRE, RETL)
HOT_PATCH_ANNULLED(.lock_try_lockstat_patch_point,
LS_LOCK_TRY_ACQUIRE, RETL)
HOT_PATCH(.lock_clear_lockstat_patch_point,
LS_LOCK_CLEAR_RELEASE, RETL)
HOT_PATCH(.lock_set_spl_lockstat_patch_point,
LS_LOCK_SET_SPL_ACQUIRE, RETL)
HOT_PATCH(.lock_clear_splx_lockstat_patch_point,
LS_LOCK_CLEAR_SPLX_RELEASE, RETL)
ret
restore
SET_SIZE(lockstat_hot_patch)
.align 16
ENTRY_NP(asm_mutex_spin_enter)
ldstub [%l6 + M_SPINLOCK], %l5 ! try to set lock, get value in %l5
1:
tst %l5
bnz 3f ! lock already held - go spin
nop
2:
jmp %l7 + 8 ! return
membar #LoadLoad
!
! Spin on lock without using an atomic operation to prevent the caches
! from unnecessarily moving ownership of the line around.
!
3:
ldub [%l6 + M_SPINLOCK], %l5
4:
tst %l5
bz,a 1b ! lock appears to be free, try again
ldstub [%l6 + M_SPINLOCK], %l5 ! delay slot - try to set lock
sethi %hi(panicstr) , %l5
ldn [%l5 + %lo(panicstr)], %l5
tst %l5
bnz 2b ! after panic, feign success
nop
b 4b
ldub [%l6 + M_SPINLOCK], %l5 ! delay - reload lock
SET_SIZE(asm_mutex_spin_enter)
ENTRY_NP(asm_mutex_spin_exit)
membar #LoadStore|#StoreStore
jmp %l7 + 8 ! return
clrb [%l6 + M_SPINLOCK] ! delay - clear lock
SET_SIZE(asm_mutex_spin_exit)
ENTRY(thread_onproc)
set TS_ONPROC, %o2 ! TS_ONPROC state
st %o2, [%o0 + T_STATE] ! store state
add %o1, CPU_THREAD_LOCK, %o3 ! pointer to disp_lock while running
retl ! return
stn %o3, [%o0 + T_LOCKP] ! delay - store new lock pointer
SET_SIZE(thread_onproc)
ENTRY(cas_delay)
casx [%o0], %g0, %g0
casx [%o0], %g0, %g0
retl
casx [%o0], %g0, %g0
SET_SIZE(cas_delay)
ENTRY(rdccr_delay)
rd %ccr, %g0
rd %ccr, %g0
retl
rd %ccr, %g0
SET_SIZE(rdccr_delay)
ENTRY(mutex_delay_default)
mov 72,%o0
1: brgz %o0, 1b
dec %o0
retl
nop
SET_SIZE(mutex_delay_default)