- Fix some bugs in Kernel Queue implementation:

* KeRemoveQueue did not set Thread->WaitNext = FALSE if it was called with WaitNext = TRUE.
  * KeRemoveQueue did not handle the case where a kernel-mode APC is pending and the previous IRQL was below APC_LEVEL.
  * KeRemoveQueue did not set the thread's wait status to 0.
  * KiInsertQueue did not set the Thread's wait status to the entry being inserted.
  * KiInsertQueue did not remove the thread from its wait list.
  * KeRemoveQueue did not properly link the wait blocks.

svn path=/trunk/; revision=20601
This commit is contained in:
Alex Ionescu 2006-01-06 02:25:12 +00:00
parent a7c21d9dbf
commit c1b51155f7

View file

@ -117,25 +117,23 @@ KeRemoveQueue(IN PKQUEUE Queue,
IN KPROCESSOR_MODE WaitMode, IN KPROCESSOR_MODE WaitMode,
IN PLARGE_INTEGER Timeout OPTIONAL) IN PLARGE_INTEGER Timeout OPTIONAL)
{ {
PLIST_ENTRY QueueEntry;
PLIST_ENTRY ListEntry;
NTSTATUS Status; NTSTATUS Status;
PKTHREAD Thread = KeGetCurrentThread(); PKTHREAD Thread = KeGetCurrentThread();
KIRQL OldIrql; KIRQL OldIrql;
PKQUEUE PreviousQueue; PKQUEUE PreviousQueue;
PKWAIT_BLOCK WaitBlock; PKWAIT_BLOCK WaitBlock;
PKWAIT_BLOCK TimerWaitBlock;
PKTIMER Timer; PKTIMER Timer;
DPRINT("KeRemoveQueue %x\n", Queue); DPRINT("KeRemoveQueue %x\n", Queue);
/* Check if the Lock is already held */ /* Check if the Lock is already held */
if (Thread->WaitNext) { if (Thread->WaitNext)
{
DPRINT("Lock is already held\n"); DPRINT("Lock is already held\n");
Thread->WaitNext = FALSE;
} else { }
else
{
/* Lock the Dispatcher Database */ /* Lock the Dispatcher Database */
DPRINT("Lock not held, acquiring\n"); DPRINT("Lock not held, acquiring\n");
OldIrql = KeAcquireDispatcherDatabaseLock(); OldIrql = KeAcquireDispatcherDatabaseLock();
@ -147,49 +145,43 @@ KeRemoveQueue(IN PKQUEUE Queue,
Thread->Queue = Queue; Thread->Queue = Queue;
/* Check if this is a different queue */ /* Check if this is a different queue */
if (Queue != PreviousQueue) { if (Queue != PreviousQueue)
{
/*
* INVESTIGATE: What is the Thread->QueueListEntry used for? It's linked it into the
* Queue->ThreadListHead when the thread registers with the queue and unlinked when
* the thread registers with a new queue. The Thread->Queue already tells us what
* queue the thread is registered with.
* -Gunnar
*/
DPRINT("Different Queue\n"); DPRINT("Different Queue\n");
if (PreviousQueue) { QueueEntry = &Thread->QueueListEntry;
if (PreviousQueue)
{
/* Remove from this list */ /* Remove from this list */
DPRINT("Removing Old Queue\n"); DPRINT("Removing Old Queue\n");
RemoveEntryList(&Thread->QueueListEntry); RemoveEntryList(QueueEntry);
/* Wake the queue */ /* Wake the queue */
DPRINT("Activating new thread\n"); DPRINT("Activating new thread\n");
KiWakeQueue(PreviousQueue); KiWakeQueue(PreviousQueue);
} }
/* Insert in this new Queue */ /* Insert in this new Queue */
DPRINT("Inserting new Queue!\n"); DPRINT("Inserting new Queue!\n");
InsertTailList(&Queue->ThreadListHead, &Thread->QueueListEntry); InsertTailList(&Queue->ThreadListHead, QueueEntry);
}
} else { else
{
/* Same queue, decrement waiting threads */ /* Same queue, decrement waiting threads */
DPRINT("Same Queue!\n"); DPRINT("Same Queue!\n");
Queue->CurrentCount--; Queue->CurrentCount--;
} }
/* Loop until the queue is processed */ /* Loop until the queue is processed */
while (TRUE) { while (TRUE)
{
/* Check if the counts are valid and if there is still a queued entry */ /* Check if the counts are valid and if there is still a queued entry */
QueueEntry = Queue->EntryListHead.Flink;
if ((Queue->CurrentCount < Queue->MaximumCount) && if ((Queue->CurrentCount < Queue->MaximumCount) &&
!IsListEmpty(&Queue->EntryListHead)) { (QueueEntry != &Queue->EntryListHead))
{
/* Remove the Entry and Save it */ /* Remove the Entry and Save it */
DPRINT("Removing Queue Entry. CurrentCount: %d, Maximum Count: %d\n", DPRINT("Removing Queue Entry. CurrentCount: %d, Maximum Count: %d\n",
Queue->CurrentCount, Queue->MaximumCount); Queue->CurrentCount, Queue->MaximumCount);
ListEntry = Queue->EntryListHead.Flink;
/* Decrease the number of entries */ /* Decrease the number of entries */
Queue->Header.SignalState--; Queue->Header.SignalState--;
@ -198,20 +190,20 @@ KeRemoveQueue(IN PKQUEUE Queue,
Queue->CurrentCount++; Queue->CurrentCount++;
/* Check if the entry is valid. If not, bugcheck */ /* Check if the entry is valid. If not, bugcheck */
if (!ListEntry->Flink || !ListEntry->Blink) { if (!(QueueEntry->Flink) || !(QueueEntry->Blink))
{
KEBUGCHECK(INVALID_WORK_QUEUE_ITEM); KEBUGCHECK(INVALID_WORK_QUEUE_ITEM);
} }
/* Remove the Entry */ /* Remove the Entry */
RemoveEntryList(ListEntry); RemoveEntryList(QueueEntry);
ListEntry->Flink = NULL; QueueEntry->Flink = NULL;
/* Nothing to wait on */ /* Nothing to wait on */
break; break;
}
} else { else
{
/* Do the wait */ /* Do the wait */
DPRINT("Waiting on Queue Entry. CurrentCount: %d, Maximum Count: %d\n", DPRINT("Waiting on Queue Entry. CurrentCount: %d, Maximum Count: %d\n",
Queue->CurrentCount, Queue->MaximumCount); Queue->CurrentCount, Queue->MaximumCount);
@ -219,11 +211,21 @@ KeRemoveQueue(IN PKQUEUE Queue,
/* Use the Thread's Wait Block, it's big enough */ /* Use the Thread's Wait Block, it's big enough */
Thread->WaitBlockList = &Thread->WaitBlock[0]; Thread->WaitBlockList = &Thread->WaitBlock[0];
/* Fail if there's an APC Pending */ /* Check if a kernel APC is pending and we were below APC_LEVEL */
if (WaitMode != KernelMode && Thread->ApcState.UserApcPending) { if ((Thread->ApcState.KernelApcPending) &&
(Thread->WaitIrql < APC_LEVEL))
{
/* Increment the count and unlock the dispatcher */
Queue->CurrentCount++;
KeReleaseDispatcherDatabaseLock(Thread->WaitIrql);
goto SkipWait;
}
/* Fail if there's a User APC Pending */
if ((WaitMode != KernelMode) && (Thread->ApcState.UserApcPending))
{
/* Return the status and increase the pending threads */ /* Return the status and increase the pending threads */
ListEntry = (PLIST_ENTRY)STATUS_USER_APC; QueueEntry = (PLIST_ENTRY)STATUS_USER_APC;
Queue->CurrentCount++; Queue->CurrentCount++;
/* Nothing to wait on */ /* Nothing to wait on */
@ -236,19 +238,17 @@ KeRemoveQueue(IN PKQUEUE Queue,
WaitBlock->WaitKey = STATUS_SUCCESS; WaitBlock->WaitKey = STATUS_SUCCESS;
WaitBlock->WaitType = WaitAny; WaitBlock->WaitType = WaitAny;
WaitBlock->Thread = Thread; WaitBlock->Thread = Thread;
WaitBlock->NextWaitBlock = WaitBlock; Thread->WaitStatus = STATUS_WAIT_0;
Thread->WaitStatus = STATUS_SUCCESS;
/* We need to wait for the object... check if we have a timeout */ /* We need to wait for the object... check if we have a timeout */
if (Timeout) { if (Timeout)
{
/* If it's zero, then don't do any waiting */ /* If it's zero, then don't do any waiting */
if (!Timeout->QuadPart) { if (!Timeout->QuadPart)
{
/* Instant Timeout, return the status and increase the pending threads */ /* Instant Timeout, return the status and increase the pending threads */
DPRINT("Queue Wait has timed out\n"); DPRINT("Queue Wait has timed out\n");
ListEntry = (PLIST_ENTRY)STATUS_TIMEOUT; QueueEntry = (PLIST_ENTRY)STATUS_TIMEOUT;
Queue->CurrentCount++; Queue->CurrentCount++;
/* Nothing to wait on */ /* Nothing to wait on */
@ -260,27 +260,31 @@ KeRemoveQueue(IN PKQUEUE Queue,
* hold on to the dispatcher lock. * hold on to the dispatcher lock.
*/ */
Timer = &Thread->Timer; Timer = &Thread->Timer;
TimerWaitBlock = &Thread->WaitBlock[1]; WaitBlock->NextWaitBlock = &Thread->WaitBlock[1];
WaitBlock = &Thread->WaitBlock[1];
/* Set up the Timer Wait Block */ /* Set up the Timer Wait Block */
TimerWaitBlock->Object = (PVOID)Timer; WaitBlock->Object = (PVOID)Timer;
TimerWaitBlock->Thread = Thread; WaitBlock->Thread = Thread;
TimerWaitBlock->WaitKey = STATUS_TIMEOUT; WaitBlock->WaitKey = STATUS_TIMEOUT;
TimerWaitBlock->WaitType = WaitAny; WaitBlock->WaitType = WaitAny;
TimerWaitBlock->NextWaitBlock = TimerWaitBlock;
/* Link the timer to this Wait Block */ /* Link the timer to this Wait Block */
InitializeListHead(&Timer->Header.WaitListHead); InitializeListHead(&Timer->Header.WaitListHead);
InsertTailList(&Timer->Header.WaitListHead, &TimerWaitBlock->WaitListEntry); InsertTailList(&Timer->Header.WaitListHead, &WaitBlock->WaitListEntry);
/* Create Timer */ /* Create Timer */
DPRINT("Creating Timer with timeout %I64d\n", *Timeout); DPRINT("Creating Timer with timeout %I64d\n", *Timeout);
KiInsertTimer(Timer, *Timeout); KiInsertTimer(Timer, *Timeout);
} }
/* Close the loop */
WaitBlock->NextWaitBlock = &Thread->WaitBlock[0];
/* Insert the wait block into the Queues's wait list */ /* Insert the wait block into the Queues's wait list */
WaitBlock = Thread->WaitBlockList; WaitBlock = &Thread->WaitBlock[0];
InsertTailList(&Queue->Header.WaitListHead, &WaitBlock->WaitListEntry); InsertTailList(&Queue->Header.WaitListHead,
&WaitBlock->WaitListEntry);
/* Block the Thread */ /* Block the Thread */
DPRINT("Blocking the Thread: %x %x!\n", KeGetCurrentThread(), Thread); DPRINT("Blocking the Thread: %x %x!\n", KeGetCurrentThread(), Thread);
@ -293,14 +297,21 @@ KeRemoveQueue(IN PKQUEUE Queue,
Thread->WaitReason = 0; Thread->WaitReason = 0;
/* Check if we were executing an APC */ /* Check if we were executing an APC */
if (Status != STATUS_KERNEL_APC) { if (Status != STATUS_KERNEL_APC)
{
/* Done Waiting */ /* Done Waiting */
DPRINT("Done waking queue. Thread: %x %x!\n", KeGetCurrentThread(), Thread); DPRINT("Done waking queue. Thread: %x %x!\n", KeGetCurrentThread(), Thread);
return (PLIST_ENTRY)Status; return (PLIST_ENTRY)Status;
} }
/* Check if we had a timeout */
if (Timeout)
{
/* FIXME: Fixup interval */
}
/* Acquire again the lock */ /* Acquire again the lock */
SkipWait:
DPRINT("Looping again\n"); DPRINT("Looping again\n");
OldIrql = KeAcquireDispatcherDatabaseLock(); OldIrql = KeAcquireDispatcherDatabaseLock();
@ -314,7 +325,7 @@ KeRemoveQueue(IN PKQUEUE Queue,
KeReleaseDispatcherDatabaseLock(Thread->WaitIrql); KeReleaseDispatcherDatabaseLock(Thread->WaitIrql);
DPRINT("Returning. CurrentCount: %d, Maximum Count: %d\n", DPRINT("Returning. CurrentCount: %d, Maximum Count: %d\n",
Queue->CurrentCount, Queue->MaximumCount); Queue->CurrentCount, Queue->MaximumCount);
return ListEntry; return QueueEntry;
} }
/* /*
@ -335,15 +346,15 @@ KeRundownQueue(IN PKQUEUE Queue)
OldIrql = KeAcquireDispatcherDatabaseLock(); OldIrql = KeAcquireDispatcherDatabaseLock();
/* Make sure the list is not empty */ /* Make sure the list is not empty */
if (!IsListEmpty(&Queue->EntryListHead)) if (!IsListEmpty(&Queue->EntryListHead))
{ {
/* Remove it */ /* Remove it */
FirstEntry = RemoveHeadList(&Queue->EntryListHead); FirstEntry = RemoveHeadList(&Queue->EntryListHead);
} }
/* Unlink threads and clear their Thread->Queue */ /* Unlink threads and clear their Thread->Queue */
while (!IsListEmpty(&Queue->ThreadListHead)) { while (!IsListEmpty(&Queue->ThreadListHead))
{
/* Get the Entry and Remove it */ /* Get the Entry and Remove it */
EnumEntry = RemoveHeadList(&Queue->ThreadListHead); EnumEntry = RemoveHeadList(&Queue->ThreadListHead);
@ -369,25 +380,26 @@ KiWakeQueue(IN PKQUEUE Queue)
PLIST_ENTRY QueueEntry; PLIST_ENTRY QueueEntry;
PLIST_ENTRY WaitEntry; PLIST_ENTRY WaitEntry;
PKWAIT_BLOCK WaitBlock; PKWAIT_BLOCK WaitBlock;
PKTHREAD Thread;
/* Decrement the number of active threads */ /* Decrement the number of active threads */
DPRINT("KiWakeQueue: %x. Thread: %x\n", Queue, KeGetCurrentThread()); DPRINT("KiWakeQueue: %x. Thread: %x\n", Queue, KeGetCurrentThread());
Queue->CurrentCount--; Queue->CurrentCount--;
/* Make sure the counts are OK */ /* Make sure the counts are OK */
if (Queue->CurrentCount < Queue->MaximumCount) { if (Queue->CurrentCount < Queue->MaximumCount)
{
/* Get the Queue Entry */ /* Get the Queue Entry */
QueueEntry = Queue->EntryListHead.Flink; QueueEntry = Queue->EntryListHead.Flink;
/* Get the Wait Entry */ /* Get the Wait Entry */
WaitEntry = Queue->Header.WaitListHead.Blink; WaitEntry = Queue->Header.WaitListHead.Blink;
DPRINT("Queue Count is ok, Queue entries: %x, %x\n", QueueEntry, WaitEntry); DPRINT("Queue Count is ok; entries: %p, %p\n", QueueEntry, WaitEntry);
/* Make sure that the Queue List isn't empty and that this entry is valid */
if (!IsListEmpty(&Queue->Header.WaitListHead) &&
(QueueEntry != &Queue->EntryListHead)) {
/* Make sure that the Queue entries are not part of empty lists */
if ((WaitEntry != &Queue->Header.WaitListHead) &&
(QueueEntry != &Queue->EntryListHead))
{
/* Remove this entry */ /* Remove this entry */
DPRINT("Queue in List, removing it\n"); DPRINT("Queue in List, removing it\n");
RemoveEntryList(QueueEntry); RemoveEntryList(QueueEntry);
@ -397,9 +409,12 @@ KiWakeQueue(IN PKQUEUE Queue)
Queue->Header.SignalState--; Queue->Header.SignalState--;
/* Unwait the Thread */ /* Unwait the Thread */
DPRINT("Unwaiting Thread\n"); WaitBlock = CONTAINING_RECORD(WaitEntry,
WaitBlock = CONTAINING_RECORD(WaitEntry, KWAIT_BLOCK, WaitListEntry); KWAIT_BLOCK,
KiAbortWaitThread(WaitBlock->Thread, (NTSTATUS)QueueEntry, IO_NO_INCREMENT); WaitListEntry);
Thread = WaitBlock->Thread;
DPRINT1("Unwaiting Thread: %d\n", Thread->State);
KiAbortWaitThread(Thread, (NTSTATUS)QueueEntry, IO_NO_INCREMENT);
} }
} }
} }
@ -417,7 +432,6 @@ KiInsertQueue(IN PKQUEUE Queue,
PKTHREAD Thread = KeGetCurrentThread(); PKTHREAD Thread = KeGetCurrentThread();
PKWAIT_BLOCK WaitBlock; PKWAIT_BLOCK WaitBlock;
PLIST_ENTRY WaitEntry; PLIST_ENTRY WaitEntry;
DPRINT("KiInsertQueue(Queue %x, Entry %x)\n", Queue, Entry); DPRINT("KiInsertQueue(Queue %x, Entry %x)\n", Queue, Entry);
/* Save the old state */ /* Save the old state */
@ -434,8 +448,8 @@ KiInsertQueue(IN PKQUEUE Queue,
*/ */
if ((Queue->CurrentCount < Queue->MaximumCount) && if ((Queue->CurrentCount < Queue->MaximumCount) &&
(WaitEntry != &Queue->Header.WaitListHead) && (WaitEntry != &Queue->Header.WaitListHead) &&
((Thread->Queue != Queue) || (Thread->WaitReason != WrQueue))) { ((Thread->Queue != Queue) || (Thread->WaitReason != WrQueue)))
{
/* Remove the wait entry */ /* Remove the wait entry */
DPRINT("Removing Entry\n"); DPRINT("Removing Entry\n");
RemoveEntryList(WaitEntry); RemoveEntryList(WaitEntry);
@ -448,12 +462,16 @@ KiInsertQueue(IN PKQUEUE Queue,
/* Reset the wait reason */ /* Reset the wait reason */
Thread->WaitReason = 0; Thread->WaitReason = 0;
/* Increase the waiting threads */ /* Increase the active threads and set the status*/
Queue->CurrentCount++; Queue->CurrentCount++;
Thread->WaitStatus = (NTSTATUS)Entry;
/* Remove the thread from its wait list */
RemoveEntryList(&Thread->WaitListEntry);
/* Check if there's a Thread Timer */ /* Check if there's a Thread Timer */
if (Thread->Timer.Header.Inserted) { if (Thread->Timer.Header.Inserted)
{
/* Cancel the Thread Timer with the no-lock fastpath */ /* Cancel the Thread Timer with the no-lock fastpath */
DPRINT("Removing the Thread's Timer\n"); DPRINT("Removing the Thread's Timer\n");
Thread->Timer.Header.Inserted = FALSE; Thread->Timer.Header.Inserted = FALSE;
@ -463,19 +481,22 @@ KiInsertQueue(IN PKQUEUE Queue,
/* Reschedule the Thread */ /* Reschedule the Thread */
DPRINT("Unblocking the Thread\n"); DPRINT("Unblocking the Thread\n");
KiUnblockThread(Thread, (PNTSTATUS)&Entry, 0); KiUnblockThread(Thread, (PNTSTATUS)&Entry, 0);
}
} else { else
{
/* Increase the Entries */ /* Increase the Entries */
DPRINT("Adding new Queue Entry: %d %d\n", Head, Queue->Header.SignalState); DPRINT("Adding new Queue Entry: %d %d\n", Head, Queue->Header.SignalState);
Queue->Header.SignalState++; Queue->Header.SignalState++;
if (Head) { /* Check which mode we're using */
if (Head)
{
/* Insert in the head */
InsertHeadList(&Queue->EntryListHead, Entry); InsertHeadList(&Queue->EntryListHead, Entry);
}
} else { else
{
/* Insert at the end */
InsertTailList(&Queue->EntryListHead, Entry); InsertTailList(&Queue->EntryListHead, Entry);
} }
} }