mirror of
https://github.com/reactos/reactos.git
synced 2024-11-20 06:15:26 +00:00
- Part 1.5 of 2: Re-factor KiDispatchInterrupt to separate DPC delivery routine (so that it can stay in C, since KiDispatch should be done in ASM so we can switch stacks).
- Use interrupt enable/disable instead of raising to HIGH_LEVEL. - Better use and pairing of locks. - Fix a lot of race conditions in DPC dispatching, due to the fact almost all data we're touching is ultra-volatile and can change at any time depending on various locks being held/released + interrupt state. - Add stub code/support for tick-hased table-based timer implementation and deferred threads. svn path=/trunk/; revision=23888
This commit is contained in:
parent
7802140549
commit
0f9c7bdf03
1 changed files with 202 additions and 161 deletions
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* PROJECT: ReactOS Kernel
|
||||
* LICENSE: GPL - See COPYING in the top level directory
|
||||
* FILE: ntoskrnl/ke/i386/cpu.c
|
||||
* FILE: ntoskrnl/ke/dpc.c
|
||||
* PURPOSE: Routines for CPU-level support
|
||||
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
|
||||
* Philip Susi (phreak@iag.net)
|
||||
|
@ -26,6 +26,177 @@ KMUTEX KiGenericCallDpcMutex;
|
|||
|
||||
/* PRIVATE FUNCTIONS *********************************************************/
|
||||
|
||||
VOID
|
||||
NTAPI
|
||||
KiQuantumEnd(VOID)
|
||||
{
|
||||
PKPRCB Prcb = KeGetCurrentPrcb();
|
||||
PKTHREAD CurrentThread = KeGetCurrentThread();
|
||||
KIRQL OldIrql;
|
||||
PKPROCESS Process;
|
||||
KPRIORITY OldPriority;
|
||||
KPRIORITY NewPriority;
|
||||
|
||||
/* Check if a DPC Event was requested to be signaled */
|
||||
if (InterlockedExchange(&Prcb->DpcSetEventRequest, 0))
|
||||
{
|
||||
/* Signal it */
|
||||
KeSetEvent(&Prcb->DpcEvent, 0, 0);
|
||||
}
|
||||
|
||||
/* Lock dispatcher */
|
||||
OldIrql = KeRaiseIrqlToSynchLevel();
|
||||
ASSERT(KeGetCurrentIrql() >= DISPATCH_LEVEL);
|
||||
|
||||
/* Get the Thread's Process */
|
||||
Process = CurrentThread->ApcState.Process;
|
||||
|
||||
/* Check if Quantum expired */
|
||||
if (CurrentThread->Quantum <= 0)
|
||||
{
|
||||
/* Reset the new Quantum */
|
||||
CurrentThread->Quantum = CurrentThread->QuantumReset;
|
||||
|
||||
/* Calculate new priority */
|
||||
OldPriority = CurrentThread->Priority;
|
||||
if (OldPriority < LOW_REALTIME_PRIORITY)
|
||||
{
|
||||
/* Set the New Priority and add the Priority Decrement */
|
||||
NewPriority = OldPriority - CurrentThread->PriorityDecrement - 1;
|
||||
|
||||
/* Don't go out of bounds */
|
||||
if (NewPriority < CurrentThread->BasePriority)
|
||||
{
|
||||
NewPriority = CurrentThread->BasePriority;
|
||||
}
|
||||
|
||||
/* Reset the priority decrement */
|
||||
CurrentThread->PriorityDecrement = 0;
|
||||
|
||||
/* Set a new priority if needed */
|
||||
if (OldPriority != NewPriority)
|
||||
{
|
||||
/* Set new Priority */
|
||||
BOOLEAN Dummy; /* <- This is a hack anyways... */
|
||||
KiSetPriorityThread(CurrentThread, NewPriority, &Dummy);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Queue new thread if none is already */
|
||||
if (!Prcb->NextThread)
|
||||
{
|
||||
/* FIXME: Schedule a New Thread, when ROS will have NT Scheduler */
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Make the current thread non-premeptive if a new thread is queued */
|
||||
CurrentThread->Preempted = FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Set the Quantum back to Maximum */
|
||||
//if (CurrentThread->DisableQuantum) {
|
||||
// CurrentThread->Quantum = MAX_QUANTUM;
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
/* Dispatch the Thread */
|
||||
KeLowerIrql(DISPATCH_LEVEL);
|
||||
KiDispatchThread(Ready);
|
||||
}
|
||||
|
||||
VOID
|
||||
FASTCALL
|
||||
KiRetireDpcList(IN PKPRCB Prcb)
|
||||
{
|
||||
PKDPC_DATA DpcData = Prcb->DpcData;
|
||||
PLIST_ENTRY DpcEntry;
|
||||
PKDPC Dpc;
|
||||
PKDEFERRED_ROUTINE DeferredRoutine;
|
||||
PVOID DeferredContext, SystemArgument1, SystemArgument2;
|
||||
|
||||
/* Main outer loop */
|
||||
do
|
||||
{
|
||||
/* Set us as active */
|
||||
Prcb->DpcRoutineActive = TRUE;
|
||||
|
||||
/* Check if this is a timer expiration request */
|
||||
if (Prcb->TimerRequest)
|
||||
{
|
||||
/* FIXME: Not yet implemented */
|
||||
ASSERT(FALSE);
|
||||
}
|
||||
|
||||
/* Loop while we have entries in the queue */
|
||||
while (DpcData->DpcQueueDepth)
|
||||
{
|
||||
/* Lock the DPC data */
|
||||
KefAcquireSpinLockAtDpcLevel(&DpcData->DpcLock);
|
||||
|
||||
/* Make sure we have an entry */
|
||||
if (!IsListEmpty(&DpcData->DpcListHead))
|
||||
{
|
||||
/* Remove the DPC from the list */
|
||||
DpcEntry = RemoveHeadList(&DpcData->DpcListHead);
|
||||
Dpc = CONTAINING_RECORD(DpcEntry, KDPC, DpcListEntry);
|
||||
|
||||
/* Clear its DPC data and save its parameters */
|
||||
Dpc->DpcData = NULL;
|
||||
DeferredRoutine = Dpc->DeferredRoutine;
|
||||
DeferredContext = Dpc->DeferredContext;
|
||||
SystemArgument1 = Dpc->SystemArgument1;
|
||||
SystemArgument2 = Dpc->SystemArgument2;
|
||||
|
||||
/* Decrease the queue depth */
|
||||
DpcData->DpcQueueDepth--;
|
||||
|
||||
/* Clear DPC Time */
|
||||
Prcb->DebugDpcTime = 0;
|
||||
|
||||
/* Release the lock */
|
||||
KefReleaseSpinLockFromDpcLevel(&DpcData->DpcLock);
|
||||
|
||||
/* Re-enable interrupts */
|
||||
Ke386EnableInterrupts();
|
||||
|
||||
/* Call the DPC */
|
||||
DeferredRoutine(Dpc,
|
||||
DeferredContext,
|
||||
SystemArgument1,
|
||||
SystemArgument2);
|
||||
ASSERT_IRQL(DISPATCH_LEVEL);
|
||||
|
||||
/* Disable interrupts and keep looping */
|
||||
Ke386DisableInterrupts();
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The queue should be flushed now */
|
||||
ASSERT(DpcData->DpcQueueDepth == 0);
|
||||
|
||||
/* Release DPC Lock */
|
||||
KefReleaseSpinLockFromDpcLevel(&DpcData->DpcLock);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Clear DPC Flags */
|
||||
Prcb->DpcRoutineActive = FALSE;
|
||||
Prcb->DpcInterruptRequested = FALSE;
|
||||
|
||||
/* Check if we have deferred threads */
|
||||
if (Prcb->DeferredReadyListHead.Next)
|
||||
{
|
||||
/* FIXME: 2K3-style scheduling not implemeted */
|
||||
ASSERT(FALSE);
|
||||
}
|
||||
} while (DpcData->DpcQueueDepth);
|
||||
}
|
||||
|
||||
VOID
|
||||
NTAPI
|
||||
KiInitializeDpc(IN PKDPC Dpc,
|
||||
|
@ -312,183 +483,53 @@ KeSetTargetProcessorDpc(IN PKDPC Dpc,
|
|||
Dpc->Number = Number + MAXIMUM_PROCESSORS;
|
||||
}
|
||||
|
||||
/*
|
||||
* FUNCTION:
|
||||
* Called when a quantum end occurs to check if priority should be changed
|
||||
* and wether a new thread should be dispatched.
|
||||
* NOTES:
|
||||
* Called when deleting a Driver.
|
||||
*/
|
||||
VOID
|
||||
STDCALL
|
||||
KiQuantumEnd(VOID)
|
||||
{
|
||||
PKPRCB Prcb;
|
||||
PKTHREAD CurrentThread;
|
||||
KIRQL OldIrql;
|
||||
PKPROCESS Process;
|
||||
KPRIORITY OldPriority;
|
||||
KPRIORITY NewPriority;
|
||||
|
||||
/* Lock dispatcher, get current thread */
|
||||
Prcb = KeGetCurrentPrcb();
|
||||
CurrentThread = KeGetCurrentThread();
|
||||
OldIrql = KeRaiseIrqlToSynchLevel();
|
||||
|
||||
/* Get the Thread's Process */
|
||||
Process = CurrentThread->ApcState.Process;
|
||||
|
||||
/* Set DPC Event if requested */
|
||||
if (Prcb->DpcSetEventRequest)
|
||||
{
|
||||
KeSetEvent(&Prcb->DpcEvent, 0, 0);
|
||||
}
|
||||
|
||||
/* Check if Quantum expired */
|
||||
if (CurrentThread->Quantum <= 0) {
|
||||
|
||||
/* Reset the new Quantum */
|
||||
CurrentThread->Quantum = CurrentThread->QuantumReset;
|
||||
|
||||
/* Calculate new priority */
|
||||
OldPriority = CurrentThread->Priority;
|
||||
if (OldPriority < LOW_REALTIME_PRIORITY) {
|
||||
|
||||
/* Set the New Priority and add the Priority Decrement */
|
||||
NewPriority = OldPriority - CurrentThread->PriorityDecrement - 1;
|
||||
|
||||
/* Don't go out of bounds */
|
||||
if (NewPriority < CurrentThread->BasePriority) NewPriority = CurrentThread->BasePriority;
|
||||
|
||||
/* Reset the priority decrement */
|
||||
CurrentThread->PriorityDecrement = 0;
|
||||
|
||||
/* Set a new priority if needed */
|
||||
if (OldPriority != NewPriority) {
|
||||
|
||||
/* Set new Priority */
|
||||
BOOLEAN Dummy; /* <- This is a hack anyways... */
|
||||
KiSetPriorityThread(CurrentThread, NewPriority, &Dummy);
|
||||
|
||||
} else {
|
||||
|
||||
/* Queue new thread if none is already */
|
||||
if (Prcb->NextThread == NULL) {
|
||||
|
||||
/* FIXME: Schedule a New Thread, when ROS will have NT Scheduler */
|
||||
|
||||
} else {
|
||||
|
||||
/* Make the current thread non-premeptive if a new thread is queued */
|
||||
CurrentThread->Preempted = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
/* Set the Quantum back to Maximum */
|
||||
//if (CurrentThread->DisableQuantum) {
|
||||
// CurrentThread->Quantum = MAX_QUANTUM;
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
/* Dispatch the Thread */
|
||||
KeLowerIrql(DISPATCH_LEVEL);
|
||||
KiDispatchThread(Ready);
|
||||
}
|
||||
|
||||
/*
|
||||
* @implemented
|
||||
*
|
||||
* FUNCTION:
|
||||
* Called whenever a system interrupt is generated at DISPATCH_LEVEL.
|
||||
* It delivers queued DPCs and dispatches a new thread if need be.
|
||||
*/
|
||||
VOID
|
||||
STDCALL
|
||||
NTAPI
|
||||
KiDispatchInterrupt(VOID)
|
||||
{
|
||||
PLIST_ENTRY DpcEntry;
|
||||
PKDPC Dpc;
|
||||
KIRQL OldIrql;
|
||||
PKPRCB Prcb;
|
||||
PKIPCR Pcr = (PKIPCR)KeGetPcr();
|
||||
PVOID ExceptionList;
|
||||
|
||||
DPRINT("Dispatching Interrupts\n");
|
||||
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
|
||||
/* Disable interrupts */
|
||||
Ke386DisableInterrupts();
|
||||
|
||||
/* Set DPC Deliver to Active */
|
||||
Prcb = KeGetCurrentPrcb();
|
||||
/* Check if we have to deliver DPCs, timers, or deferred threads */
|
||||
if ((Pcr->PrcbData.DpcData[DPC_NORMAL].DpcQueueDepth) ||
|
||||
(Pcr->PrcbData.TimerRequest) ||
|
||||
(Pcr->PrcbData.DeferredReadyListHead.Next))
|
||||
{
|
||||
/* Save the exception list and clear it */
|
||||
ExceptionList = Pcr->NtTib.ExceptionList;
|
||||
Pcr->NtTib.ExceptionList = EXCEPTION_CHAIN_END;
|
||||
|
||||
if (Prcb->DpcData[0].DpcQueueDepth > 0) {
|
||||
/* Raise IRQL */
|
||||
KeRaiseIrql(HIGH_LEVEL, &OldIrql);
|
||||
#ifdef CONFIG_SMP
|
||||
KiAcquireSpinLock(&Prcb->DpcData[0].DpcLock);
|
||||
#endif
|
||||
Prcb->DpcRoutineActive = TRUE;
|
||||
/* FIXME: Switch to DPC Stack */
|
||||
|
||||
DPRINT("&Prcb->DpcData[0].DpcListHead: %x\n", &Prcb->DpcData[0].DpcListHead);
|
||||
/* Loop while we have entries */
|
||||
while (!IsListEmpty(&Prcb->DpcData[0].DpcListHead)) {
|
||||
/* Deliver DPCs */
|
||||
KiRetireDpcList(Pcr->Prcb);
|
||||
|
||||
ASSERT(Prcb->DpcData[0].DpcQueueDepth > 0);
|
||||
DPRINT("Queue Depth: %x\n", Prcb->DpcData[0].DpcQueueDepth);
|
||||
/* FIXME: Restore stack */
|
||||
|
||||
/* Get the DPC call it */
|
||||
DpcEntry = RemoveHeadList(&Prcb->DpcData[0].DpcListHead);
|
||||
Dpc = CONTAINING_RECORD(DpcEntry, KDPC, DpcListEntry);
|
||||
DPRINT("Dpc->DpcListEntry.Flink %x\n", Dpc->DpcListEntry.Flink);
|
||||
Dpc->DpcData = NULL;
|
||||
Prcb->DpcData[0].DpcQueueDepth--;
|
||||
#ifdef CONFIG_SMP
|
||||
KiReleaseSpinLock(&Prcb->DpcData[0].DpcLock);
|
||||
#endif
|
||||
/* Disable/Enabled Interrupts and Call the DPC */
|
||||
KeLowerIrql(OldIrql);
|
||||
DPRINT("Calling DPC: %x\n", Dpc);
|
||||
Dpc->DeferredRoutine(Dpc,
|
||||
Dpc->DeferredContext,
|
||||
Dpc->SystemArgument1,
|
||||
Dpc->SystemArgument2);
|
||||
KeRaiseIrql(HIGH_LEVEL, &OldIrql);
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
KiAcquireSpinLock(&Prcb->DpcData[0].DpcLock);
|
||||
/*
|
||||
* If the dpc routine drops the irql below DISPATCH_LEVEL,
|
||||
* a thread switch can occur and after the next thread switch
|
||||
* the execution may start on an other processor.
|
||||
*/
|
||||
if (Prcb != KeGetCurrentPrcb()) {
|
||||
|
||||
Prcb->DpcRoutineActive = FALSE;
|
||||
KiReleaseSpinLock(&Prcb->DpcData[0].DpcLock);
|
||||
Prcb = KeGetCurrentPrcb();
|
||||
KiAcquireSpinLock(&Prcb->DpcData[0].DpcLock);
|
||||
Prcb->DpcRoutineActive = TRUE;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
/* Clear DPC Flags */
|
||||
Prcb->DpcRoutineActive = FALSE;
|
||||
Prcb->DpcInterruptRequested = FALSE;
|
||||
#ifdef CONFIG_SMP
|
||||
KiReleaseSpinLock(&Prcb->DpcData[0].DpcLock);
|
||||
#endif
|
||||
|
||||
/* DPC Dispatching Ended, re-enable interrupts */
|
||||
KeLowerIrql(OldIrql);
|
||||
/* Restore exception list */
|
||||
Pcr->NtTib.ExceptionList = ExceptionList;
|
||||
}
|
||||
|
||||
DPRINT("Checking for Quantum End\n");
|
||||
/* Re-enable interrupts */
|
||||
Ke386EnableInterrupts();
|
||||
|
||||
/* If we have Quantum End, call the function */
|
||||
if (Prcb->QuantumEnd) {
|
||||
|
||||
Prcb->QuantumEnd = FALSE;
|
||||
/* Check if we have quantum end */
|
||||
if (Pcr->PrcbData.QuantumEnd)
|
||||
{
|
||||
/* Process it */
|
||||
Pcr->PrcbData.QuantumEnd = FALSE;
|
||||
KiQuantumEnd();
|
||||
}
|
||||
else if (Pcr->PrcbData.NextThread)
|
||||
{
|
||||
/* FIXME: Schedule new thread */
|
||||
}
|
||||
}
|
||||
|
||||
/* EOF */
|
||||
|
|
Loading…
Reference in a new issue