- Implement KiAcquireDispatcherObject, KiReleaseDispatcherObject, used on SMP for dispatcher objects which use the volatile long member in the dispatcher header (such as gates).

- Implement KiTryThreadLock which tries to acquire the lock but exits if it can't, isntead of looping.
- Re-factor KeWaitForGate to make it SMP-safe by acquiring various locks when required.
- Fixed up KeSignalGateBoostPriority so it can also be SMP-safe, and also fix what exactly happens when the gate is signaled (directly make the other thread ready instead of doing a KiUnwaitThread on it).
- Split KxAcquireSpinLock/KxReleaseSpinLock into SMP vs UP like the other routines.

svn path=/trunk/; revision=24103
This commit is contained in:
Alex Ionescu 2006-09-14 05:53:51 +00:00
parent d53352c677
commit 20425875ce
3 changed files with 275 additions and 97 deletions

View file

@ -43,6 +43,7 @@ Author:
// Object Type Mask for Kernel Dispatcher Objects
//
#define KOBJECT_TYPE_MASK 0x7F
#define KOBJECT_LOCK_BIT 0x80
//
// Dispatcher Priority increments

View file

@ -384,45 +384,16 @@ KxUnwaitThreadForEvent(IN PKEVENT Event,
} while (WaitEntry != WaitList);
}
#ifndef _CONFIG_SMP
//
// Spinlock Acquisition at IRQL >= DISPATCH_LEVEL
// Spinlock Acquire at IRQL >= DISPATCH_LEVEL
//
FORCEINLINE
VOID
KxAcquireSpinLock(IN PKSPIN_LOCK SpinLock)
{
#ifdef CONFIG_SMP
for (;;)
{
/* Try to acquire it */
if (InterlockedBitTestAndSet((PLONG)SpinLock, 0))
{
/* Value changed... wait until it's locked */
while (*(volatile KSPIN_LOCK *)SpinLock == 1)
{
#ifdef DBG
/* On debug builds, we use a much slower but useful routine */
Kii386SpinOnSpinLock(SpinLock, 5);
#else
/* Otherwise, just yield and keep looping */
YieldProcessor();
#endif
}
}
else
{
#ifdef DBG
/* On debug builds, we OR in the KTHREAD */
*SpinLock = KeGetCurrentThread() | 1;
#endif
/* All is well, break out */
break;
}
}
#else
/* On UP builds, spinlocks don't exist at IRQL >= DISPATCH */
UNREFERENCED_PARAMETER(SpinLock);
#endif
}
//
@ -432,27 +403,30 @@ FORCEINLINE
VOID
KxReleaseSpinLock(IN PKSPIN_LOCK SpinLock)
{
#ifdef CONFIG_SMP
#ifdef DBG
/* Make sure that the threads match */
if ((KeGetCurrentThread() | 1) != *SpinLock)
{
/* They don't, bugcheck */
KeBugCheckEx(SPIN_LOCK_NOT_OWNED, SpinLock, 0, 0, 0);
}
#endif
/* Clear the lock */
InterlockedAnd(SpinLock, 0);
#else
/* On UP builds, spinlocks don't exist at IRQL >= DISPATCH */
UNREFERENCED_PARAMETER(SpinLock);
#endif
}
//
// Thread Scheduling Routines
// This routine protects against multiple CPU acquires, it's meaningless on UP.
//
#ifndef _CONFIG_SMP
VOID
FORCEINLINE
KiAcquireDispatcherObject(IN DISPATCHER_HEADER* Object)
{
UNREFERENCED_PARAMETER(Object);
}
//
// This routine protects against multiple CPU acquires, it's meaningless on UP.
//
VOID
FORCEINLINE
KiReleaseDispatcherObject(IN DISPATCHER_HEADER* Object)
{
UNREFERENCED_PARAMETER(Object);
}
KIRQL
FORCEINLINE
KiAcquireDispatcherLock(VOID)
@ -560,6 +534,17 @@ KiReleaseThreadLock(IN PKTHREAD Thread)
UNREFERENCED_PARAMETER(Thread);
}
//
// This routine protects against multiple CPU acquires, it's meaningless on UP.
//
FORCEINLINE
BOOLEAN
KiTryThreadLock(IN PKTHREAD Thread)
{
UNREFERENCED_PARAMETER(Thread);
return FALSE;
}
FORCEINLINE
VOID
KiCheckDeferredReadyList(IN PKPRCB Prcb)
@ -597,6 +582,98 @@ KiRequestApcInterrupt(IN BOOLEAN NeedApc,
#else
//
// Spinlock Acquisition at IRQL >= DISPATCH_LEVEL
//
FORCEINLINE
VOID
KxAcquireSpinLock(IN PKSPIN_LOCK SpinLock)
{
for (;;)
{
/* Try to acquire it */
if (InterlockedBitTestAndSet((PLONG)SpinLock, 0))
{
/* Value changed... wait until it's locked */
while (*(volatile KSPIN_LOCK *)SpinLock == 1)
{
#ifdef DBG
/* On debug builds, we use a much slower but useful routine */
Kii386SpinOnSpinLock(SpinLock, 5);
#else
/* Otherwise, just yield and keep looping */
YieldProcessor();
#endif
}
}
else
{
#ifdef DBG
/* On debug builds, we OR in the KTHREAD */
*SpinLock = KeGetCurrentThread() | 1;
#endif
/* All is well, break out */
break;
}
}
}
//
// Spinlock Release at IRQL >= DISPATCH_LEVEL
//
FORCEINLINE
VOID
KxReleaseSpinLock(IN PKSPIN_LOCK SpinLock)
{
#ifdef DBG
/* Make sure that the threads match */
if ((KeGetCurrentThread() | 1) != *SpinLock)
{
/* They don't, bugcheck */
KeBugCheckEx(SPIN_LOCK_NOT_OWNED, SpinLock, 0, 0, 0);
}
#endif
/* Clear the lock */
InterlockedAnd(SpinLock, 0);
}
KIRQL
FORCEINLINE
KiAcquireDispatcherObject(IN DISPATCHER_HEADER* Object)
{
LONG OldValue, NewValue;
/* Make sure we're at a safe level to touch the lock */
ASSERT(KeGetCurrentIrql() >= DISPATCH_LEVEL);
/* Start acquire loop */
do
{
/* Loop until the other CPU releases it */
while ((UCHAR)Object->Lock & KOBJECT_LOCK_BIT)
{
/* Let the CPU know that this is a loop */
YieldProcessor();
};
/* Try acquiring the lock now */
NewValue = InterlockedCompareExchange(&Object->Lock,
OldValue | KOBJECT_LOCK_BIT,
OldValue);
} while (NewValue != OldValue);
}
KIRQL
FORCEINLINE
KiReleaseDispatcherObject(IN DISPATCHER_HEADER* Object)
{
/* Make sure we're at a safe level to touch the lock */
ASSERT(KeGetCurrentIrql() >= DISPATCH_LEVEL);
/* Release it */
InterlockedAnd(&Object->Lock, ~KOBJECT_LOCK_BIT);
}
KIRQL
FORCEINLINE
KiAcquireDispatcherLock(VOID)
@ -753,6 +830,23 @@ KiReleaseThreadLock(IN PKTHREAD Thread)
InterlockedAnd(&Thread->ThreadLock, 0);
}
FORCEINLINE
BOOLEAN
KiTryThreadLock(IN PKTHREAD Thread)
{
LONG Value;
/* If the lock isn't acquired, return false */
if (!Thread->ThreadLock) return FALSE;
/* Otherwise, try to acquire it and check the result */
Value = 1;
Value = InterlockedExchange(&Thread->ThreadLock, &Value);
/* Return the lock state */
return (Value == TRUE);
}
FORCEINLINE
VOID
KiCheckDeferredReadyList(IN PKPRCB Prcb)

