Author: ion
Date: Mon Mar 19 20:55:38 2007
New Revision: 26139
URL:
http://svn.reactos.org/svn/reactos?rev=26139&view=rev
Log:
- Change FASTCALL_PROLOG to use the stack to update FS, since we run in the DPC stack.
- Implement KeDisableInterrupts to disable interrupts and return whether or not they were
enabled.
- Implement KiCheckTimerTable, in DBG mode, to validate the timer tables.
- Implement DPC Timeout detection, in DBG mode.
- Fix a bug in KiQuantumEnd which would've affected real-time threads.
- Fix some bugs in KiRetireDpcList to avoid issues should the DPC Queue Depth drop below
0, and solve some possible races.
- Fix KeRemoveQueueDpc only to enable interrupts if it was called with interrupts
enabled.
Modified:
trunk/reactos/ntoskrnl/include/internal/i386/asmmacro.S
trunk/reactos/ntoskrnl/include/internal/ke.h
trunk/reactos/ntoskrnl/ke/dpc.c
trunk/reactos/ntoskrnl/ke/i386/irqobj.c
trunk/reactos/ntoskrnl/ke/i386/systimer.S
Modified: trunk/reactos/ntoskrnl/include/internal/i386/asmmacro.S
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/include/internal/…
==============================================================================
--- trunk/reactos/ntoskrnl/include/internal/i386/asmmacro.S (original)
+++ trunk/reactos/ntoskrnl/include/internal/i386/asmmacro.S Mon Mar 19 20:55:38 2007
@@ -700,10 +700,8 @@
//
.macro FASTCALL_PROLOG Label EndLabel
/* Set FS to PCR */
- mov ecx, KGDT_R0_PCR
- mov fs, cx
- //push KGDT_R0_PCR
- //pop fs
+ push KGDT_R0_PCR
+ pop fs
/* Set user selector */
mov ecx, KGDT_R3_DATA | RPL_MASK
Modified: trunk/reactos/ntoskrnl/include/internal/ke.h
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/include/internal/…
==============================================================================
--- trunk/reactos/ntoskrnl/include/internal/ke.h (original)
+++ trunk/reactos/ntoskrnl/include/internal/ke.h Mon Mar 19 20:55:38 2007
@@ -922,6 +922,12 @@
NTAPI
KeThawExecution(IN BOOLEAN Enable);
+BOOLEAN
+NTAPI
+KeDisableInterrupts(
+ VOID
+);
+
#include "ke_x.h"
#endif /* __NTOSKRNL_INCLUDE_INTERNAL_KE_H */
Modified: trunk/reactos/ntoskrnl/ke/dpc.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/ke/dpc.c?rev=2613…
==============================================================================
--- trunk/reactos/ntoskrnl/ke/dpc.c (original)
+++ trunk/reactos/ntoskrnl/ke/dpc.c Mon Mar 19 20:55:38 2007
@@ -24,8 +24,55 @@
FAST_MUTEX KiGenericCallDpcMutex;
KDPC KiTimerExpireDpc;
ULONG KiTimeLimitIsrMicroseconds;
+ULONG KiDPCTimeout = 110;
/* PRIVATE FUNCTIONS *********************************************************/
+
+VOID
+NTAPI
+KiCheckTimerTable(IN ULARGE_INTEGER CurrentTime)
+{
+#if DBG
+ ULONG i = 0;
+ PLIST_ENTRY ListHead, NextEntry;
+ KIRQL OldIrql;
+ PKTIMER Timer;
+
+ /* Raise IRQL to high and loop timers */
+ KeRaiseIrql(HIGH_LEVEL, &OldIrql);
+ do
+ {
+ /* Loop the current list */
+ ListHead = &KiTimerTableListHead[i].Entry;
+ NextEntry = ListHead->Flink;
+ while (NextEntry != ListHead)
+ {
+ /* Get the timer and move to the next one */
+ Timer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry);
+ NextEntry = NextEntry->Flink;
+
+ /* Check if it expired */
+ if (Timer->DueTime.QuadPart <= CurrentTime.QuadPart)
+ {
+ /* Check if the DPC was queued, but didn't run */
+ if (!(KeGetCurrentPrcb()->TimerRequest) &&
+ !(*((volatile PULONG*)(&KiTimerExpireDpc.DpcData))))
+ {
+ /* This is bad, breakpoint! */
+ DPRINT1("Invalid timer state!\n");
+ DbgBreakPoint();
+ }
+ }
+ }
+
+ /* Move to the next timer */
+ i++;
+ } while(i < TIMER_TABLE_SIZE);
+
+ /* Lower IRQL and return */
+ KeLowerIrql(OldIrql);
+#endif
+}
VOID
NTAPI
@@ -34,7 +81,8 @@
IN PVOID SystemArgument1,
IN PVOID SystemArgument2)
{
- LARGE_INTEGER SystemTime, InterruptTime, Interval;
+ ULARGE_INTEGER SystemTime, InterruptTime;
+ LARGE_INTEGER Interval;
LONG Limit, Index, i;
ULONG Timers, ActiveTimers, DpcCalls;
PLIST_ENTRY ListHead, NextEntry;
@@ -50,7 +98,7 @@
_disable();
/* Query system and interrupt time */
- KeQuerySystemTime(&SystemTime);
+ KeQuerySystemTime((PLARGE_INTEGER)&SystemTime);
InterruptTime.QuadPart = KeQueryInterruptTime();
Limit = KeTickCount.LowPart;
@@ -226,6 +274,9 @@
}
} while (Index != Limit);
+ /* Verify the timer table, on debug builds */
+ if (KeNumberProcessors == 1) KiCheckTimerTable(InterruptTime);
+
/* Check if we still have DPC entries */
if (DpcCalls)
{
@@ -274,9 +325,14 @@
/* Check if Quantum expired */
if (Thread->Quantum <= 0)
{
- /* Make sure that we're not real-time or without a quantum */
- if ((Thread->Priority < LOW_REALTIME_PRIORITY) &&
- !(Thread->ApcState.Process->DisableQuantum))
+ /* Check if we're real-time and with quantums disabled */
+ if ((Thread->Priority >= LOW_REALTIME_PRIORITY) &&
+ (Thread->ApcState.Process->DisableQuantum))
+ {
+ /* Otherwise, set maximum quantum */
+ Thread->Quantum = MAX_QUANTUM;
+ }
+ else
{
/* Reset the new Quantum */
Thread->Quantum = Thread->QuantumReset;
@@ -287,7 +343,6 @@
/* Check if a new thread is scheduled */
if (!Prcb->NextThread)
{
-#ifdef NEW_SCHEDULER
/* Get a new ready thread */
NextThread = KiSelectReadyThread(Thread->Priority, Prcb);
if (NextThread)
@@ -296,13 +351,6 @@
NextThread->State = Standby;
Prcb->NextThread = NextThread;
}
-#else
- /* Just leave now */
- KiReleasePrcbLock(Prcb);
- KeLowerIrql(DISPATCH_LEVEL);
- KiDispatchThread(Ready);
- return;
-#endif
}
else
{
@@ -310,11 +358,6 @@
Thread->Preempted = FALSE;
}
}
- else
- {
- /* Otherwise, set maximum quantum */
- Thread->Quantum = MAX_QUANTUM;
- }
}
/* Release the thread lock */
@@ -360,13 +403,17 @@
FASTCALL
KiRetireDpcList(IN PKPRCB Prcb)
{
- PKDPC_DATA DpcData = Prcb->DpcData;
- PLIST_ENTRY DpcEntry;
+ PKDPC_DATA DpcData;
+ PLIST_ENTRY ListHead, DpcEntry;
PKDPC Dpc;
PKDEFERRED_ROUTINE DeferredRoutine;
PVOID DeferredContext, SystemArgument1, SystemArgument2;
ULONG_PTR TimerHand;
+ /* Get data and list variables before starting anything else */
+ DpcData = &Prcb->DpcData[DPC_NORMAL];
+ ListHead = &DpcData->DpcListHead;
+
/* Main outer loop */
do
{
@@ -376,24 +423,28 @@
/* Check if this is a timer expiration request */
if (Prcb->TimerRequest)
{
+ /* It is, get the timer hand and disable timer request */
TimerHand = Prcb->TimerHand;
Prcb->TimerRequest = 0;
+
+ /* Expire timers with interrups enabled */
_enable();
- KiTimerExpiration(NULL, NULL, (PVOID) TimerHand, NULL);
+ KiTimerExpiration(NULL, NULL, (PVOID)TimerHand, NULL);
_disable();
}
/* Loop while we have entries in the queue */
- while (DpcData->DpcQueueDepth)
- {
- /* Lock the DPC data */
+ while (DpcData->DpcQueueDepth != 0)
+ {
+ /* Lock the DPC data and get the DPC entry*/
KefAcquireSpinLockAtDpcLevel(&DpcData->DpcLock);
+ DpcEntry = ListHead->Flink;
/* Make sure we have an entry */
- if (!IsListEmpty(&DpcData->DpcListHead))
+ if (DpcEntry != ListHead)
{
/* Remove the DPC from the list */
- DpcEntry = RemoveHeadList(&DpcData->DpcListHead);
+ RemoveEntryList(DpcEntry);
Dpc = CONTAINING_RECORD(DpcEntry, KDPC, DpcListEntry);
/* Clear its DPC data and save its parameters */
@@ -432,7 +483,6 @@
/* Release DPC Lock */
KefReleaseSpinLockFromDpcLevel(&DpcData->DpcLock);
- break;
}
}
@@ -446,7 +496,7 @@
/* FIXME: 2K3-style scheduling not implemeted */
ASSERT(FALSE);
}
- } while (DpcData->DpcQueueDepth);
+ } while (DpcData->DpcQueueDepth != 0);
}
VOID
@@ -458,7 +508,7 @@
{
/* Setup the DPC Object */
Dpc->Type = Type;
- Dpc->Number= 0;
+ Dpc->Number = 0;
Dpc->Importance= MediumImportance;
Dpc->DeferredRoutine = DeferredRoutine;
Dpc->DeferredContext = DeferredContext;
@@ -503,7 +553,7 @@
IN PVOID SystemArgument2)
{
KIRQL OldIrql;
- PKPRCB Prcb, CurrentPrcb = KeGetCurrentPrcb();
+ PKPRCB Prcb, CurrentPrcb;
ULONG Cpu;
PKDPC_DATA DpcData;
BOOLEAN DpcConfigured = FALSE, DpcInserted = FALSE;
@@ -511,6 +561,7 @@
/* Check IRQL and Raise it to HIGH_LEVEL */
KeRaiseIrql(HIGH_LEVEL, &OldIrql);
+ CurrentPrcb = KeGetCurrentPrcb();
/* Check if the DPC has more then the maximum number of CPUs */
if (Dpc->Number >= MAXIMUM_PROCESSORS)
@@ -525,6 +576,9 @@
Prcb = CurrentPrcb;
Cpu = Prcb->Number;
}
+
+ /* ROS Sanity Check */
+ ASSERT(Prcb == CurrentPrcb);
/* Check if this is a threaded DPC and threaded DPCs are enabled */
if ((Dpc->Type == ThreadedDpcObject) && (Prcb->ThreadDpcEnable))
@@ -564,13 +618,14 @@
}
/* Check if this is the DPC on the threaded list */
- if (&Prcb->DpcData[DPC_THREADED].DpcListHead ==
&DpcData->DpcListHead)
+ if (&Prcb->DpcData[DPC_THREADED] == DpcData)
{
/* Make sure a threaded DPC isn't already active */
if (!(Prcb->DpcThreadActive) && !(Prcb->DpcThreadRequested))
{
/* FIXME: Setup Threaded DPC */
- ASSERT(FALSE);
+ DPRINT1("Threaded DPC not supported\n");
+ while (TRUE);
}
}
else
@@ -650,14 +705,13 @@
KeRemoveQueueDpc(IN PKDPC Dpc)
{
PKDPC_DATA DpcData;
- UCHAR DpcType;
+ BOOLEAN Enable;
ASSERT_DPC(Dpc);
/* Disable interrupts */
- _disable();
-
- /* Get DPC data and type */
- DpcType = Dpc->Type;
+ Enable = KeDisableInterrupts();
+
+ /* Get DPC data */
DpcData = Dpc->DpcData;
if (DpcData)
{
@@ -678,7 +732,7 @@
}
/* Re-enable interrupts */
- _enable();
+ if (Enable) _enable();
/* Return if the DPC was in the queue or not */
return DpcData ? TRUE : FALSE;
@@ -691,14 +745,15 @@
NTAPI
KeFlushQueuedDpcs(VOID)
{
+ PKPRCB CurrentPrcb = KeGetCurrentPrcb();
PAGED_CODE();
/* Check if this is an UP machine */
if (KeActiveProcessors == 1)
{
/* Check if there are DPCs on either queues */
- if ((KeGetCurrentPrcb()->DpcData[DPC_NORMAL].DpcQueueDepth) ||
- (KeGetCurrentPrcb()->DpcData[DPC_THREADED].DpcQueueDepth))
+ if ((CurrentPrcb->DpcData[DPC_NORMAL].DpcQueueDepth > 0) ||
+ (CurrentPrcb->DpcData[DPC_THREADED].DpcQueueDepth > 0))
{
/* Request an interrupt */
HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
Modified: trunk/reactos/ntoskrnl/ke/i386/irqobj.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/ke/i386/irqobj.c?…
==============================================================================
--- trunk/reactos/ntoskrnl/ke/i386/irqobj.c (original)
+++ trunk/reactos/ntoskrnl/ke/i386/irqobj.c Mon Mar 19 20:55:38 2007
@@ -22,6 +22,22 @@
extern ULONG KiChainedDispatch2ndLvl;
/* PRIVATE FUNCTIONS *********************************************************/
+
+BOOLEAN
+NTAPI
+KeDisableInterrupts(VOID)
+{
+ ULONG Flags = 0;
+ BOOLEAN Return;
+
+ /* Get EFLAGS and check if the interrupt bit is set */
+ Ke386SaveFlags(Flags);
+ Return = (Flags & EFLAGS_INTERRUPT_MASK) ? TRUE: FALSE;
+
+ /* Disable interrupts */
+ _disable();
+ return Return;
+}
VOID
NTAPI
Modified: trunk/reactos/ntoskrnl/ke/i386/systimer.S
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/ke/i386/systimer.…
==============================================================================
--- trunk/reactos/ntoskrnl/ke/i386/systimer.S (original)
+++ trunk/reactos/ntoskrnl/ke/i386/systimer.S Mon Mar 19 20:55:38 2007
@@ -10,6 +10,11 @@
#include <asm.h>
#include <internal/i386/asmmacro.S>
.intel_syntax noprefix
+
+/* GLOBALS *******************************************************************/
+
+_DpcTimeoutMsg:
+ .asciz "\n*** DPC routine > 1 sec --- This is not a break in
KeUpdateSystemTime\n"
/* FUNCTIONS ******************************************************************/
@@ -93,7 +98,32 @@
/* At dispatch, increase DPC time */
inc dword ptr [eax+KPCR_PRCB_DPC_TIME]
+#ifdef DBG
+ /* Update the DPC time */
inc dword ptr [eax+KPCR_PRCB_DEBUG_DPC_TIME]
+
+ /* Check if we've timed out */
+ mov edx, _KiDPCTimeout
+ cmp dword ptr [eax+KPCR_PRCB_DEBUG_DPC_TIME], edx
+ jc AfterSet
+
+ /* We did, print out a message */
+ push offset _DpcTimeoutMsg
+ call _DbgPrint
+ add esp, 4
+
+ /* Check if the debugger is enabled */
+ cmp byte ptr __KdDebuggerEnabled, 0
+ je ResetDpcTime
+
+ /* Breakpoint */
+ call _DbgBreakPoint@0
+
+ResetDpcTime:
+ /* Restore state */
+ mov eax, PCR[KPCR_SELF]
+ mov dword ptr [eax+KPCR_PRCB_DEBUG_DPC_TIME], 0
+#endif
jmp AfterSet
AboveDispatch: