mirror of
https://github.com/reactos/reactos.git
synced 2025-08-06 06:43:13 +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
|
* PROJECT: ReactOS Kernel
|
||||||
* LICENSE: GPL - See COPYING in the top level directory
|
* 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
|
* PURPOSE: Routines for CPU-level support
|
||||||
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
|
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
|
||||||
* Philip Susi (phreak@iag.net)
|
* Philip Susi (phreak@iag.net)
|
||||||
|
@ -26,6 +26,177 @@ KMUTEX KiGenericCallDpcMutex;
|
||||||
|
|
||||||
/* PRIVATE FUNCTIONS *********************************************************/
|
/* 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
|
VOID
|
||||||
NTAPI
|
NTAPI
|
||||||
KiInitializeDpc(IN PKDPC Dpc,
|
KiInitializeDpc(IN PKDPC Dpc,
|
||||||
|
@ -312,183 +483,53 @@ KeSetTargetProcessorDpc(IN PKDPC Dpc,
|
||||||
Dpc->Number = Number + MAXIMUM_PROCESSORS;
|
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
|
* @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
|
VOID
|
||||||
STDCALL
|
NTAPI
|
||||||
KiDispatchInterrupt(VOID)
|
KiDispatchInterrupt(VOID)
|
||||||
{
|
{
|
||||||
PLIST_ENTRY DpcEntry;
|
PKIPCR Pcr = (PKIPCR)KeGetPcr();
|
||||||
PKDPC Dpc;
|
PVOID ExceptionList;
|
||||||
KIRQL OldIrql;
|
|
||||||
PKPRCB Prcb;
|
|
||||||
|
|
||||||
DPRINT("Dispatching Interrupts\n");
|
/* Disable interrupts */
|
||||||
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
|
Ke386DisableInterrupts();
|
||||||
|
|
||||||
/* Set DPC Deliver to Active */
|
/* Check if we have to deliver DPCs, timers, or deferred threads */
|
||||||
Prcb = KeGetCurrentPrcb();
|
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) {
|
/* FIXME: Switch to DPC Stack */
|
||||||
/* Raise IRQL */
|
|
||||||
KeRaiseIrql(HIGH_LEVEL, &OldIrql);
|
|
||||||
#ifdef CONFIG_SMP
|
|
||||||
KiAcquireSpinLock(&Prcb->DpcData[0].DpcLock);
|
|
||||||
#endif
|
|
||||||
Prcb->DpcRoutineActive = TRUE;
|
|
||||||
|
|
||||||
DPRINT("&Prcb->DpcData[0].DpcListHead: %x\n", &Prcb->DpcData[0].DpcListHead);
|
/* Deliver DPCs */
|
||||||
/* Loop while we have entries */
|
KiRetireDpcList(Pcr->Prcb);
|
||||||
while (!IsListEmpty(&Prcb->DpcData[0].DpcListHead)) {
|
|
||||||
|
|
||||||
ASSERT(Prcb->DpcData[0].DpcQueueDepth > 0);
|
/* FIXME: Restore stack */
|
||||||
DPRINT("Queue Depth: %x\n", Prcb->DpcData[0].DpcQueueDepth);
|
|
||||||
|
|
||||||
/* Get the DPC call it */
|
/* Restore exception list */
|
||||||
DpcEntry = RemoveHeadList(&Prcb->DpcData[0].DpcListHead);
|
Pcr->NtTib.ExceptionList = ExceptionList;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DPRINT("Checking for Quantum End\n");
|
/* Re-enable interrupts */
|
||||||
|
Ke386EnableInterrupts();
|
||||||
|
|
||||||
/* If we have Quantum End, call the function */
|
/* Check if we have quantum end */
|
||||||
if (Prcb->QuantumEnd) {
|
if (Pcr->PrcbData.QuantumEnd)
|
||||||
|
{
|
||||||
Prcb->QuantumEnd = FALSE;
|
/* Process it */
|
||||||
|
Pcr->PrcbData.QuantumEnd = FALSE;
|
||||||
KiQuantumEnd();
|
KiQuantumEnd();
|
||||||
}
|
}
|
||||||
|
else if (Pcr->PrcbData.NextThread)
|
||||||
|
{
|
||||||
|
/* FIXME: Schedule new thread */
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* EOF */
|
/* EOF */
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue