mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1106 lines
27 KiB
1106 lines
27 KiB
TITLE "Spin Locks"
|
|
;++
|
|
;
|
|
; Copyright (c) 1989 Microsoft Corporation
|
|
;
|
|
; Module Name:
|
|
;
|
|
; spinlock.asm
|
|
;
|
|
; Abstract:
|
|
;
|
|
; This module implements the routines for acquiring and releasing
|
|
; spin locks.
|
|
;
|
|
; Author:
|
|
;
|
|
; Bryan Willman (bryanwi) 13 Dec 89
|
|
;
|
|
; Environment:
|
|
;
|
|
; Kernel mode only.
|
|
;
|
|
; Revision History:
|
|
;
|
|
; Ken Reneris (kenr) 22-Jan-1991
|
|
; Removed KeAcquireSpinLock macros, and made functions
|
|
;--
|
|
|
|
PAGE
|
|
|
|
.586p
|
|
|
|
include ks386.inc
|
|
include callconv.inc ; calling convention macros
|
|
include i386\kimacro.inc
|
|
include mac386.inc
|
|
|
|
EXTRNP KfRaiseIrql,1,IMPORT,FASTCALL
|
|
EXTRNP KfLowerIrql,1,IMPORT,FASTCALL
|
|
EXTRNP _KeGetCurrentIrql,0,IMPORT
|
|
EXTRNP _KeBugCheckEx,5
|
|
|
|
|
|
_TEXT$00 SEGMENT PARA PUBLIC 'CODE'
|
|
ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
|
|
|
|
PAGE
|
|
SUBTTL "Acquire Kernel Spin Lock"
|
|
;++
|
|
;
|
|
; VOID
|
|
; KeInializeSpinLock (
|
|
; IN PKSPIN_LOCK SpinLock,
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; This function initializes a SpinLock
|
|
;
|
|
; Arguments:
|
|
;
|
|
; SpinLock (TOS+4) - Supplies a pointer to an kernel spin lock.
|
|
;
|
|
; Return Value:
|
|
;
|
|
; None.
|
|
;
|
|
;--
|
|
cPublicProc _KeInitializeSpinLock ,1
|
|
cPublicFpo 1,0
|
|
mov eax, dword ptr [esp+4]
|
|
mov dword ptr [eax], 0
|
|
stdRET _KeInitializeSpinLock
|
|
stdENDP _KeInitializeSpinLock
|
|
|
|
|
|
|
|
PAGE
|
|
SUBTTL "Ke Acquire Spin Lock At DPC Level"
|
|
|
|
;++
|
|
;
|
|
; VOID
|
|
; KefAcquireSpinLockAtDpcLevel (
|
|
; IN PKSPIN_LOCK SpinLock
|
|
; )
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; This function acquires a kernel spin lock.
|
|
;
|
|
; N.B. This function assumes that the current IRQL is set properly.
|
|
; It neither raises nor lowers IRQL.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; (ecx) SpinLock - Supplies a pointer to an kernel spin lock.
|
|
;
|
|
; Return Value:
|
|
;
|
|
; None.
|
|
;
|
|
;--
|
|
|
|
align 16
|
|
cPublicFastCall KefAcquireSpinLockAtDpcLevel, 1
|
|
cPublicFpo 0, 0
|
|
if DBG
|
|
push ecx
|
|
stdCall _KeGetCurrentIrql
|
|
pop ecx
|
|
|
|
cmp al, DISPATCH_LEVEL
|
|
jl short asld50
|
|
endif
|
|
|
|
ifdef NT_UP
|
|
fstRET KefAcquireSpinLockAtDpcLevel
|
|
else
|
|
;
|
|
; Attempt to assert the lock
|
|
;
|
|
|
|
asld10: ACQUIRE_SPINLOCK ecx,<short asld20>
|
|
fstRET KefAcquireSpinLockAtDpcLevel
|
|
|
|
;
|
|
; Lock is owned, spin till it looks free, then go get it again.
|
|
;
|
|
|
|
align 4
|
|
asld20: SPIN_ON_SPINLOCK ecx,<short asld10>
|
|
|
|
endif
|
|
|
|
if DBG
|
|
asld50: stdCall _KeBugCheckEx,<IRQL_NOT_GREATER_OR_EQUAL,ecx,eax,0,0>
|
|
int 3 ; help debugger backtrace.
|
|
endif
|
|
|
|
fstENDP KefAcquireSpinLockAtDpcLevel
|
|
|
|
|
|
;++
|
|
;
|
|
; VOID
|
|
; KeAcquireSpinLockAtDpcLevel (
|
|
; IN PKSPIN_LOCK SpinLock
|
|
; )
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; Thunk for standard call callers
|
|
;
|
|
;--
|
|
|
|
cPublicProc _KeAcquireSpinLockAtDpcLevel, 1
|
|
cPublicFpo 1,0
|
|
|
|
ifndef NT_UP
|
|
mov ecx,[esp+4] ; SpinLock
|
|
|
|
aslc10: ACQUIRE_SPINLOCK ecx,<short aslc20>
|
|
stdRET _KeAcquireSpinLockAtDpcLevel
|
|
|
|
aslc20: SPIN_ON_SPINLOCK ecx,<short aslc10>
|
|
endif
|
|
stdRET _KeAcquireSpinLockAtDpcLevel
|
|
stdENDP _KeAcquireSpinLockAtDpcLevel
|
|
|
|
|
|
PAGE
|
|
SUBTTL "Ke Release Spin Lock From Dpc Level"
|
|
;++
|
|
;
|
|
; VOID
|
|
; KefReleaseSpinLockFromDpcLevel (
|
|
; IN PKSPIN_LOCK SpinLock
|
|
; )
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; This function releases a kernel spin lock.
|
|
;
|
|
; N.B. This function assumes that the current IRQL is set properly.
|
|
; It neither raises nor lowers IRQL.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; (ecx) SpinLock - Supplies a pointer to an executive spin lock.
|
|
;
|
|
; Return Value:
|
|
;
|
|
; None.
|
|
;
|
|
;--
|
|
align 16
|
|
cPublicFastCall KefReleaseSpinLockFromDpcLevel ,1
|
|
cPublicFpo 0,0
|
|
ifndef NT_UP
|
|
RELEASE_SPINLOCK ecx
|
|
endif
|
|
fstRET KefReleaseSpinLockFromDpcLevel
|
|
|
|
fstENDP KefReleaseSpinLockFromDpcLevel
|
|
|
|
;++
|
|
;
|
|
; VOID
|
|
; KeReleaseSpinLockFromDpcLevel (
|
|
; IN PKSPIN_LOCK SpinLock
|
|
; )
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; Thunk for standard call callers
|
|
;
|
|
;--
|
|
|
|
cPublicProc _KeReleaseSpinLockFromDpcLevel, 1
|
|
cPublicFpo 1,0
|
|
ifndef NT_UP
|
|
mov ecx, [esp+4] ; (ecx) = SpinLock
|
|
RELEASE_SPINLOCK ecx
|
|
endif
|
|
stdRET _KeReleaseSpinLockFromDpcLevel
|
|
stdENDP _KeReleaseSpinLockFromDpcLevel
|
|
|
|
|
|
|
|
PAGE
|
|
SUBTTL "Ki Acquire Kernel Spin Lock"
|
|
|
|
;++
|
|
;
|
|
; VOID
|
|
; FASTCALL
|
|
; KiAcquireSpinLock (
|
|
; IN PKSPIN_LOCK SpinLock
|
|
; )
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; This function acquires a kernel spin lock.
|
|
;
|
|
; N.B. This function assumes that the current IRQL is set properly.
|
|
; It neither raises nor lowers IRQL.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; (ecx) SpinLock - Supplies a pointer to an kernel spin lock.
|
|
;
|
|
; Return Value:
|
|
;
|
|
; None.
|
|
;
|
|
;--
|
|
|
|
align 16
|
|
cPublicFastCall KiAcquireSpinLock ,1
|
|
cPublicFpo 0,0
|
|
ifndef NT_UP
|
|
|
|
;
|
|
; Attempt to assert the lock
|
|
;
|
|
|
|
asl10: ACQUIRE_SPINLOCK ecx,<short asl20>
|
|
fstRET KiAcquireSpinLock
|
|
|
|
;
|
|
; Lock is owned, spin till it looks free, then go get it again.
|
|
;
|
|
|
|
align 4
|
|
asl20: SPIN_ON_SPINLOCK ecx,<short asl10>
|
|
|
|
else
|
|
fstRET KiAcquireSpinLock
|
|
endif
|
|
|
|
fstENDP KiAcquireSpinLock
|
|
|
|
PAGE
|
|
SUBTTL "Ki Release Kernel Spin Lock"
|
|
;++
|
|
;
|
|
; VOID
|
|
; FASTCALL
|
|
; KiReleaseSpinLock (
|
|
; IN PKSPIN_LOCK SpinLock
|
|
; )
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; This function releases a kernel spin lock.
|
|
;
|
|
; N.B. This function assumes that the current IRQL is set properly.
|
|
; It neither raises nor lowers IRQL.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; (ecx) SpinLock - Supplies a pointer to an executive spin lock.
|
|
;
|
|
; Return Value:
|
|
;
|
|
; None.
|
|
;
|
|
;--
|
|
align 16
|
|
cPublicFastCall KiReleaseSpinLock ,1
|
|
cPublicFpo 0,0
|
|
ifndef NT_UP
|
|
|
|
RELEASE_SPINLOCK ecx
|
|
|
|
endif
|
|
fstRET KiReleaseSpinLock
|
|
|
|
fstENDP KiReleaseSpinLock
|
|
|
|
PAGE
|
|
SUBTTL "Try to acquire Kernel Spin Lock"
|
|
|
|
;++
|
|
;
|
|
; BOOLEAN
|
|
; KeTryToAcquireSpinLock (
|
|
; IN PKSPIN_LOCK SpinLock,
|
|
; OUT PKIRQL OldIrql
|
|
; )
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; This function attempts acquires a kernel spin lock. If the
|
|
; spinlock is busy, it is not acquire and FALSE is returned.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; SpinLock (TOS+4) - Supplies a pointer to an kernel spin lock.
|
|
; OldIrql (TOS+8) = Location to store old irql
|
|
;
|
|
; Return Value:
|
|
; TRUE - Spinlock was acquired & irql was raise
|
|
; FALSE - SpinLock was not acquired - irql is unchanged.
|
|
;
|
|
;--
|
|
|
|
align dword
|
|
cPublicProc _KeTryToAcquireSpinLock ,2
|
|
cPublicFpo 2,0
|
|
|
|
ifdef NT_UP
|
|
; UP Version of KeTryToAcquireSpinLock
|
|
|
|
mov ecx, DISPATCH_LEVEL
|
|
fstCall KfRaiseIrql
|
|
|
|
mov ecx, [esp+8] ; (ecx) -> ptr to OldIrql
|
|
mov [ecx], al ; save OldIrql
|
|
|
|
mov eax, 1 ; Return TRUE
|
|
stdRET _KeTryToAcquireSpinLock
|
|
|
|
else
|
|
; MP Version of KeTryToAcquireSpinLock
|
|
|
|
mov edx,[esp+4] ; (edx) -> spinlock
|
|
|
|
;
|
|
; First check the spinlock without asserting a lock
|
|
;
|
|
|
|
TEST_SPINLOCK edx,<short ttsl10>
|
|
|
|
;
|
|
; Spinlock looks free raise irql & try to acquire it
|
|
;
|
|
|
|
;
|
|
; raise to dispatch_level
|
|
;
|
|
|
|
mov ecx, DISPATCH_LEVEL
|
|
fstCall KfRaiseIrql
|
|
|
|
mov edx, [esp+4] ; (edx) -> spinlock
|
|
mov ecx, [esp+8] ; (ecx) = Return OldIrql
|
|
|
|
ACQUIRE_SPINLOCK edx,<short ttsl20>
|
|
|
|
mov [ecx], al ; save OldIrql
|
|
mov eax, 1 ; spinlock was acquired, return TRUE
|
|
|
|
stdRET _KeTryToAcquireSpinLock
|
|
|
|
ttsl10: xor eax, eax ; return FALSE
|
|
YIELD
|
|
stdRET _KeTryToAcquireSpinLock
|
|
|
|
ttsl20:
|
|
YIELD
|
|
mov cl, al ; (cl) = OldIrql
|
|
fstCall KfLowerIrql ; spinlock was busy, restore irql
|
|
xor eax, eax ; return FALSE
|
|
stdRET _KeTryToAcquireSpinLock
|
|
endif
|
|
|
|
stdENDP _KeTryToAcquireSpinLock
|
|
|
|
PAGE
|
|
SUBTTL "Ki Try to acquire Kernel Spin Lock"
|
|
;++
|
|
;
|
|
; BOOLEAN
|
|
; FASTCALL
|
|
; KeTryToAcquireSpinLockAtDpcLevel (
|
|
; IN PKSPIN_LOCK SpinLock
|
|
; )
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; This function attempts acquires a kernel spin lock. If the
|
|
; spinlock is busy, it is not acquire and FALSE is returned.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; SpinLock (ecx) - Supplies a pointer to an kernel spin lock.
|
|
;
|
|
; Return Value:
|
|
;
|
|
; TRUE - Spinlock was acquired
|
|
; FALSE - SpinLock was not acquired
|
|
;
|
|
;--
|
|
|
|
align dword
|
|
cPublicFastCall KeTryToAcquireSpinLockAtDpcLevel ,1
|
|
cPublicFpo 0, 0
|
|
|
|
;
|
|
; First check the spinlock without asserting a lock
|
|
;
|
|
|
|
ifndef NT_UP
|
|
|
|
TEST_SPINLOCK ecx, <short atsl20>
|
|
|
|
;
|
|
; Spinlock looks free try to acquire it.
|
|
;
|
|
|
|
ACQUIRE_SPINLOCK ecx, <short atsl20>
|
|
|
|
endif
|
|
|
|
mov eax, 1 ; spinlock was acquired, return TRUE
|
|
|
|
fstRET KeTryToAcquireSpinLockAtDpcLevel
|
|
|
|
ifndef NT_UP
|
|
|
|
atsl20: YIELD ;
|
|
xor eax, eax ; return FALSE
|
|
|
|
fstRET KeTryToAcquireSpinLockAtDpclevel
|
|
|
|
endif
|
|
|
|
fstENDP KeTryToAcquireSpinLockAtDpcLevel
|
|
|
|
;++
|
|
;
|
|
; BOOLEAN
|
|
; KeTestSpinLock (
|
|
; IN PKSPIN_LOCK SpinLock
|
|
; )
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; This function tests a kernel spin lock. If the spinlock is
|
|
; busy, FALSE is returned. If not, TRUE is returned. The spinlock
|
|
; is never acquired. This is provided to allow code to spin at low
|
|
; IRQL, only raising the IRQL when there is a reasonable hope of
|
|
; acquiring the lock.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; SpinLock (ecx) - Supplies a pointer to a kernel spin lock.
|
|
;
|
|
; Return Value:
|
|
; TRUE - Spinlock appears available
|
|
; FALSE - SpinLock is busy
|
|
;
|
|
;--
|
|
|
|
cPublicFastCall KeTestSpinLock ,1
|
|
TEST_SPINLOCK ecx,<short tso10>
|
|
mov eax, 1
|
|
fstRET KeTestSpinLock
|
|
|
|
tso10: YIELD
|
|
xor eax, eax
|
|
fstRET KeTestSpinLock
|
|
|
|
fstENDP KeTestSpinLock
|
|
|
|
page ,132
|
|
subttl "Acquire In Stack Queued SpinLock At Dpc Level"
|
|
|
|
ifdef QLOCK_STAT_GATHER
|
|
|
|
EXTRNP KiQueueStatTrySucceeded,2,,FASTCALL
|
|
EXTRNP KiQueueStatTryFailed,1,,FASTCALL
|
|
EXTRNP KiQueueStatTry,1,,FASTCALL
|
|
EXTRNP KiQueueStatAcquireQueuedLock,1,,FASTCALL
|
|
EXTRNP KiQueueStatAcquireQueuedLockRTS,1,,FASTCALL
|
|
EXTRNP KiQueueStatTryAcquire,3,,FASTCALL
|
|
EXTRNP KiQueueStatReleaseQueuedLock,2,,FASTCALL
|
|
EXTRNP KiQueueStatAcquire,1,,FASTCALL
|
|
EXTRNP KiQueueStatRelease,1,,FASTCALL
|
|
EXTRNP KiAcquireQueuedLock,1,,FASTCALL
|
|
EXTRNP KiReleaseQueuedLock,1,,FASTCALL
|
|
|
|
;
|
|
; The following routines are used to wrap the actual calls to the
|
|
; real routines which have been usurped here by patching the import
|
|
; table.
|
|
;
|
|
|
|
cPublicFastCall __cap_KeAcquireQueuedSpinLock,1
|
|
sub esp, 8 ; make room to save time
|
|
push ecx ; save args
|
|
rdtsc ; get time
|
|
mov [esp].4, eax ; save low part
|
|
mov [esp].8, edx ; save high part
|
|
mov ecx, [esp] ; restore arg
|
|
fstCall KiQueueStatAcquireQueuedLock
|
|
acqst: mov ecx, esp ; set arg pointer for data accum
|
|
push eax ; save result
|
|
fstCall KiQueueStatAcquire
|
|
pop eax ; restore result
|
|
add esp, 12 ; restore stack pointer
|
|
fstRET __cap_KeAcquireQueuedSpinLock
|
|
fstENDP __cap_KeAcquireQueuedSpinLock
|
|
|
|
cPublicFastCall __cap_KeAcquireQueuedSpinLockRaiseToSynch,1
|
|
sub esp, 8 ; make room to save time
|
|
push ecx ; save args
|
|
rdtsc ; get time
|
|
mov [esp].4, eax ; save low part
|
|
mov [esp].8, edx ; save high part
|
|
mov ecx, [esp] ; restore arg
|
|
fstCall KiQueueStatAcquireQueuedLockRTS
|
|
jmp short acqst ; use common code to finish
|
|
fstENDP __cap_KeAcquireQueuedSpinLockRaiseToSynch
|
|
|
|
cPublicFastCall __cap_KeTryToAcquireQueuedSpinLockRaiseToSynch,2
|
|
push ecx ; save arg
|
|
push SYNCH_LEVEL
|
|
tryst: fstCall KiQueueStatTryAcquire
|
|
push eax ; save result
|
|
mov ecx, esp
|
|
fstCall KiQueueStatTry
|
|
pop eax ; restore result
|
|
add esp, 4 ; drop saved arg
|
|
or eax, eax ; some assembly callers expect appropriate flags
|
|
fstRET __cap_KeTryToAcquireQueuedSpinLockRaiseToSynch
|
|
fstENDP __cap_KeTryToAcquireQueuedSpinLockRaiseToSynch
|
|
|
|
cPublicFastCall __cap_KeTryToAcquireQueuedSpinLock,2
|
|
push ecx ; save arg
|
|
push DISPATCH_LEVEL
|
|
jmp short tryst ; use common code to finish
|
|
fstENDP __cap_KeTryToAcquireQueuedSpinLock
|
|
|
|
cPublicFastCall __cap_KeReleaseQueuedSpinLock,2
|
|
push ecx ; save args
|
|
mov ecx, esp ; set arg for stat release routine
|
|
push edx ; save other arg
|
|
fstCall KiQueueStatRelease
|
|
pop edx
|
|
pop ecx
|
|
fstCall KiQueueStatReleaseQueuedLock
|
|
fstRET __cap_KeReleaseQueuedSpinLock
|
|
fstENDP __cap_KeReleaseQueuedSpinLock
|
|
|
|
;
|
|
; KeAcquireQueuedSpinLockAtDpcLevel
|
|
; KeReleaseQueuedSpinLockFromDpcLevel
|
|
;
|
|
; These two routines are defined here in assembly code so
|
|
; as to capture the caller's address.
|
|
;
|
|
|
|
cPublicFastCall KeAcquireQueuedSpinLockAtDpcLevel,1
|
|
sub esp, 8 ; make room to save time
|
|
push ecx ; save args
|
|
rdtsc ; get time
|
|
mov [esp].4, eax ; save low part
|
|
mov [esp].8, edx ; save high part
|
|
mov ecx, [esp] ; restore arg
|
|
fstCall KiAcquireQueuedLock
|
|
mov ecx, esp
|
|
fstCall KiQueueStatAcquire
|
|
add esp, 12 ; restore SP
|
|
fstRET KeAcquireQueuedSpinLockAtDpcLevel
|
|
fstENDP KeAcquireQueuedSpinLockAtDpcLevel
|
|
|
|
cPublicFastCall KeReleaseQueuedSpinLockFromDpcLevel,1
|
|
push ecx ; save args
|
|
mov ecx, esp ; set arg for stat release routine
|
|
fstCall KiQueueStatRelease
|
|
pop ecx
|
|
fstCall KiReleaseQueuedLock
|
|
fstRET KeReleaseQueuedSpinLockFromDpcLevel
|
|
fstENDP KeReleaseQueuedSpinLockFromDpcLevel
|
|
|
|
;
|
|
; KiCaptureQueuedSpinlockRoutines
|
|
;
|
|
; Replace the import table entries for the x86 HAL queued spinlock
|
|
; routines with our statistic capturing variety.
|
|
;
|
|
|
|
EXTRNP KeAcquireQueuedSpinLock,1,IMPORT,FASTCALL
|
|
EXTRNP KeAcquireQueuedSpinLockRaiseToSynch,1,IMPORT,FASTCALL
|
|
EXTRNP KeTryToAcquireQueuedSpinLockRaiseToSynch,2,IMPORT,FASTCALL
|
|
EXTRNP KeTryToAcquireQueuedSpinLock,2,IMPORT,FASTCALL
|
|
EXTRNP KeReleaseQueuedSpinLock,2,IMPORT,FASTCALL
|
|
|
|
cPublicFastCall KiCaptureQueuedSpinlockRoutines,0
|
|
|
|
mov eax, @__cap_KeAcquireQueuedSpinLock@4
|
|
mov [__imp_@KeAcquireQueuedSpinLock@4], eax
|
|
|
|
mov eax, @__cap_KeAcquireQueuedSpinLockRaiseToSynch@4
|
|
mov [__imp_@KeAcquireQueuedSpinLockRaiseToSynch@4], eax
|
|
|
|
mov eax, @__cap_KeTryToAcquireQueuedSpinLockRaiseToSynch@8
|
|
mov [__imp_@KeTryToAcquireQueuedSpinLockRaiseToSynch@8], eax
|
|
|
|
mov eax, @__cap_KeTryToAcquireQueuedSpinLock@8
|
|
mov [__imp_@KeTryToAcquireQueuedSpinLock@8], eax
|
|
|
|
mov eax, @__cap_KeReleaseQueuedSpinLock@8
|
|
mov [__imp_@KeReleaseQueuedSpinLock@8], eax
|
|
|
|
fstRet KiCaptureQueuedSpinlockRoutines
|
|
fstENDP KiCaptureQueuedSpinlockRoutines
|
|
|
|
else
|
|
|
|
;++
|
|
;
|
|
; VOID
|
|
; FASTCALL
|
|
; KeAcquireInStackQueuedSpinLockAtDpcLevel (
|
|
; IN PKSPIN_LOCK SpinLock,
|
|
; IN PKLOCK_QUEUE_HANDLE LockHandle
|
|
; )
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; This function acquires the specified in stack queued spin lock at the
|
|
; current IRQL.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; SpinLock (ecx) - Supplies the address of a spin lock.
|
|
;
|
|
; LockHandle (edx) - Supplies the address of an in stack lock handle.
|
|
;
|
|
; Return Value:
|
|
;
|
|
; None.
|
|
;--
|
|
|
|
align 16
|
|
cPublicFastCall KeAcquireInStackQueuedSpinLockAtDpcLevel,2
|
|
cPublicFpo 0,0
|
|
|
|
ifndef NT_UP
|
|
|
|
xor eax, eax ; set next link to NULL
|
|
mov [edx].LqhNext, eax ;
|
|
mov [edx].LqhLock, ecx ; set spin lock address
|
|
lea ecx, dword ptr [edx+LqhNext] ; compute address of lock queue
|
|
jmp short @KeAcquireQueuedSpinLockAtDpcLevel@4 ; finish in common code
|
|
|
|
else
|
|
|
|
fstRET KeAcquireInStackQueuedSpinLockAtDpcLevel
|
|
|
|
endif
|
|
|
|
fstENDP KeAcquireInStackQueuedSpinLockAtDpcLevel
|
|
|
|
page ,132
|
|
subttl "Acquire Queued SpinLock"
|
|
|
|
;++
|
|
;
|
|
; VOID
|
|
; KeAcquireQueuedSpinLockAtDpcLevel (
|
|
; IN PKSPIN_LOCK_QUEUE QueuedLock
|
|
; )
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; This function acquires the specified queued spinlock.
|
|
; No change to IRQL is made, IRQL is not returned. It is
|
|
; expected IRQL is sufficient to avoid context switch.
|
|
;
|
|
; Unlike the equivalent Ke versions of these routines,
|
|
; the argument to this routine is the address of the
|
|
; lock queue entry (for the lock to be acquired) in the
|
|
; PRCB rather than the LockQueueNumber. This saves us
|
|
; a couple of instructions as the address can be calculated
|
|
; at compile time.
|
|
;
|
|
; NOTE: This code may be modified for use during textmode
|
|
; setup if this is an MP kernel running with a UP HAL.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; LockQueueEntry (ecx) - Supplies the address of the queued
|
|
; spinlock entry in this processor's
|
|
; PRCB.
|
|
;
|
|
; Return Value:
|
|
;
|
|
; None.
|
|
;
|
|
; N.B. ecx is preserved, assembly code callers can take advantage
|
|
; of this by avoiding setting up ecx for the call to release if
|
|
; the caller can preserve the lock that long.
|
|
;
|
|
;--
|
|
|
|
; compile time assert sizeof(KSPIN_LOCK_QUEUE) == 8
|
|
|
|
.errnz (LOCK_QUEUE_HEADER_SIZE - 8)
|
|
|
|
align 16
|
|
cPublicFastCall KeAcquireQueuedSpinLockAtDpcLevel,1
|
|
cPublicFpo 0,0
|
|
|
|
ifndef NT_UP
|
|
|
|
; Get address of the actual lock.
|
|
|
|
mov edx, [ecx].LqLock
|
|
mov eax, ecx ; save Lock Queue entry address
|
|
|
|
; Exchange the value of the lock with the address of this
|
|
; Lock Queue entry.
|
|
|
|
xchg [edx], eax
|
|
|
|
cmp eax, 0 ; check if lock is held
|
|
jnz short @f ; jiff held
|
|
|
|
; note: the actual lock address will be word aligned, we use
|
|
; the bottom two bits as indicators, bit 0 is LOCK_QUEUE_WAIT,
|
|
; bit 1 is LOCK_QUEUE_OWNER.
|
|
|
|
or edx, LOCK_QUEUE_OWNER ; mark self as lock owner
|
|
mov [ecx].LqLock, edx
|
|
|
|
; lock has been acquired, return.
|
|
|
|
aqsl20:
|
|
|
|
endif
|
|
|
|
fstRET KeAcquireQueuedSpinLockAtDpcLevel
|
|
|
|
ifndef NT_UP
|
|
|
|
@@:
|
|
|
|
if DBG
|
|
|
|
; make sure it isn't already held by THIS processor.
|
|
|
|
test edx, LOCK_QUEUE_OWNER
|
|
jz short @f
|
|
|
|
; KeBugCheckEx(SPIN_LOCK_ALREADY_OWNED,
|
|
; actual lock address,
|
|
; my context,
|
|
; previous acquirer,
|
|
; 2);
|
|
|
|
stdCall _KeBugCheckEx,<SPIN_LOCK_ALREADY_OWNED,edx,ecx,eax,2>
|
|
@@:
|
|
|
|
endif
|
|
; The lock is already held by another processor. Set the wait
|
|
; bit in this processor's Lock Queue entry, then set the next
|
|
; field in the Lock Queue entry of the last processor to attempt
|
|
; to acquire the lock (this is the address returned by the xchg
|
|
; above) to point to THIS processor's lock queue entry.
|
|
|
|
or edx, LOCK_QUEUE_WAIT ; set lock bit
|
|
mov [ecx].LqLock, edx
|
|
|
|
mov [eax].LqNext, ecx ; set previous acquirer's
|
|
; next field.
|
|
|
|
; Wait.
|
|
@@:
|
|
test [ecx].LqLock, LOCK_QUEUE_WAIT ; check if still waiting
|
|
jz short aqsl20 ; jif lock acquired
|
|
YIELD ; fire avoidance.
|
|
jmp short @b ; else, continue waiting
|
|
|
|
endif
|
|
|
|
fstENDP KeAcquireQueuedSpinLockAtDpcLevel
|
|
|
|
page ,132
|
|
subttl "Release In Stack Queued SpinLock From Dpc Level"
|
|
;++
|
|
;
|
|
; VOID
|
|
; FASTCALL
|
|
; KeReleaseInStackQueuedSpinLockFromDpcLevel (
|
|
; IN PKLOCK_QUEUE_HANDLE LockHandle
|
|
; )
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; This function releases a queued spinlock and preserves the current
|
|
; IRQL.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; LockHandle (ecx) - Supplies the address of a lock handle.
|
|
;
|
|
; Return Value:
|
|
;
|
|
; None.
|
|
;
|
|
;--
|
|
|
|
cPublicFastCall KeReleaseInStackQueuedSpinLockFromDpcLevel,1
|
|
cPublicFpo 0,0
|
|
|
|
ifndef NT_UP
|
|
|
|
lea ecx, dword ptr[ecx+LqhNext] ; compute address of lock queue
|
|
jmp short @KeReleaseQueuedSpinLockFromDpcLevel@4 ; finish in common code
|
|
|
|
else
|
|
|
|
fstRET KeReleaseInStackQueuedSpinLockFromDpcLevel
|
|
|
|
endif
|
|
|
|
fstENDP KeReleaseInStackQueuedSpinLockFromDpcLevel
|
|
|
|
page ,132
|
|
subttl "Release Queued SpinLock"
|
|
|
|
;++
|
|
;
|
|
; VOID
|
|
; KeReleaseQueuedSpinLockFromDpcLevel (
|
|
; IN PKSPIN_LOCK_QUEUE QueuedLock
|
|
; )
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; This function releases a queued spinlock.
|
|
; No change to IRQL is made, IRQL is not returned. It is
|
|
; expected IRQL is sufficient to avoid context switch.
|
|
;
|
|
; NOTE: This code may be modified for use during textmode
|
|
; setup if this is an MP kernel running with a UP HAL.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; LockQueueEntry (ecx) - Supplies the address of the queued
|
|
; spinlock entry in this processor's
|
|
; PRCB.
|
|
;
|
|
; Return Value:
|
|
;
|
|
; None.
|
|
;
|
|
;--
|
|
|
|
cPublicFastCall KeReleaseQueuedSpinLockFromDpcLevel,1
|
|
cPublicFpo 0,0
|
|
|
|
.errnz (LOCK_QUEUE_OWNER - 2) ; error if not bit 1 for btr
|
|
|
|
ifndef NT_UP
|
|
|
|
|
|
mov eax, ecx ; need in eax for cmpxchg
|
|
mov edx, [ecx].LqNext
|
|
mov ecx, [ecx].LqLock
|
|
|
|
|
|
; Quick check: If Lock Queue entry's Next field is not NULL,
|
|
; there is another waiter. Don't bother with ANY atomic ops
|
|
; in this case.
|
|
;
|
|
; N.B. Careful ordering, the test will clear the CF bit and set
|
|
; the ZF bit appropriately if the Next Field (in EDX) is zero.
|
|
; The BTR will set the CF bit to the previous value of the owner
|
|
; bit.
|
|
|
|
test edx, edx
|
|
|
|
; Clear the "I am owner" field in the Lock entry.
|
|
|
|
btr ecx, 1 ; clear owner bit
|
|
|
|
if DBG
|
|
jnc short rqsl90 ; bugcheck if was not set
|
|
; tests CF
|
|
endif
|
|
|
|
mov [eax].LqLock, ecx ; clear lock bit in queue entry
|
|
jnz short rqsl40 ; jif another processor waits
|
|
; tests ZF
|
|
|
|
xor edx, edx ; new lock owner will be NULL
|
|
push eax ; save &PRCB->LockQueue[Number]
|
|
|
|
; Use compare exchange to attempt to clear the actual lock.
|
|
; If there are still no processors waiting for the lock when
|
|
; the compare exchange happens, the old contents of the lock
|
|
; should be the address of this lock entry (eax).
|
|
|
|
lock cmpxchg [ecx], edx ; store 0 if no waiters
|
|
pop eax ; restore lock queue address
|
|
jnz short rqsl60 ; jif store failed
|
|
|
|
; The lock has been released. Return to caller.
|
|
|
|
endif
|
|
|
|
fstRET KeReleaseQueuedSpinLockFromDpcLevel
|
|
|
|
ifndef NT_UP
|
|
|
|
; Another processor is waiting on this lock. Hand the lock
|
|
; to that processor by getting the address of its LockQueue
|
|
; entry, turning ON its owner bit and OFF its wait bit.
|
|
|
|
rqsl40: xor [edx].LqLock, (LOCK_QUEUE_OWNER+LOCK_QUEUE_WAIT)
|
|
|
|
; Done, the other processor now owns the lock, clear the next
|
|
; field in my LockQueue entry (to preserve the order for entering
|
|
; the queue again) and return.
|
|
|
|
mov [eax].LqNext, 0
|
|
fstRET KeReleaseQueuedSpinLockFromDpcLevel
|
|
|
|
; We get here if another processor is attempting to acquire
|
|
; the lock but had not yet updated the next field in this
|
|
; processor's Queued Lock Next field. Wait for the next
|
|
; field to be updated.
|
|
|
|
rqsl60: mov edx, [eax].LqNext
|
|
test edx, edx ; check if still 0
|
|
jnz short rqsl40 ; jif Next field now set.
|
|
YIELD ; wait a bit
|
|
jmp short rqsl60 ; continue waiting
|
|
|
|
if DBG
|
|
|
|
rqsl90:
|
|
stdCall _KeBugCheckEx,<SPIN_LOCK_NOT_OWNED,ecx,eax,0,0>
|
|
int 3 ; help debugger back trace.
|
|
|
|
endif
|
|
|
|
endif
|
|
|
|
fstENDP KeReleaseQueuedSpinLockFromDpcLevel
|
|
|
|
endif
|
|
|
|
page ,132
|
|
subttl "Try to Acquire Queued SpinLock"
|
|
|
|
;++
|
|
;
|
|
; LOGICAL
|
|
; KeTryToAcquireQueuedSpinLockAtRaisedIrql (
|
|
; IN PKSPIN_LOCK_QUEUE QueuedLock
|
|
; )
|
|
;
|
|
; Routine Description:
|
|
;
|
|
; This function attempts to acquire the specified queued spinlock.
|
|
; No change to IRQL is made, IRQL is not returned. It is
|
|
; expected IRQL is sufficient to avoid context switch.
|
|
;
|
|
; NOTE: This code may be modified for use during textmode
|
|
; setup if this is an MP kernel running with a UP HAL.
|
|
;
|
|
; Arguments:
|
|
;
|
|
; LockQueueEntry (ecx) - Supplies the address of the queued
|
|
; spinlock entry in this processor's
|
|
; PRCB.
|
|
;
|
|
; Return Value:
|
|
;
|
|
; TRUE if the lock was acquired, FALSE otherwise.
|
|
; N.B. ZF is set if FALSE returned, clear otherwise.
|
|
;
|
|
;--
|
|
|
|
|
|
align 16
|
|
cPublicFastCall KeTryToAcquireQueuedSpinLockAtRaisedIrql,1
|
|
cPublicFpo 0,0
|
|
|
|
ifndef NT_UP
|
|
|
|
; Get address of Lock Queue entry
|
|
|
|
mov edx, [ecx].LqLock
|
|
|
|
; Store the Lock Queue entry address in the lock ONLY if the
|
|
; current lock value is 0.
|
|
|
|
xor eax, eax ; old value must be 0
|
|
lock cmpxchg [edx], ecx
|
|
jnz short taqsl60
|
|
|
|
; Lock has been acquired.
|
|
|
|
; note: the actual lock address will be word aligned, we use
|
|
; the bottom two bits as indicators, bit 0 is LOCK_QUEUE_WAIT,
|
|
; bit 1 is LOCK_QUEUE_OWNER.
|
|
|
|
or edx, LOCK_QUEUE_OWNER ; mark self as lock owner
|
|
mov [ecx].LqLock, edx
|
|
|
|
ifdef QLOCK_STAT_GATHER
|
|
|
|
mov edx, [esp]
|
|
fstCall KiQueueStatTrySucceeded
|
|
|
|
endif
|
|
|
|
or eax, 1 ; return TRUE
|
|
|
|
fstRET KeTryToAcquireQueuedSpinLockAtRaisedIrql
|
|
|
|
taqsl60:
|
|
|
|
if 0
|
|
|
|
; note: it is not fatal if the current processor already owns the
|
|
; lock as this is perfectly normal - just return FALSE.
|
|
|
|
test edx, LOCK_QUEUE_OWNER
|
|
jz short @f
|
|
|
|
stdCall _KeBugCheckEx,<SPIN_LOCK_ALREADY_OWNED, edx, ecx,0,1>
|
|
@@:
|
|
|
|
endif
|
|
|
|
; The lock is already held by another processor. Indicate
|
|
; failure to the caller.
|
|
|
|
ifdef QLOCK_STAT_GATHER
|
|
|
|
fstCall KiQueueStatTryFailed
|
|
|
|
endif
|
|
|
|
xor eax, eax ; return FALSE
|
|
fstRET KeTryToAcquireQueuedSpinLockAtRaisedIrql
|
|
|
|
; In the event that this is an MP kernel running with a UP
|
|
; HAL, the following UP version is copied over the MP version
|
|
; during kernel initialization.
|
|
|
|
public _KeTryToAcquireQueuedSpinLockAtRaisedIrqlUP
|
|
_KeTryToAcquireQueuedSpinLockAtRaisedIrqlUP:
|
|
|
|
endif
|
|
|
|
; UP version, always succeed.
|
|
|
|
xor eax, eax
|
|
or eax, 1
|
|
fstRet KeTryToAcquireQueuedSpinLockAtRaisedIrql
|
|
|
|
fstENDP KeTryToAcquireQueuedSpinLockAtRaisedIrql
|
|
|
|
|
|
_TEXT$00 ends
|
|
|
|
end
|