View file

@ -33,21 +33,23 @@ KeWaitForGate(IN PKGATE Gate,
IN KPROCESSOR_MODE WaitMode)
{
KLOCK_QUEUE_HANDLE ApcLock;
PKTHREAD CurrentThread = KeGetCurrentThread();
PKTHREAD Thread = KeGetCurrentThread();
PKWAIT_BLOCK GateWaitBlock;
NTSTATUS Status;
PKQUEUE Queue;
ASSERT_GATE(Gate);
ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
ASSERT(FALSE);
/* Start wait loop */
do
{
/* Acquire the APC lock */
KiAcquireApcLock(CurrentThread, &ApcLock);
KiAcquireApcLock(Thread, &ApcLock);
/* Check if a kernel APC is pending and we're below APC_LEVEL */
if ((CurrentThread->ApcState.KernelApcPending) &&
!(CurrentThread->SpecialApcDisable) &&
if ((Thread->ApcState.KernelApcPending) &&
!(Thread->SpecialApcDisable) &&
(ApcLock.OldIrql < APC_LEVEL))
{
/* Release the lock, this will fire the APC */
@ -55,41 +57,71 @@ KeWaitForGate(IN PKGATE Gate,
}
else
{
/* Check if we have a queue and lock the dispatcher if so */
Queue = Thread->Queue;
if (Queue) KiAcquireDispatcherLockAtDpcLevel();
/* Lock the thread */
KiAcquireThreadLock(Thread);
/* Lock the gate */
KiAcquireDispatcherObject(&Gate->Header);
/* Check if it's already signaled */
if (Gate->Header.SignalState)
{
/* Unsignal it */
Gate->Header.SignalState = 0;
/* Release the gate and thread locks */
KiReleaseDispatcherObject(&Gate->Header);
KiReleaseThreadLock(Thread);
/* Release the APC lock and return */
KiReleaseApcLock(&ApcLock);
return;
}
/* Setup a Wait Block */
GateWaitBlock = &CurrentThread->WaitBlock[0];
GateWaitBlock = &Thread->WaitBlock[0];
GateWaitBlock->Object = (PVOID)Gate;
GateWaitBlock->Thread = CurrentThread;
GateWaitBlock->Thread = Thread;
/* Set the Thread Wait Data */
CurrentThread->WaitMode = WaitMode;
CurrentThread->WaitReason = WaitReason;
CurrentThread->WaitIrql = ApcLock.OldIrql;
CurrentThread->State = GateWait;
CurrentThread->GateObject = Gate;
Thread->WaitMode = WaitMode;
Thread->WaitReason = WaitReason;
Thread->WaitIrql = ApcLock.OldIrql;
Thread->State = GateWait;
Thread->GateObject = Gate;
/* Insert into the Wait List */
InsertTailList(&Gate->Header.WaitListHead,
&GateWaitBlock->WaitListEntry);
/* Handle Kernel Queues */
if (CurrentThread->Queue) KiWakeQueue(CurrentThread->Queue);
/* Release the gate lock */
KiReleaseDispatcherObject(&Gate->Header);
/* Set swap busy */
KiSetThreadSwapBusy(Thread);
/* Release the thread lock */
KiReleaseThreadLock(Thread);
/* Check if we had a queue */
if (Queue)
{
/* Wake it up */
KiWakeQueue(Queue);
/* Release the dispatcher lock */
KiReleaseDispatcherLockFromDpcLevel();
}
/* Release the APC lock but stay at DPC level */
KiReleaseApcLockFromDpcLevel(&ApcLock);
/* Find a new thread to run */
Status = KiSwapThread(CurrentThread, KeGetCurrentPrcb());
Status = KiSwapThread(Thread, KeGetCurrentPrcb());
/* Check if we were executing an APC */
if (Status != STATUS_KERNEL_APC) return;
@ -104,49 +136,100 @@ KeSignalGateBoostPriority(IN PKGATE Gate)
PKTHREAD WaitThread;
PKWAIT_BLOCK WaitBlock;
KIRQL OldIrql;
NTSTATUS WaitStatus = STATUS_SUCCESS;
ASSERT_GATE(Gate);
ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
ASSERT(FALSE);
/* Raise to synch level */
OldIrql = KeRaiseIrqlToSynchLevel();
/* Make sure we're not already signaled or that the list is empty */
if (Gate->Header.SignalState)
/* Start entry loop */
for (;;)
{
/* Lower IRQL and quit */
KeLowerIrql(OldIrql);
return;
/* Raise to synch level */
OldIrql = KeRaiseIrqlToSynchLevel();
/* Lock the gate */
KiAcquireDispatcherObject(&Gate->Header);
/* Make sure we're not already signaled or that the list is empty */
if (Gate->Header.SignalState)
{
/* Lower IRQL and quit */
KeLowerIrql(OldIrql);
return;
}
/* Check if our wait list is empty */
if (IsListEmpty(&Gate->Header.WaitListHead))
{
/* It is, so signal the event */
Gate->Header.SignalState = 1;
}
else
{
/* Get WaitBlock */
WaitBlock = CONTAINING_RECORD(Gate->Header.WaitListHead.Flink,
KWAIT_BLOCK,
WaitListEntry);
/* Get the Associated thread */
WaitThread = WaitBlock->Thread;
/* Check to see if the waiting thread is locked */
if (!KiTryThreadLock(WaitThread))
{
/* Unlock the gate */
KiReleaseDispatcherObject(&Gate->Header);
/* Lower IRQL and loop again */
KeLowerIrql(OldIrql);
continue;
}
/* Remove it */
RemoveEntryList(&WaitBlock->WaitListEntry);
/* Clear wait status */
WaitThread->WaitStatus = 0;
/* Set state and CPU */
WaitThread->State = DeferredReady;
WaitThread->DeferredProcessor = KeGetCurrentPrcb()->Number;
/* Release the gate lock */
KiReleaseDispatcherObject(&Gate->Header);
/* Release the thread lock */
KiReleaseThreadLock(WaitThread);
/* FIXME: Boosting */
/* Check if we have a queue */
if (WaitThread->Queue)
{
/* Acquire the dispatcher lock */
KiAcquireDispatcherLockAtDpcLevel();
/* Check if we still have one */
if (WaitThread->Queue)
{
/* Increment active threads */
WaitThread->Queue->CurrentCount++;
}
/* Release lock */
KiReleaseDispatcherLockFromDpcLevel();
}
/* Make the thread ready */
KiReadyThread(WaitThread);
/* Exit the dispatcher */
KiExitDispatcher(OldIrql);
}
}
/* Check if our wait list is empty */
if (IsListEmpty(&Gate->Header.WaitListHead))
{
/* It is, so signal the event */
Gate->Header.SignalState = 1;
}
else
{
/* Get WaitBlock */
WaitBlock = CONTAINING_RECORD(Gate->Header.WaitListHead.Flink,
KWAIT_BLOCK,
WaitListEntry);
/* Remove it */
RemoveEntryList(&WaitBlock->WaitListEntry);
/* Get the Associated thread */
WaitThread = WaitBlock->Thread;
/* Increment the Queue's active threads */
if (WaitThread->Queue) WaitThread->Queue->CurrentCount++;
/* FIXME: This isn't really correct!!! */
KiUnwaitThread(WaitThread, WaitStatus, EVENT_INCREMENT);
}
/* Exit the dispatcher */
KiExitDispatcher(OldIrql);
/* If we got here, then there's no rescheduling. */
KiReleaseDispatcherObject(&Gate->Header);
KeLowerIrql(OldIrql);
}
/* EOF */