- Fix several flaws in APC logic.

- Properly support Special APC Disabling.
- Allow Guarded Mutex and Guarded Regions to function properly.
- Optimize some code and add some additional checks.
- Fix several bugs in KeFreezeAllThreads

svn path=/trunk/; revision=18117
This commit is contained in:
Alex Ionescu 2005-09-27 01:57:27 +00:00
parent bfcd1fab3c
commit 29322a96bc
3 changed files with 328 additions and 190 deletions

View file

@ -55,7 +55,8 @@ extern ULONG_PTR KERNEL_BASE;
PKTHREAD _Thread = KeGetCurrentThread(); \ PKTHREAD _Thread = KeGetCurrentThread(); \
if((_Thread) && (++_Thread->KernelApcDisable == 0)) \ if((_Thread) && (++_Thread->KernelApcDisable == 0)) \
{ \ { \
if (!IsListEmpty(&_Thread->ApcState.ApcListHead[KernelMode])) \ if (!IsListEmpty(&_Thread->ApcState.ApcListHead[KernelMode]) && \
(_Thread->SpecialApcDisable == 0)) \
{ \ { \
KiKernelApcDeliveryCheck(); \ KiKernelApcDeliveryCheck(); \
} \ } \

View file

@ -123,7 +123,8 @@ KeLeaveCriticalRegion (VOID)
if((Thread) && (++Thread->KernelApcDisable == 0)) if((Thread) && (++Thread->KernelApcDisable == 0))
{ {
/* Check if we need to request an APC Delivery */ /* Check if we need to request an APC Delivery */
if (!IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode])) if ((!IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode])) &&
(Thread->SpecialApcDisable == 0))
{ {
/* Check for the right environment */ /* Check for the right environment */
KiKernelApcDeliveryCheck(); KiKernelApcDeliveryCheck();
@ -225,6 +226,32 @@ KeInitializeApc(IN PKAPC Apc,
} }
} }
static
__inline
VOID
KiRequestApcInterrupt(IN PKTHREAD Thread)
{
#ifdef CONFIG_SMP
PKPRCB Prcb, CurrentPrcb;
LONG i;
CurrentPrcb = KeGetCurrentPrcb();
for (i = 0; i < KeNumberProcessors; i++)
{
Prcb = ((PKPCR)(KPCR_BASE + i * PAGE_SIZE))->Prcb;
if (Prcb->CurrentThread == Thread)
{
ASSERT (CurrentPrcb != Prcb);
KiIpiSendRequest(Prcb->SetMember, IPI_APC);
break;
}
}
ASSERT (i < KeNumberProcessors);
#else
HalRequestSoftwareInterrupt(APC_LEVEL);
#endif
}
/*++ /*++
* KiInsertQueueApc * KiInsertQueueApc
* *
@ -238,8 +265,7 @@ KeInitializeApc(IN PKAPC Apc,
* PriorityBoost - Priority Boost to apply to the Thread. * PriorityBoost - Priority Boost to apply to the Thread.
* *
* Returns: * Returns:
* If the APC is already inserted or APC queueing is disabled, FALSE. * None
* Otherwise, TRUE.
* *
* Remarks: * Remarks:
* The APC will execute at APC_LEVEL for the KernelRoutine registered, and * The APC will execute at APC_LEVEL for the KernelRoutine registered, and
@ -248,122 +274,175 @@ KeInitializeApc(IN PKAPC Apc,
* Callers of this routine must have locked the dipatcher database. * Callers of this routine must have locked the dipatcher database.
* *
*--*/ *--*/
BOOLEAN VOID
STDCALL FASTCALL
KiInsertQueueApc(PKAPC Apc, KiInsertQueueApc(PKAPC Apc,
KPRIORITY PriorityBoost) KPRIORITY PriorityBoost)
{ {
PKTHREAD Thread = Apc->Thread; PKTHREAD Thread = Apc->Thread;
PKAPC_STATE ApcState;
KPROCESSOR_MODE ApcMode;
PLIST_ENTRY ListHead, NextEntry;
PKAPC QueuedApc;
NTSTATUS Status;
/* Acquire the lock (only needed on MP) */
KeAcquireSpinLockAtDpcLevel(&Thread->ApcQueueLock); KeAcquireSpinLockAtDpcLevel(&Thread->ApcQueueLock);
/* Don't do anything if the APC is already inserted */ /* Little undocumented feature: Special Apc State Index */
if (Apc->Inserted) { if (Apc->ApcStateIndex == 3)
{
KeReleaseSpinLockFromDpcLevel(&Thread->ApcQueueLock); /* This tells us to use the thread's */
return FALSE; Apc->ApcStateIndex = Thread->ApcStateIndex;
} }
/* Get the APC State for this Index, and the mode too */
ApcState = Thread->ApcStatePointer[(int)Apc->ApcStateIndex];
ApcMode = Apc->ApcMode;
/* Three scenarios: /* Three scenarios:
1) Kernel APC with Normal Routine or User APC = Put it at the end of the List * 1) Kernel APC with Normal Routine or User APC = Put it at the end of the List
2) User APC which is PsExitSpecialApc = Put it at the front of the List * 2) User APC which is PsExitSpecialApc = Put it at the front of the List
3) Kernel APC without Normal Routine = Put it at the end of the No-Normal Routine Kernel APC list * 3) Kernel APC without Normal Routine = Put it at the end of the No-Normal Routine Kernel APC list
*/ */
if ((Apc->ApcMode != KernelMode) && (Apc->KernelRoutine == (PKKERNEL_ROUTINE)PsExitSpecialApc)) { if (Apc->NormalRoutine)
{
/* Normal APC; is it the Thread Termination APC? */
if ((ApcMode != KernelMode) && (Apc->KernelRoutine == PsExitSpecialApc))
{
/* Set User APC pending to true */
Thread->ApcState.UserApcPending = TRUE;
DPRINT("Inserting the Thread Exit APC for '%.16s' into the Queue\n", ((PETHREAD)Thread)->ThreadsProcess->ImageFileName); /* Insert it at the top of the list */
Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->UserApcPending = TRUE; InsertHeadList(&ApcState->ApcListHead[ApcMode], &Apc->ApcListEntry);
InsertHeadList(&Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->ApcListHead[(int)Apc->ApcMode],
&Apc->ApcListEntry);
} else if (Apc->NormalRoutine == NULL) { /* Display debug message */
DPRINT1("Inserted the Thread Exit APC for '%.16s' into the Queue\n",
((PETHREAD)Thread)->ThreadsProcess->ImageFileName);
}
else
{
/* Regular user or kernel Normal APC */
InsertTailList(&ApcState->ApcListHead[ApcMode], &Apc->ApcListEntry);
DPRINT("Inserting Special APC %x for '%.16s' into the Queue\n", Apc, ((PETHREAD)Thread)->ThreadsProcess->ImageFileName); /* Display debug message */
DPRINT("Inserted Normal APC for '%.16s' into the Queue\n",
((PETHREAD)Thread)->ThreadsProcess->ImageFileName);
}
}
else
{
/* Special APC, find the first Normal APC in the list */
ListHead = &ApcState->ApcListHead[ApcMode];
NextEntry = ListHead->Flink;
while(NextEntry != ListHead)
{
/* Get the APC */
QueuedApc = CONTAINING_RECORD(NextEntry, KAPC, ApcListEntry);
/* insert special apc before normal apcs (if any) but after the last special apc (fifo) */ /* Is this a Normal APC? If so, break */
InsertAscendingListFIFO( if (QueuedApc->NormalRoutine) break;
&Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->ApcListHead[(int)Apc->ApcMode],
Apc,
KAPC,
ApcListEntry,
NormalRoutine /* sort field */
);
} else { /* Move to the next APC in the Queue */
NextEntry = NextEntry->Flink;
}
DPRINT("Inserting Normal APC %x for '%.16s' into the %x Queue\n", Apc, ((PETHREAD)Thread)->ThreadsProcess->ImageFileName, Apc->ApcMode); /* Move to the APC before this one (ie: the last Special APC) */
InsertTailList(&Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->ApcListHead[(int)Apc->ApcMode], NextEntry = NextEntry->Blink;
&Apc->ApcListEntry);
/* Insert us here */
InsertHeadList(NextEntry, &Apc->ApcListEntry);
DPRINT("Inserted Special APC for '%.16s' into the Queue\n",
((PETHREAD)Thread)->ThreadsProcess->ImageFileName);
} }
/* Confirm Insertion */ /* Now check if the Apc State Indexes match */
Apc->Inserted = TRUE; if (Thread->ApcStateIndex == Apc->ApcStateIndex)
{
KeReleaseSpinLockFromDpcLevel(&Thread->ApcQueueLock); /* Check that if the thread matches */
if (Thread == KeGetCurrentThread())
/* {
* Three possibilites here again: /* Check if this is kernel mode */
* 1) Kernel APC, The thread is Running: Request an Interrupt if (ApcMode == KernelMode)
* 2) Kernel APC, The Thread is Waiting at PASSIVE_LEVEL and APCs are enabled and not in progress: Unwait the Thread
* 3) User APC, Unwait the Thread if it is alertable
*/
if (Apc->ApcMode == KernelMode) {
/* Set Kernel APC pending */
Thread->ApcState.KernelApcPending = TRUE;
/* Check the Thread State */
if (Thread->State == Running) {
#ifdef CONFIG_SMP
PKPRCB Prcb, CurrentPrcb;
LONG i;
#endif
DPRINT ("Requesting APC Interrupt for Running Thread \n");
#ifdef CONFIG_SMP
CurrentPrcb = KeGetCurrentPrcb();
if (CurrentPrcb->CurrentThread == Thread)
{ {
HalRequestSoftwareInterrupt(APC_LEVEL); /* All valid, a Kernel APC is pending now */
Thread->ApcState.KernelApcPending = TRUE;
/* Check if Special APCs are disabled */
if (Thread->SpecialApcDisable == 0)
{
/* They're not, so request the interrupt */
HalRequestSoftwareInterrupt(APC_LEVEL);
}
}
}
else
{
/* Check if this is a non-kernel mode APC */
if (ApcMode != KernelMode)
{
/* Not a Kernel-Mode APC. Are we waiting in user-mode? */
if ((Thread->State == Waiting) && (Thread->WaitMode == UserMode))
{
/* The thread is waiting. Are we alertable, or is an APC pending */
if ((Thread->Alertable) || (Thread->ApcState.UserApcPending))
{
/* Set user-mode APC pending */
Thread->ApcState.UserApcPending = TRUE;
Status = STATUS_USER_APC;
goto Unwait;
}
}
} }
else else
{ {
for (i = 0; i < KeNumberProcessors; i++) /* Kernel-mode APC, set us pending */
{ Thread->ApcState.KernelApcPending = TRUE;
Prcb = ((PKPCR)(KPCR_BASE + i * PAGE_SIZE))->Prcb;
if (Prcb->CurrentThread == Thread) /* Are we currently running? */
{ if (Thread->State == Running)
ASSERT (CurrentPrcb != Prcb); {
KiIpiSendRequest(Prcb->SetMember, IPI_APC); /* The thread is running, so send an APC request */
break; KiRequestApcInterrupt(Thread);
} }
} else
ASSERT (i < KeNumberProcessors); {
/*
* If the thread is Waiting at PASSIVE_LEVEL AND
* Special APCs are not disabled AND
* He is a Normal APC AND
* Kernel APCs are not disabled AND
* Kernel APC is not pending OR
* He is a Special APC THEN
* Unwait thread with STATUS_KERNEL_APC
*/
if ((Thread->State == Waiting) &&
(Thread->WaitIrql == PASSIVE_LEVEL) &&
(!Thread->SpecialApcDisable) &&
((!Apc->NormalRoutine) ||
((!Thread->KernelApcDisable) &&
(!Thread->ApcState.KernelApcInProgress))))
{
/* We'll unwait with this status */
Status = STATUS_KERNEL_APC;
/* Wake up the thread */
Unwait:
DPRINT("Waking up Thread for %lx Delivery \n", Status);
KiAbortWaitThread(Thread, Status, PriorityBoost);
}
else
{
/* FIXME: Handle deferred ready sometime far far in the future */
}
}
} }
#else
HalRequestSoftwareInterrupt(APC_LEVEL);
#endif
} else if ((Thread->State == Waiting) && (Thread->WaitIrql == PASSIVE_LEVEL) &&
((Apc->NormalRoutine == NULL) ||
((!Thread->KernelApcDisable) && (!Thread->ApcState.KernelApcInProgress)))) {
DPRINT("Waking up Thread for Kernel-Mode APC Delivery \n");
KiAbortWaitThread(Thread, STATUS_KERNEL_APC, PriorityBoost);
} }
} else if ((Thread->State == Waiting) &&
(Thread->WaitMode != KernelMode) &&
(Thread->Alertable)) {
DPRINT("Waking up Thread for User-Mode APC Delivery \n");
Thread->ApcState.UserApcPending = TRUE;
KiAbortWaitThread(Thread, STATUS_USER_APC, PriorityBoost);
} }
return TRUE; /* Return to caller */
KeReleaseSpinLockFromDpcLevel(&Thread->ApcQueueLock);
return;
} }
/*++ /*++
@ -399,11 +478,9 @@ KeInsertQueueApc(PKAPC Apc,
PVOID SystemArgument1, PVOID SystemArgument1,
PVOID SystemArgument2, PVOID SystemArgument2,
KPRIORITY PriorityBoost) KPRIORITY PriorityBoost)
{ {
KIRQL OldIrql; KIRQL OldIrql;
PKTHREAD Thread; PKTHREAD Thread;
BOOLEAN Inserted;
ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL); ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
DPRINT("KeInsertQueueApc(Apc %x, SystemArgument1 %x, " DPRINT("KeInsertQueueApc(Apc %x, SystemArgument1 %x, "
@ -416,25 +493,25 @@ KeInsertQueueApc(PKAPC Apc,
/* Get the Thread specified in the APC */ /* Get the Thread specified in the APC */
Thread = Apc->Thread; Thread = Apc->Thread;
/* Make sure the thread allows APC Queues. /* Make sure we can Queue APCs and that this one isn't already inserted */
* The thread is not apc queueable, for instance, when it's (about to be) terminated. if ((Thread->ApcQueueable == FALSE) && (Apc->Inserted == TRUE))
*/ {
if (Thread->ApcQueueable == FALSE) { DPRINT("Can't queue the APC\n");
DPRINT("Thread doesn't allow APC Queues\n");
KeReleaseDispatcherDatabaseLock(OldIrql); KeReleaseDispatcherDatabaseLock(OldIrql);
return FALSE; return FALSE;
} }
/* Set the System Arguments */ /* Set the System Arguments and set it as inserted */
Apc->SystemArgument1 = SystemArgument1; Apc->SystemArgument1 = SystemArgument1;
Apc->SystemArgument2 = SystemArgument2; Apc->SystemArgument2 = SystemArgument2;
Apc->Inserted = TRUE;
/* Call the Internal Function */ /* Call the Internal Function */
Inserted = KiInsertQueueApc(Apc, PriorityBoost); KiInsertQueueApc(Apc, PriorityBoost);
/* Return Sucess if we are here */ /* Return Sucess if we are here */
KeReleaseDispatcherDatabaseLock(OldIrql); KeReleaseDispatcherDatabaseLock(OldIrql);
return Inserted; return TRUE;
} }
/*++ /*++
@ -464,23 +541,25 @@ KeFlushQueueApc(IN PKTHREAD Thread,
{ {
KIRQL OldIrql; KIRQL OldIrql;
PKAPC Apc; PKAPC Apc;
PLIST_ENTRY FirstEntry = NULL; PLIST_ENTRY FirstEntry, CurrentEntry;
/* Lock the Dispatcher Database and APC Queue */ /* Lock the Dispatcher Database and APC Queue */
OldIrql = KeAcquireDispatcherDatabaseLock(); OldIrql = KeAcquireDispatcherDatabaseLock();
KeAcquireSpinLockAtDpcLevel(&Thread->ApcQueueLock); KeAcquireSpinLockAtDpcLevel(&Thread->ApcQueueLock);
/* mark all apcs as not-inserted */ if (IsListEmpty(&Thread->ApcState.ApcListHead[PreviousMode])) {
LIST_FOR_EACH(Apc, &Thread->ApcState.ApcListHead[PreviousMode], KAPC, ApcListEntry) { FirstEntry = NULL;
Apc->Inserted = FALSE; } else {
}
if (!IsListEmpty(&Thread->ApcState.ApcListHead[PreviousMode])) {
FirstEntry = Thread->ApcState.ApcListHead[PreviousMode].Flink; FirstEntry = Thread->ApcState.ApcListHead[PreviousMode].Flink;
/* unlink list head from the rest of the list */
RemoveEntryList(&Thread->ApcState.ApcListHead[PreviousMode]); RemoveEntryList(&Thread->ApcState.ApcListHead[PreviousMode]);
CurrentEntry = FirstEntry;
do {
Apc = CONTAINING_RECORD(CurrentEntry, KAPC, ApcListEntry);
Apc->Inserted = FALSE;
CurrentEntry = CurrentEntry->Flink;
} while (CurrentEntry != FirstEntry);
} }
/* Release the locks */ /* Release the locks */
KeReleaseSpinLockFromDpcLevel(&Thread->ApcQueueLock); KeReleaseSpinLockFromDpcLevel(&Thread->ApcQueueLock);
KeReleaseDispatcherDatabaseLock(OldIrql); KeReleaseDispatcherDatabaseLock(OldIrql);
@ -516,46 +595,42 @@ KeRemoveQueueApc(PKAPC Apc)
{ {
KIRQL OldIrql; KIRQL OldIrql;
PKTHREAD Thread = Apc->Thread; PKTHREAD Thread = Apc->Thread;
PKAPC_STATE ApcState;
BOOLEAN Inserted;
ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL); ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
DPRINT("KeRemoveQueueApc called for APC: %x \n", Apc); DPRINT("KeRemoveQueueApc called for APC: %x \n", Apc);
/* Acquire locks */
OldIrql = KeAcquireDispatcherDatabaseLock(); OldIrql = KeAcquireDispatcherDatabaseLock();
KeAcquireSpinLockAtDpcLevel(&Thread->ApcQueueLock); KeAcquireSpinLockAtDpcLevel(&Thread->ApcQueueLock);
/* Check if it's inserted */ /* Check if it's inserted */
if (Apc->Inserted) { if ((Inserted = Apc->Inserted))
{
/* Remove it from the Queue*/ /* Remove it from the Queue*/
RemoveEntryList(&Apc->ApcListEntry);
Apc->Inserted = FALSE; Apc->Inserted = FALSE;
ApcState = Thread->ApcStatePointer[(int)Apc->ApcStateIndex];
RemoveEntryList(&Apc->ApcListEntry);
/* If the Queue is completely empty, then no more APCs are pending */ /* If the Queue is completely empty, then no more APCs are pending */
if (IsListEmpty(&Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->ApcListHead[(int)Apc->ApcMode])) { if (IsListEmpty(&Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->ApcListHead[(int)Apc->ApcMode]))
{
/* Set the correct State based on the Apc Mode */ /* Set the correct State based on the Apc Mode */
if (Apc->ApcMode == KernelMode) { if (Apc->ApcMode == KernelMode)
{
Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->KernelApcPending = FALSE; ApcState->KernelApcPending = FALSE;
}
} else { else
{
Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->UserApcPending = FALSE; ApcState->UserApcPending = FALSE;
} }
} }
} else {
/* It's not inserted, fail */
KeReleaseSpinLockFromDpcLevel(&Thread->ApcQueueLock);
KeReleaseDispatcherDatabaseLock(OldIrql);
return(FALSE);
} }
/* Restore IRQL and Return */ /* Restore IRQL and Return */
KeReleaseSpinLockFromDpcLevel(&Thread->ApcQueueLock); KeReleaseSpinLockFromDpcLevel(&Thread->ApcQueueLock);
KeReleaseDispatcherDatabaseLock(OldIrql); KeReleaseDispatcherDatabaseLock(OldIrql);
return(TRUE); return Inserted;
} }
/*++ /*++
@ -591,6 +666,8 @@ KiDeliverApc(KPROCESSOR_MODE DeliveryMode,
PKTRAP_FRAME TrapFrame) PKTRAP_FRAME TrapFrame)
{ {
PKTHREAD Thread = KeGetCurrentThread(); PKTHREAD Thread = KeGetCurrentThread();
PKPROCESS Process = KeGetCurrentProcess();
PKTRAP_FRAME OldTrapFrame;
PLIST_ENTRY ApcListEntry; PLIST_ENTRY ApcListEntry;
PKAPC Apc; PKAPC Apc;
KIRQL OldIrql; KIRQL OldIrql;
@ -599,17 +676,22 @@ KiDeliverApc(KPROCESSOR_MODE DeliveryMode,
PKNORMAL_ROUTINE NormalRoutine; PKNORMAL_ROUTINE NormalRoutine;
PVOID SystemArgument1; PVOID SystemArgument1;
PVOID SystemArgument2; PVOID SystemArgument2;
ASSERT_IRQL_EQUAL(APC_LEVEL); ASSERT_IRQL_EQUAL(APC_LEVEL);
/* Lock the APC Queue and Raise IRQL to Synch */ /* Save the old trap frame */
KeAcquireSpinLock(&Thread->ApcQueueLock, &OldIrql); OldTrapFrame = Thread->TrapFrame;
/* Clear APC Pending */ /* Clear Kernel APC Pending */
Thread->ApcState.KernelApcPending = FALSE; Thread->ApcState.KernelApcPending = FALSE;
/* Check if Special APCs are disabled */
if (Thread->SpecialApcDisable != 0) goto Quickie;
/* Do the Kernel APCs first */ /* Do the Kernel APCs first */
while (!IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode])) { while (!IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode]))
{
/* Lock the APC Queue and Raise IRQL to Synch */
KeAcquireSpinLock(&Thread->ApcQueueLock, &OldIrql);
/* Get the next Entry */ /* Get the next Entry */
ApcListEntry = Thread->ApcState.ApcListHead[KernelMode].Flink; ApcListEntry = Thread->ApcState.ApcListHead[KernelMode].Flink;
@ -623,8 +705,8 @@ KiDeliverApc(KPROCESSOR_MODE DeliveryMode,
SystemArgument2 = Apc->SystemArgument2; SystemArgument2 = Apc->SystemArgument2;
/* Special APC */ /* Special APC */
if (NormalRoutine == NULL) { if (!NormalRoutine)
{
/* Remove the APC from the list */ /* Remove the APC from the list */
RemoveEntryList(ApcListEntry); RemoveEntryList(ApcListEntry);
Apc->Inserted = FALSE; Apc->Inserted = FALSE;
@ -639,15 +721,13 @@ KiDeliverApc(KPROCESSOR_MODE DeliveryMode,
&NormalContext, &NormalContext,
&SystemArgument1, &SystemArgument1,
&SystemArgument2); &SystemArgument2);
}
/* Raise IRQL and Lock again */ else
KeAcquireSpinLock(&Thread->ApcQueueLock, &OldIrql); {
/* Normal Kernel APC, make sur APCs aren't disabled or in progress*/
} else { if ((Thread->ApcState.KernelApcInProgress) ||
(Thread->KernelApcDisable))
/* Normal Kernel APC */ {
if (Thread->ApcState.KernelApcInProgress || Thread->KernelApcDisable) {
/* /*
* DeliveryMode must be KernelMode in this case, since one may not * DeliveryMode must be KernelMode in this case, since one may not
* return to umode while being inside a critical section or while * return to umode while being inside a critical section or while
@ -656,8 +736,9 @@ KiDeliverApc(KPROCESSOR_MODE DeliveryMode,
*/ */
ASSERT(DeliveryMode == KernelMode); ASSERT(DeliveryMode == KernelMode);
/* Release lock and return */
KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql); KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
return; goto Quickie;
} }
/* Dequeue the APC */ /* Dequeue the APC */
@ -676,8 +757,8 @@ KiDeliverApc(KPROCESSOR_MODE DeliveryMode,
&SystemArgument2); &SystemArgument2);
/* If There still is a Normal Routine, then we need to call this at PASSIVE_LEVEL */ /* If There still is a Normal Routine, then we need to call this at PASSIVE_LEVEL */
if (NormalRoutine != NULL) { if (NormalRoutine)
{
/* At Passive Level, this APC can be prempted by a Special APC */ /* At Passive Level, this APC can be prempted by a Special APC */
Thread->ApcState.KernelApcInProgress = TRUE; Thread->ApcState.KernelApcInProgress = TRUE;
KeLowerIrql(PASSIVE_LEVEL); KeLowerIrql(PASSIVE_LEVEL);
@ -688,21 +769,34 @@ KiDeliverApc(KPROCESSOR_MODE DeliveryMode,
KeRaiseIrql(APC_LEVEL, &OldIrql); KeRaiseIrql(APC_LEVEL, &OldIrql);
} }
/* Raise IRQL and Lock again */ /* Set Kernel APC in progress to false and loop again */
KeAcquireSpinLock(&Thread->ApcQueueLock, &OldIrql);
Thread->ApcState.KernelApcInProgress = FALSE; Thread->ApcState.KernelApcInProgress = FALSE;
} }
} }
/* Now we do the User APCs */ /* Now we do the User APCs */
if ((!IsListEmpty(&Thread->ApcState.ApcListHead[UserMode])) && if ((DeliveryMode == UserMode) &&
(DeliveryMode != KernelMode) && (Thread->ApcState.UserApcPending == TRUE)) { (!IsListEmpty(&Thread->ApcState.ApcListHead[UserMode])) &&
(Thread->ApcState.UserApcPending == TRUE))
{
/* Lock the APC Queue and Raise IRQL to Synch */
KeAcquireSpinLock(&Thread->ApcQueueLock, &OldIrql);
/* It's not pending anymore */ /* It's not pending anymore */
Thread->ApcState.UserApcPending = FALSE; Thread->ApcState.UserApcPending = FALSE;
/* Get the APC Object */ /* Get the APC Entry */
ApcListEntry = Thread->ApcState.ApcListHead[UserMode].Flink; ApcListEntry = Thread->ApcState.ApcListHead[UserMode].Flink;
/* Is it empty now? */
if (!ApcListEntry)
{
/* Release the lock and return */
KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
goto Quickie;
}
/* Get the actual APC object */
Apc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry); Apc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry);
/* Save Parameters so that it's safe to free the Object in Kernel Routine*/ /* Save Parameters so that it's safe to free the Object in Kernel Routine*/
@ -724,13 +818,13 @@ KiDeliverApc(KPROCESSOR_MODE DeliveryMode,
&SystemArgument1, &SystemArgument1,
&SystemArgument2); &SystemArgument2);
if (NormalRoutine == NULL) { if (!NormalRoutine)
{
/* Check if more User APCs are Pending */ /* Check if more User APCs are Pending */
KeTestAlertThread(UserMode); KeTestAlertThread(UserMode);
}
} else { else
{
/* Set up the Trap Frame and prepare for Execution in NTDLL.DLL */ /* Set up the Trap Frame and prepare for Execution in NTDLL.DLL */
DPRINT("Delivering a User APC: %x\n", Apc); DPRINT("Delivering a User APC: %x\n", Apc);
KiInitializeUserApc(Reserved, KiInitializeUserApc(Reserved,
@ -740,12 +834,23 @@ KiDeliverApc(KPROCESSOR_MODE DeliveryMode,
SystemArgument1, SystemArgument1,
SystemArgument2); SystemArgument2);
} }
} else {
/* Go back to APC_LEVEL */
KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
} }
Quickie:
/* Make sure we're still in the same process */
if (Process != Thread->ApcState.Process)
{
/* Erm, we got attached or something! BAD! */
KEBUGCHECKEX(INVALID_PROCESS_ATTACH_ATTEMPT,
(ULONG_PTR)Process,
(ULONG_PTR)Thread->ApcState.Process,
Thread->ApcStateIndex,
KeGetCurrentPrcb()->DpcRoutineActive);
}
/* Restore the trap frame */
Thread->TrapFrame = OldTrapFrame;
return;
} }
VOID VOID
@ -865,7 +970,7 @@ STDCALL
KeAreApcsDisabled(VOID) KeAreApcsDisabled(VOID)
{ {
/* Return the Kernel APC State */ /* Return the Kernel APC State */
return KeGetCurrentThread()->KernelApcDisable ? TRUE : FALSE; return KeGetCurrentThread()->CombinedApcDisable ? TRUE : FALSE;
} }
/*++ /*++

View file

@ -527,8 +527,8 @@ KeResumeThread(PKTHREAD Thread)
return PreviousCount; return PreviousCount;
} }
BOOLEAN VOID
STDCALL FASTCALL
KiInsertQueueApc(PKAPC Apc, KiInsertQueueApc(PKAPC Apc,
KPRIORITY PriorityBoost); KPRIORITY PriorityBoost);
@ -547,20 +547,42 @@ KeFreezeAllThreads(PKPROCESS Process)
/* Acquire Lock */ /* Acquire Lock */
OldIrql = KeAcquireDispatcherDatabaseLock(); OldIrql = KeAcquireDispatcherDatabaseLock();
/* If someone is already trying to free us, try again */
while (CurrentThread->FreezeCount)
{
/* Release and re-acquire the lock so the APC will go through */
KeReleaseDispatcherDatabaseLock(OldIrql);
OldIrql = KeAcquireDispatcherDatabaseLock();
}
/* Enter a critical region */
KeEnterCriticalRegion();
/* Loop the Process's Threads */ /* Loop the Process's Threads */
LIST_FOR_EACH(Current, &Process->ThreadListHead, KTHREAD, ThreadListEntry) LIST_FOR_EACH(Current, &Process->ThreadListHead, KTHREAD, ThreadListEntry)
{ {
/* Make sure it's not ours */ /* Make sure it's not ours */
if (Current == CurrentThread) continue; if (Current != CurrentThread)
/* Make sure it wasn't already frozen, and that it's not suspended */
if (!(++Current->FreezeCount) && !(Current->SuspendCount))
{ {
/* Insert the APC */ /* Should be bother inserting the APC? */
if (!KiInsertQueueApc(&Current->SuspendApc, IO_NO_INCREMENT)) if (Current->ApcQueueable)
{ {
/* Unsignal the Semaphore, the APC already got inserted */ /* Make sure it wasn't already frozen, and that it's not suspended */
Current->SuspendSemaphore.Header.SignalState--; if (!(++Current->FreezeCount) && !(Current->SuspendCount))
{
/* Did we already insert it? */
if (!Current->SuspendApc.Inserted)
{
/* Insert the APC */
Current->SuspendApc.Inserted = TRUE;
KiInsertQueueApc(&Current->SuspendApc, IO_NO_INCREMENT);
}
else
{
/* Unsignal the Semaphore, the APC already got inserted */
Current->SuspendSemaphore.Header.SignalState--;
}
}
} }
} }
} }
@ -589,20 +611,30 @@ KeSuspendThread(PKTHREAD Thread)
{ {
/* Raise an exception */ /* Raise an exception */
KeReleaseDispatcherDatabaseLock(OldIrql); KeReleaseDispatcherDatabaseLock(OldIrql);
ExRaiseStatus(STATUS_SUSPEND_COUNT_EXCEEDED); RtlRaiseStatus(STATUS_SUSPEND_COUNT_EXCEEDED);
} }
/* Increment it */ /* Should we bother to queue at all? */
Thread->SuspendCount++; if (Thread->ApcQueueable)
{
/* Increment the suspend count */
Thread->SuspendCount++;
/* Check if we should suspend it */ /* Check if we should suspend it */
if (!PreviousCount && !Thread->FreezeCount) { if (!PreviousCount && !Thread->FreezeCount)
{
/* Insert the APC */ /* Is the APC already inserted? */
if (!KiInsertQueueApc(&Thread->SuspendApc, IO_NO_INCREMENT)) { if (!Thread->SuspendApc.Inserted)
{
/* Unsignal the Semaphore, the APC already got inserted */ /* Not inserted, insert it */
Thread->SuspendSemaphore.Header.SignalState--; Thread->SuspendApc.Inserted = TRUE;
KiInsertQueueApc(&Thread->SuspendApc, IO_NO_INCREMENT);
}
else
{
/* Unsignal the Semaphore, the APC already got inserted */
Thread->SuspendSemaphore.Header.SignalState--;
}
} }
} }