windows-nt/Source/XPSP1/NT/base/ntos/ke/alpha/spinlock.s
2020-09-26 16:20:57 +08:00

1231 lines
33 KiB
ArmAsm
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// TITLE("Spin Locks")
//++
//
// Copyright (c) 1990 Microsoft Corporation
// Copyright (c) 1992 Digital Equipment Corporation
//
// Module Name:
//
// spinlock.s
//
// Abstract:
//
// This module implements the routines for acquiring and releasing
// spin locks.
//
// Author:
//
// David N. Cutler (davec) 23-Mar-1990
// Joe Notarangelo 06-Apr-1992
//
// Environment:
//
// Kernel mode only.
//
// Revision History:
//
//--
#include "ksalpha.h"
//++
//
// VOID
// KeInitializeSpinLock (
// IN PKSPIN_LOCK SpinLock
// )
//
// Routine Description:
//
// This function initializes an executive spin lock.
//
// Argument:
//
// SpinLock (a0) - Supplies a pointer to the executive spin lock.
//
// Return Value:
//
// None.
//
//--
LEAF_ENTRY( KeInitializeSpinLock )
STP zero, 0(a0) // set spin lock not owned
ret zero, (ra) // return
.end KeInitializeSpinLock
//++
//
// VOID
// KeAcquireSpinLock (
// IN PKSPIN_LOCK SpinLock
// OUT PKIRQL OldIrql
// )
//
// Routine Description:
//
// This function raises the current IRQL to DISPATCH_LEVEL and acquires
// the specified executive spinlock.
//
// Arguments:
//
// SpinLock (a0) - Supplies a pointer to a executive spinlock.
//
// OldIrql (a1) - Supplies a pointer to a variable that receives the
// the previous IRQL value.
//
// Return Value:
//
// None.
//
//--
LEAF_ENTRY(KeAcquireSpinLock)
//
// Raise IRQL to DISPATCH_LEVEL and acquire the specified spinlock.
//
// N.B. The raise IRQL code is duplicated here is avoid any extra overhead
// since this is such a common operation.
//
// N.B. The previous IRQL must not be stored until the lock is owned.
//
// N.B. The longword surrounding the previous IRQL must not be read
// until the lock is owned.
//
bis a0, zero, t5 // t5 = address of spin lock
ldil a0, DISPATCH_LEVEL // set new IRQL
bis a1, zero, t0 // t0 = a1, a1 may be destroyed
SWAP_IRQL // swap irql, on return v0 = old irql
//
// Acquire the specified spinlock.
//
// N.B. code below branches forward if spinlock fails intentionally
// because branch forwards are predicted to miss
//
#if !defined(NT_UP)
10: LDP_L t3, 0(t5) // get current lock value
bis t5, zero, t4 // set ownership value
bne t3, 15f // if ne => lock owned
STP_C t4, 0(t5) // set lock owned
beq t4, 15f // if eq => stx_c failed
mb // synchronize memory access
#endif
//
// Save the old Irql at the address saved by the caller.
// Insure that the old Irql is updated with longword granularity.
//
ldq_u t1, 0(t0) // read quadword surrounding KIRQL
bic t0, 3, t2 // get address of containing longword
mskbl t1, t0, t1 // clear KIRQL byte in quadword
insbl v0, t0, v0 // get new KIRQL to correct byte
bis t1, v0, t1 // merge KIRQL into quadword
extll t1, t2, t1 // get longword containg KIRQL
stl t1, 0(t2) // store containing longword
ret zero, (ra) // return
//
// Attempt to acquire spinlock failed.
//
#if !defined(NT_UP)
15: LDP t3, 0(t5) // get current lock value
beq t3, 10b // retry acquire lock if unowned
br zero, 15b // loop in cache until lock free
#endif
.end KeAcquireSpinLock
SBTTL("Acquire SpinLock and Raise to Synch")
//++
//
// KIRQL
// KeAcquireSpinLockRaiseToSynch (
// IN PKSPIN_LOCK SpinLock
// )
//
// Routine Description:
//
// This function raises the current IRQL to synchronization level and
// acquires the specified spinlock.
//
// Arguments:
//
// SpinLock (a0) - Supplies a pointer to the spinlock that is to be
// acquired.
//
// Return Value:
//
// The previous IRQL is returned as the function value.
//
//--
LEAF_ENTRY(KeAcquireSpinLockRaiseToSynch)
#if !defined(NT_UP)
bis a0, zero, t5 // save spinlock address
ldl a0, KiSynchIrql // get synch level IRQL
//
// Raise IRQL and attempt to acquire the specified spinlock.
//
10: SWAP_IRQL // raise IRQL to synch level
LDP_L t3, 0(t5) // get current lock value
bis t5, zero, t4 // set ownership value
bne t3, 25f // if ne, lock owned
STP_C t4, 0(t5) // set lock owned
beq t4, 25f // if eq, conditional store failed
mb // synchronize subsequent reads
ret zero, (ra)
//
// Spinlock is owned, lower IRQL and spin in cache until it looks free.
//
25: bis v0, zero, a0 // get previous IRQL value
SWAP_IRQL // lower IRQL
bis v0, zero, a0 // save synch level IRQL
26: LDP t3, 0(t5) // get current lock value
beq t3, 10b // retry acquire if unowned
br zero, 26b // loop in cache until free
#else
ldl a0, KiSynchIrql // get synch level IRQL
SWAP_IRQL // rasie IRQL to synch levcel
ret zero, (ra) // return
.end KeAcquireSpinLockRaiseToSynch
#endif
//++
//
// KIRQL
// KeAcquireSpinLockRaiseToDpc (
// IN PKSPIN_LOCK SpinLock
// )
//
// Routine Description:
//
// This function raises the current IRQL to dispatcher level and acquires
// the specified spinlock.
//
// Arguments:
//
// SpinLock (a0) - Supplies a pointer to the spinlock that is to be
// acquired.
//
// Return Value:
//
// The previous IRQL is returned as the function value.
//
//--
#if !defined(NT_UP)
ALTERNATE_ENTRY(KeAcquireSpinLockRaiseToDpc)
bis a0, zero, t5 // save spinlock address
ldil a0, DISPATCH_LEVEL // set IRQL level
br 10b // finish in common code
.end KeAcquireSpinLockRaiseToSynch
#else
LEAF_ENTRY(KeAcquireSpinLockRaiseToDpc)
ldil a0, DISPATCH_LEVEL // set new IRQL
SWAP_IRQL // old irql in v0
ret zero, (ra)
.end KeAcquireSpinLockRaiseToDpc
#endif
//++
//
// VOID
// KeReleaseSpinLock (
// IN PKSPIN_LOCK SpinLock
// IN KIRQL OldIrql
// )
//
// Routine Description:
//
// This function releases an executive spin lock and lowers the IRQL
// to its previous value.
//
// Arguments:
//
// SpinLock (a0) - Supplies a pointer to an executive spin lock.
//
// OldIrql (a1) - Supplies the previous IRQL value.
//
// Return Value:
//
// None.
//
//--
LEAF_ENTRY(KeReleaseSpinLock)
//
// Release the specified spinlock.
//
#if !defined(NT_UP)
mb // synchronize memory access
STP zero, 0(a0) // set spin lock not owned
#endif
//
// Lower the IRQL to the specified level.
//
// N.B. The lower IRQL code is duplicated here is avoid any extra overhead
// since this is such a common operation.
//
10: bis a1, zero, a0 // a0 = new irql
SWAP_IRQL // change to new irql
ret zero, (ra) // return
.end KeReleaseSpinLock
//++
//
// BOOLEAN
// KeTryToAcquireSpinLock (
// IN PKSPIN_LOCK SpinLock
// OUT PKIRQL OldIrql
// )
//
// Routine Description:
//
// This function raises the current IRQL to DISPATCH_LEVEL and attempts
// to acquires the specified executive spinlock. If the spinlock can be
// acquired, then TRUE is returned. Otherwise, the IRQL is restored to
// its previous value and FALSE is returned.
//
// Arguments:
//
// SpinLock (a0) - Supplies a pointer to a executive spinlock.
//
// OldIrql (a1) - Supplies a pointer to a variable that receives the
// the previous IRQL value.
//
// Return Value:
//
// If the spin lock is acquired, then a value of TRUE is returned.
// Otherwise, a value of FALSE is returned.
//
//--
LEAF_ENTRY(KeTryToAcquireSpinLock)
//
// Raise IRQL to DISPATCH_LEVEL and try to acquire the specified spinlock.
//
// N.B. The raise IRQL code is duplicated here is avoid any extra overhead
// since this is such a common operation.
//
bis a0, zero, t5 // t5 = address of spin lock
ldil a0, DISPATCH_LEVEL // new irql
bis a1, zero, t11 // t11 = a1, a1 may be clobbered
SWAP_IRQL // a0 = new, on return v0 = old irql
//
// Try to acquire the specified spinlock.
//
// N.B. A noninterlocked test is done before the interlocked attempt. This
// allows spinning without interlocked cycles.
//
#if !defined(NT_UP)
LDP t0, 0(t5) // get current lock value
bne t0, 20f // if ne, lock owned
10: LDP_L t0, 0(t5) // get current lock value
bis t5, zero, t3 // t3 = ownership value
bne t0, 20f // if ne, spin lock owned
STP_C t3, 0(t5) // set lock owned
beq t3, 15f // if eq, store conditional failure
mb // synchronize memory access
#endif
//
// The attempt to acquire the specified spin lock succeeded.
//
// Save the old Irql at the address saved by the caller.
// Insure that the old Irql is updated with longword granularity.
//
ldq_u t1, 0(t11) // read quadword containing KIRQL
bic t11, 3, t2 // get address of containing longword
mskbl t1, t11, t1 // clear byte position of KIRQL
bis v0, zero, a0 // save old irql
insbl v0, t11, v0 // get KIRQL to correct byte
bis t1, v0, t1 // merge KIRQL into quadword
extll t1, t2, t1 // extract containing longword
stl t1, 0(t2) // store containing longword
ldil v0, TRUE // set return value
ret zero, (ra) // return
//
// The attempt to acquire the specified spin lock failed. Lower IRQL to its
// previous value and return FALSE.
//
// N.B. The lower IRQL code is duplicated here is avoid any extra overhead
// since this is such a common operation.
//
#if !defined(NT_UP)
20: bis v0, zero, a0 // set old IRQL value
SWAP_IRQL // change back to old irql(a0)
ldil v0, FALSE // set return to failed
ret zero, (ra) // return
//
// Attempt to acquire spinlock failed.
//
15: br zero, 10b // retry spinlock
#endif
.end KeTryToAcquireSpinLock
//++
//
// KIRQL
// 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.
//
// Arguments:
//
// SpinLock (a0) - Supplies a pointer to a kernel spin lock.
//
// Return Value:
//
// None.
//
//--
LEAF_ENTRY(KiAcquireSpinLock)
ALTERNATE_ENTRY(KeAcquireSpinLockAtDpcLevel)
#if !defined(NT_UP)
GET_CURRENT_THREAD // v0 = current thread address
10: LDP_L t2, 0(a0) // get current lock value
bis v0, zero, t3 // set ownership value
bne t2, 15f // if ne, spin lock owned
STP_C t3, 0(a0) // set spin lock owned
beq t3, 15f // if eq, store conditional failure
mb // synchronize memory access
ret zero, (ra) // return
//
// attempt to acquire spinlock failed.
//
15: LDP t2, 0(a0) // get current lock value
beq t2, 10b // retry acquire lock if unowned
br zero, 15b // loop in cache until lock free
#else
ret zero, (ra) // return
#endif
.end KiAcquireSpinLock
//++
//
// VOID
// 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.
//
// Arguments:
//
// SpinLock (a0) - Supplies a pointer to an executive spin lock.
//
// Return Value:
//
// None.
//
//--
LEAF_ENTRY(KiReleaseSpinLock)
ALTERNATE_ENTRY(KeReleaseSpinLockFromDpcLevel)
#if !defined(NT_UP)
mb // synchronize memory accesss
STP zero, 0(a0) // set spin lock not owned
#endif
ret zero, (ra) // return
.end KiReleaseSpinLock
//++
//
// KIRQL
// KiTryToAcquireSpinLock (
// IN PKSPIN_LOCK SpinLock
// )
//
// Routine Description:
//
// This function attempts to acquires the specified kernel spinlock. If
// the spinlock can be acquired, then TRUE is returned. Otherwise, FALSE
// is returned.
//
// N.B. This function assumes that the current IRQL is set properly.
//
// Arguments:
//
// SpinLock (a0) - Supplies a pointer to a kernel spin lock.
//
// Return Value:
//
// If the spin lock is acquired, then a value of TRUE is returned.
// Otherwise, a value of FALSE is returned.
//
//--
LEAF_ENTRY(KiTryToAcquireSpinLock)
#if !defined(NT_UP)
GET_CURRENT_THREAD // v0 = current thread address
10: LDP_L t2, 0(a0) // get current lock value
bis v0, zero, t3 // set ownership value
bne t2, 20f // if ne, spin lock owned
STP_C t3, 0(a0) // set spin lock owned
beq t3, 15f // if eq, conditional store failed
mb // synchronize memory access
ldil v0, TRUE // set success return value
ret zero, (ra) // return
20: ldil v0, FALSE // set failure return value
ret zero, (ra) // return
//
// Attempt to acquire spinlock failed.
//
15: br zero, 10b // retry
#else
ldil v0, TRUE // set success return value
ret zero, (ra) // return
#endif
.end KiTryToAcquireSpinLock
//++
//
// 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 (a0) - Supplies a pointer to a kernel spin lock.
//
// Return Value:
//
// TRUE - Spinlock appears available
// FALSE - SpinLock is busy
//--
#if !defined(NT_UP)
LEAF_ENTRY(KeTestSpinLock)
LDP t0, (a0) // get current spinlock value
ldil v0, 1 // default TRUE
cmovne t0, zero, v0 // if t0 != 0, return FALSE
ret zero, (ra) // return
.end KeTestSpinLock
#endif
SBTTL("Acquire Queued SpinLock and Raise IRQL")
//++
//
// VOID
// KeAcquireInStackQueuedSpinLock (
// IN PKSPIN_LOCK SpinLock,
// IN PKLOCK_QUEUE_HANDLE LockHandle
// )
//
// VOID
// KeAcquireInStackQueuedSpinLockRaiseToSynch (
// IN PKSPIN_LOCK SpinLock,
// IN PKLOCK_QUEUE_HANDLE LockHandle
// )
//
// Routine Description:
//
// This function raises the current IRQL to either synchronization or
// dispatch level and acquires the specified queued spinlock.
//
// Arguments:
//
// SpinLock (a0) - Supplies a pointer to a spin lock.
//
// LockHandle (a1) - Supplies a pointer to a lock handle.
//
// Return Value:
//
// None.
//
//--
LEAF_ENTRY(KeAcquireInStackQueuedSpinLock)
ldil v0, DISPATCH_LEVEL // get dispatch level IRQL
br zero, 5f //
ALTERNATE_ENTRY(KeAcquireInStackQueuedSpinLockRaiseToSynch)
ldl v0, KiSynchIrql // get synch level IRQL
5: mov a1, t5 // save address of lock handle
#if !defined(NT_UP)
STP zero, LqhNext(t5) // set next link to NULL
STP a0, LqhLock(t5) // set spin lock address
mb // synchronize memory access
#endif
mov v0, a0 // set new Irql value
SWAP_IRQL // raise Irql to specified level
stl v0, LqhOldIrql(t5) // save old IRQL
#if !defined(NT_UP)
ADDP t5, LqhNext, t5 // set address of lock queue
br zero, KxqAcquireQueuedSpinLock // finish in common code
#else
ret zero, (ra) // return
#endif
.end KeAcquireInStackQueuedSpinLock
SBTTL("Acquire Queued SpinLock and Raise IRQL")
//++
//
// KIRQL
// KeAcquireQueuedSpinLock (
// IN KSPIN_LOCK_QUEUE_NUMBER Number
// )
//
// KIRQL
// KeAcquireQueuedSpinLockRaiseToSynch (
// IN KSPIN_LOCK_QUEUE_NUMBER Number
// )
//
// Routine Description:
//
// This function raises the current IRQL to synchronization level and
// acquires the specified queued spinlock.
//
// Arguments:
//
// Number (a0) - Supplies the queued spinlock number.
//
// Return Value:
//
// The previous IRQL is returned as the function value.
//
//--
LEAF_ENTRY(KeAcquireQueuedSpinLock)
addq a0, a0, t5 // account for two addresses
ldil a0, DISPATCH_LEVEL // get dispatch level IRQL
br zero, 5f //
ALTERNATE_ENTRY(KeAcquireQueuedSpinLockRaiseToSynch)
addq a0, a0, t5 // account for two addresses
ldl a0, KiSynchIrql // get synch level IRQL
5: SWAP_IRQL // raise Irql to specified level
#if !defined(NT_UP)
bis v0, zero, t0 // save previous Irql
GET_PROCESSOR_CONTROL_BLOCK_BASE // get current prcb address
SPADDP t5, v0, t5 // entry address
lda t5, PbLockQueue(t5) //
ALTERNATE_ENTRY(KxqAcquireQueuedSpinLock)
LDP t4, LqLock(t5) // get associated spinlock address
10: LDP_L t3, 0(t4) // get current lock value
bis t5, zero, t2 // set lock ownership value
STP_C t2, 0(t4) //
beq t2, 50f // if eq, conditional store failed
bne t3, 30f // if ne, lock already owned
bis t4, LOCK_QUEUE_OWNER, t4 // set lock owner bit in lock entry
STP t4, LqLock(t5) //
mb // synchronize subsequent reads
20: bis t0, zero, v0 // set old IRQL value
#endif
ret zero, (ra) // return
//
// The lock is owned by another processor. Set the lock bit in the current
// processor lock queue entry, set the next link in the previous lock queue
// entry, and spin on the current processor's lock bit.
//
#if !defined(NT_UP)
30: bis t4, LOCK_QUEUE_WAIT, t4 // set lock wait bit in lock entry
STP t4, LqLock(t5) //
mb // synchronize memory access
STP t5, LqNext(t3) // set address of lock queue entry
40: LDP t4, LqLock(t5) // get lock address and lock wait bit
blbc t4, 20b // if lbc (lock wait), ownership granted
br zero, 40b // try again
//
// Conditional store failed.
//
50: br zero, 10b // try again
#endif
.end KeAcquireQueuedSpinLock
SBTTL("Release Queued SpinLock and Lower IRQL")
//++
//
// VOID
// KeReleaseInStackQueuedSpinLock (
// IN PKLOCK_QUEUE_HANDLE LockHandle
// )
//
// Routine Description:
//
// This function releases a queued spinlock and lowers the IRQL to its
// previous value.
//
// Arguments:
//
// LockHandle (a0) - Supplies a pointer to a lock handle.
//
// Return Value:
//
// None.
//
//--
LEAF_ENTRY(KeReleaseInStackQueuedSpinLock)
ldl t0, LqhOldIrql(a0) // save old IRQL
ADDP a0, LqhNext, t5 // set address of lock queue
br zero, KxqReleaseQueuedSpinLock // finish in common code
.end KeReleaseInStackQueuedSpinLock
SBTTL("Release Queued SpinLock and Lower IRQL")
//++
//
// VOID
// KeReleaseQueuedSpinLock (
// IN KSPIN_LOCK_QUEUE_NUMBER Number,
// IN KIRQL OldIrql
// )
//
// Routine Description:
//
// This function releases a queued spinlock and lowers the IRQL to its
// previous value.
//
// Arguments:
//
// Number (a0) - Supplies the queued spinlock number.
//
// OldIrql (a1) - Supplies the previous IRQL value.
//
// Return Value:
//
// None.
//
//--
LEAF_ENTRY(KeReleaseQueuedSpinLock)
bis a1, zero, t0 // save old IRQL
#if !defined(NT_UP)
addq a0, a0, t5 // account for two addresses
GET_PROCESSOR_CONTROL_BLOCK_BASE // get current prcb address
SPADDP t5, v0, t5 // compute per processor lock queue
lda t5, PbLockQueue(t5) // entry address
#endif
ALTERNATE_ENTRY(KxqReleaseQueuedSpinLock)
#if !defined(NT_UP)
mb // synchronize memory access
LDP t4, LqLock(t5) // get associated spin lock address
bic t4, LOCK_QUEUE_OWNER, t4 // clear lock owner bit in lock entry
10: LDP_L t3, 0(t4) // get current lock ownership value
xor t3, t5, t2 // set lock ownership value
bne t2, 20f // if ne, another processor waiting
STP_C t2, 0(t4) // set new ownership value
beq t2, 10b // if eq, conditional store failed
STP t4, LqLock(t5) // store associated spin lock address
mb // synchronize memory access
#endif
bis t0, zero, a0 // set old IRQL value
SWAP_IRQL // lower IRQL to previous level
ret zero, (ra) // return
//
// Another processor has inserted its lock queue entry in the lock queue,
// but has not yet written its lock queue entry address in the current
// processor's next link. Spin until the lock queue address is written.
//
#if !defined(NT_UP)
20: LDP t3, LqNext(t5) // get next lock queue entry address
beq t3, 20b // if eq, address not written yet
//
// Grant the next process in the lock queue ownership of the spinlock.
//
LDP t2, LqLock(t3) // get spinlock address and wait bit
STP zero, LqNext(t5) // clear next lock queue entry address
STP t4, LqLock(t5) // store associated spin lock address
bic t2, LOCK_QUEUE_WAIT, t2 // clear lock wait bit in lock entry
bis t2, LOCK_QUEUE_OWNER, t2 // set lock owner bit in lock entry
STP t2, LqLock(t3) //
mb // synchronize memory access
bis t0, zero, a0 // set old IRQL value
SWAP_IRQL // lower IRQL to previous level
ret zero, (ra) // return
#endif
.end KeReleaseQueuedSpinLock
SBTTL("Try to Acquire Queued SpinLock and Raise IRQL")
//++
//
// LOGICAL
// KeTryToAcquireQueuedSpinLock (
// IN KSPIN_LOCK_QUEUE_NUMBER Number
// OUT PKIRQL OldIrql
// )
//
// LOGICAL
// KeTryToAcquireQueuedSpinLockRaiseToSynch (
// IN KSPIN_LOCK_QUEUE_NUMBER Number
// OUT PKIRQL OldIrql
// )
//
// Routine Description:
//
// This function raises the current IRQL to synchronization level and
// attempts to acquire the specified queued spinlock. If the spinlock
// cannot be acquired, then IRQL is restored and FALSE is returned as
// the function value. Otherwise, TRUE is returned as the function
// value.
//
// Arguments:
//
// Number (a0) - Supplies the queued spinlock number.
//
// OldIrql (a1) - Supplies a pointer to a variable that receives the
// the previous IRQL value.
//
// Return Value:
//
// If the spin lock is acquired, then a value of TRUE is returned.
// Otherwise, a value of FALSE is returned.
//
//--
LEAF_ENTRY(KeTryToAcquireQueuedSpinLock)
addq a0, a0, t5 // account for two addresses
ldil a0, DISPATCH_LEVEL // get dispatch level irql
br zero, 5f //
ALTERNATE_ENTRY(KeTryToAcquireQueuedSpinLockRaiseToSynch)
addq a0, a0, t5 // account for two addresses
ldl a0, KiSynchIrql // get synch level irql
5: bis a1, zero, t0 // save previous irql address
SWAP_IRQL // raise irql to specified level
bis v0, zero, t1 // save previous irql
#if !defined(NT_UP)
GET_PROCESSOR_CONTROL_BLOCK_BASE // get current prcb address
SPADDP t5, v0, t5 // compute per processor lock queue
lda t5, PbLockQueue(t5) // entry address
LDP t4, LqLock(t5) // get associated spinlock address
//
// Try to acquire the specified spinlock.
//
// N.B. A noninterlocked test is done before the interlocked attempt. This
// allows spinning without interlocked cycles.
//
LDP t3, 0(t4) // get current lock value
bne t3, 20f // if ne, lock owned
10: LDP_L t3, 0(t4) // get current lock value
bis t5, zero, t2 // set lock ownership value
bne t3, 20f // if ne, spin lock owned
STP_C t2, 0(t4) // set lock owned
beq t2, 30f // if eq, store conditional failure
//
// The attempt to acquire the specified spin lock succeeded. Set the spin
// lock owner bit and save the old irql at the address specified by the
// caller. Insure that the old Irql is updated with longword granularity.
//
bis t4, LOCK_QUEUE_OWNER, t4 // set lock owner bit in lock entry
STP t4, LqLock(t5) //
mb // synchronize memory access
#endif
ldq_u t2, 0(t0) // get quadword containing irql
bic t0, 3, t3 // get containing longword address
mskbl t2, t0, t2 // clear byte position of Irql
insbl t1, t0, t1 // shift irql to correct byte
bis t2, t1, t2 // merge irql into quadword
extll t2, t3, t2 // extract containing longword
stl t2, 0(t3) // store containing longword
ldil v0, TRUE // set return value
ret zero, (ra) // return
//
// The attempt to acquire the specified spin lock failed. Lower IRQL to its
// previous value and return FALSE.
//
#if !defined(NT_UP)
20: bis t1, zero, a0 // set old irql value
SWAP_IRQL // lower irql to previous level
ldil v0, FALSE // set return value
ret zero, (ra) // return
//
// Attempt to acquire spinlock failed.
//
30: br zero, 10b // retry spinlock
#endif
.end KeTryToAcquireQueuedSpinLock
SBTTL("Acquire Queued SpinLock at Current IRQL")
//++
// VOID
// KeAcquireInStackQueuedSpinLockAtDpcLevel (
// IN PKSPIN_LOCK SpinLock,
// IN PKLOCK_QUEUE_HANDLE LockHandle
// )
//
// Routine Description:
//
// This function acquires the specified queued spinlock at the current
// IRQL.
//
// Arguments:
//
// SpinLock (a0) - Supplies the address of a spin lock.
//
// LockHandle (a1) - Supplies the address of an in stack lock handle.
//
// Return Value:
//
// None.
//
//--
LEAF_ENTRY(KeAcquireInStackQueuedSpinLockAtDpcLevel)
#if !defined(NT_UP)
STP zero, LqhNext(a1) // set next link to NULL
STP a0, LqhLock(a1) // set spin lock address
#endif
ADDP a1, LqhNext, a0 // compute address of lock queue
//++
//
// VOID
// KeAcquireQueuedSpinLockAtDpcLevel (
// IN PKSPIN_LOCK_QUEUE LockQueue
// )
//
// Routine Description:
//
// This function acquires the specified queued spinlock at the current
// IRQL.
//
// Arguments:
//
// LockQueue (a0) - Supplies the address of the lock queue entry.
//
// Return Value:
//
// None.
//
//--
ALTERNATE_ENTRY(KeAcquireQueuedSpinLockAtDpcLevel)
#if !defined(NT_UP)
mb // synchronize memory access
LDP t4, LqLock(a0) // get associated spinlock address
10: LDP_L t3, 0(t4) // get current lock value
bis a0, zero, t2 // set lock ownership value
STP_C t2, 0(t4) //
beq t2, 50f // if eq, conditional store failed
bne t3, 30f // if ne, lock already owned
bis t4, LOCK_QUEUE_OWNER, t4 // set lock owner bit in lock entry
STP t4, LqLock(a0) //
mb // synchronize subsequent reads
#endif
20: ret zero, (ra) // return
//
// The lock is owned by another processor. Set the lock bit in the current
// processor lock queue entry, set the next link in the previous lock queue
// entry, and spin on the current processor's lock bit.
//
#if !defined(NT_UP)
30: bis t4, LOCK_QUEUE_WAIT, t4 // set lock wait bit in lock entry
STP t4, LqLock(a0) //
mb // synchronize memory access
STP a0, LqNext(t3) // set address of lock queue entry
40: LDP t4, LqLock(a0) // get lock address and lock wait bit
blbc t4, 20b // if lbc (lock wait), ownership granted
br zero, 40b // try again
//
// Conditional store failed.
//
50: br zero, 10b // try again
#endif
.end KeAcquireInStackQueuedSpinLockAtDpcLevel
SBTTL("Release Queued SpinLock at Current IRQL")
//++
//
// VOID
// KeReleaseInStackQueuedSpinLockFromDpcLevel (
// IN PKLOCK_QUEUE_HANDLE LockHandle
// )
//
// Routine Description:
//
// This function releases a queued spinlock and preserves the current
// IRQL.
//
// Arguments:
//
// LockHandle (a0) - Supplies the address of a lock handle.
//
// Return Value:
//
// None.
//
//--
LEAF_ENTRY(KeReleaseInStackQueuedSpinLockFromDpcLevel)
ADDP a0, LqhNext, a0 // compute address of lock queue
//++
//
// VOID
// KeReleaseQueuedSpinLockFromDpcLevel (
// IN PKSPIN_LOCK_QUEUE LockQueue
// )
//
// Routine Description:
//
// This function releases a queued spinlock and preserves the current
// IRQL.
//
// Arguments:
//
// LockQueue (a0) - Supplies the address of the lock queue entry.
//
// Return Value:
//
// None.
//
//--
ALTERNATE_ENTRY(KeReleaseQueuedSpinLockFromDpcLevel)
#if !defined(NT_UP)
mb // synchronize memory access
LDP t4, LqLock(a0) // get associate spin lock address
bic t4, LOCK_QUEUE_OWNER, t4 // clear lock owner bit in lock entry
10: LDP_L t3, 0(t4) // get current lock ownership value
xor t3, a0, t2 // set lock ownership value
bne t2, 20f // if ne, another processor waiting
STP_C t2, 0(t4) // set new ownership value
beq t2, 10b // if eq, conditional store failed
STP t4, LqLock(a0) //
mb // synchronize memory access
ret zero, (ra) // return
//
// Another processor has inserted its lock queue entry in the lock queue,
// but has not yet written its lock queue entry address in the current
// processor's next link. Spin until the lock queue address is written.
//
20: LDP t3, LqNext(a0) // get next lock queue entry address
beq t3, 20b // if eq, address not written yet
//
// Grant the next process in the lock queue ownership of the spinlock.
//
LDP t2, LqLock(t3) // get spinlock address and lock bit
STP zero, LqNext(a0) // clear next lock queue entry address
STP t4, LqLock(a0) //
bic t2, LOCK_QUEUE_WAIT, t2 // clear lock wait bit in lock entry
bis t2, LOCK_QUEUE_OWNER, t2 // set lock owner bit in lock entry
STP t2, LqLock(t3) //
mb // synchronize memory access
#endif
ret zero, (ra) // return
.end KeReleaseInStackQueuedSpinLockFromDpcLevel