mirror of
https://github.com/reactos/reactos.git
synced 2024-11-20 06:15:26 +00:00
- 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. svn path=/trunk/; revision=26139
This commit is contained in:
parent
755994a386
commit
1d2fd8ac1f
5 changed files with 147 additions and 42 deletions
|
@ -700,10 +700,8 @@ Dr_&EndLabel:
|
|||
//
|
||||
.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
|
||||
|
|
|
@ -922,6 +922,12 @@ VOID
|
|||
NTAPI
|
||||
KeThawExecution(IN BOOLEAN Enable);
|
||||
|
||||
BOOLEAN
|
||||
NTAPI
|
||||
KeDisableInterrupts(
|
||||
VOID
|
||||
);
|
||||
|
||||
#include "ke_x.h"
|
||||
|
||||
#endif /* __NTOSKRNL_INCLUDE_INTERNAL_KE_H */
|
||||
|
|
|
@ -24,9 +24,56 @@ BOOLEAN KeThreadDpcEnable;
|
|||
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
|
||||
KiTimerExpiration(IN PKDPC Dpc,
|
||||
|
@ -34,7 +81,8 @@ KiTimerExpiration(IN PKDPC Dpc,
|
|||
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 @@ KiTimerExpiration(IN PKDPC Dpc,
|
|||
_disable();
|
||||
|
||||
/* Query system and interrupt time */
|
||||
KeQuerySystemTime(&SystemTime);
|
||||
KeQuerySystemTime((PLARGE_INTEGER)&SystemTime);
|
||||
InterruptTime.QuadPart = KeQueryInterruptTime();
|
||||
Limit = KeTickCount.LowPart;
|
||||
|
||||
|
@ -226,6 +274,9 @@ KiTimerExpiration(IN PKDPC Dpc,
|
|||
}
|
||||
} 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 @@ KiQuantumEnd(VOID)
|
|||
/* 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 @@ KiQuantumEnd(VOID)
|
|||
/* 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 @@ KiQuantumEnd(VOID)
|
|||
NextThread->State = Standby;
|
||||
Prcb->NextThread = NextThread;
|
||||
}
|
||||
#else
|
||||
/* Just leave now */
|
||||
KiReleasePrcbLock(Prcb);
|
||||
KeLowerIrql(DISPATCH_LEVEL);
|
||||
KiDispatchThread(Ready);
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -310,11 +358,6 @@ KiQuantumEnd(VOID)
|
|||
Thread->Preempted = FALSE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Otherwise, set maximum quantum */
|
||||
Thread->Quantum = MAX_QUANTUM;
|
||||
}
|
||||
}
|
||||
|
||||
/* Release the thread lock */
|
||||
|
@ -360,13 +403,17 @@ VOID
|
|||
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 @@ KiRetireDpcList(IN PKPRCB Prcb)
|
|||
/* 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)
|
||||
while (DpcData->DpcQueueDepth != 0)
|
||||
{
|
||||
/* Lock the DPC data */
|
||||
/* 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 @@ KiRetireDpcList(IN PKPRCB Prcb)
|
|||
|
||||
/* Release DPC Lock */
|
||||
KefReleaseSpinLockFromDpcLevel(&DpcData->DpcLock);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -446,7 +496,7 @@ KiRetireDpcList(IN PKPRCB Prcb)
|
|||
/* FIXME: 2K3-style scheduling not implemeted */
|
||||
ASSERT(FALSE);
|
||||
}
|
||||
} while (DpcData->DpcQueueDepth);
|
||||
} while (DpcData->DpcQueueDepth != 0);
|
||||
}
|
||||
|
||||
VOID
|
||||
|
@ -458,7 +508,7 @@ KiInitializeDpc(IN PKDPC Dpc,
|
|||
{
|
||||
/* 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 @@ KeInsertQueueDpc(IN PKDPC Dpc,
|
|||
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 @@ KeInsertQueueDpc(IN PKDPC Dpc,
|
|||
|
||||
/* 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)
|
||||
|
@ -526,6 +577,9 @@ KeInsertQueueDpc(IN PKDPC Dpc,
|
|||
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 @@ KeInsertQueueDpc(IN PKDPC Dpc,
|
|||
}
|
||||
|
||||
/* 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 @@ NTAPI
|
|||
KeRemoveQueueDpc(IN PKDPC Dpc)
|
||||
{
|
||||
PKDPC_DATA DpcData;
|
||||
UCHAR DpcType;
|
||||
BOOLEAN Enable;
|
||||
ASSERT_DPC(Dpc);
|
||||
|
||||
/* Disable interrupts */
|
||||
_disable();
|
||||
Enable = KeDisableInterrupts();
|
||||
|
||||
/* Get DPC data and type */
|
||||
DpcType = Dpc->Type;
|
||||
/* Get DPC data */
|
||||
DpcData = Dpc->DpcData;
|
||||
if (DpcData)
|
||||
{
|
||||
|
@ -678,7 +732,7 @@ KeRemoveQueueDpc(IN PKDPC Dpc)
|
|||
}
|
||||
|
||||
/* 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 @@ VOID
|
|||
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);
|
||||
|
|
|
@ -23,6 +23,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
|
||||
KiGetVectorDispatch(IN ULONG Vector,
|
||||
|
|
|
@ -11,6 +11,11 @@
|
|||
#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 ******************************************************************/
|
||||
|
||||
.globl _KiComputeTimerTableIndex@8
|
||||
|
@ -93,7 +98,32 @@ _KeUpdateRunTime@4:
|
|||
|
||||
/* 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:
|
||||
|
|
Loading…
Reference in a new issue