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/i... ============================================================================== --- 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/k... ============================================================================== --- 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=26139... ============================================================================== --- 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?r... ============================================================================== --- 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.S... ============================================================================== --- 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: