reactos/ntoskrnl/ke/dpc.c
2024-12-14 23:33:49 +01:00

1053 lines
30 KiB
C

/*
* PROJECT: ReactOS Kernel
* LICENSE: GPL - See COPYING in the top level directory
* FILE: ntoskrnl/ke/dpc.c
* PURPOSE: Deferred Procedure Call (DPC) Support
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
* Philip Susi (phreak@iag.net)
* Eric Kohl
*/
/* INCLUDES ******************************************************************/
#include <ntoskrnl.h>
#define NDEBUG
#include <debug.h>
/* GLOBALS *******************************************************************/
ULONG KiMaximumDpcQueueDepth = 4;
ULONG KiMinimumDpcRate = 3;
ULONG KiAdjustDpcThreshold = 20;
ULONG KiIdealDpcRate = 20;
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,
IN PVOID DeferredContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2)
{
ULARGE_INTEGER SystemTime, InterruptTime;
LARGE_INTEGER Interval;
LONG Limit, Index, i;
ULONG Timers, ActiveTimers, DpcCalls;
PLIST_ENTRY ListHead, NextEntry;
KIRQL OldIrql;
PKTIMER Timer;
PKDPC TimerDpc;
ULONG Period;
DPC_QUEUE_ENTRY DpcEntry[MAX_TIMER_DPCS];
PKSPIN_LOCK_QUEUE LockQueue;
PKPRCB Prcb = KeGetCurrentPrcb();
/* Disable interrupts */
_disable();
/* Query system and interrupt time */
KeQuerySystemTime((PLARGE_INTEGER)&SystemTime);
InterruptTime.QuadPart = KeQueryInterruptTime();
Limit = KeTickCount.LowPart;
/* Bring interrupts back */
_enable();
/* Get the index of the timer and normalize it */
Index = PtrToLong(SystemArgument1);
if ((Limit - Index) >= TIMER_TABLE_SIZE)
{
/* Normalize it */
Limit = Index + TIMER_TABLE_SIZE - 1;
}
/* Setup index and actual limit */
Index--;
Limit &= (TIMER_TABLE_SIZE - 1);
/* Setup accounting data */
DpcCalls = 0;
Timers = 24;
ActiveTimers = 4;
/* Lock the Database and Raise IRQL */
OldIrql = KiAcquireDispatcherLock();
/* Start expiration loop */
do
{
/* Get the current index */
Index = (Index + 1) & (TIMER_TABLE_SIZE - 1);
/* Get list pointers and loop the list */
ListHead = &KiTimerTableListHead[Index].Entry;
while (ListHead != ListHead->Flink)
{
/* Lock the timer and go to the next entry */
LockQueue = KiAcquireTimerLock(Index);
NextEntry = ListHead->Flink;
/* Get the current timer and check its due time */
Timers--;
Timer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry);
if ((NextEntry != ListHead) &&
(Timer->DueTime.QuadPart <= InterruptTime.QuadPart))
{
/* It's expired, remove it */
ActiveTimers--;
KiRemoveEntryTimer(Timer);
/* Make it non-inserted, unlock it, and signal it */
Timer->Header.Inserted = FALSE;
KiReleaseTimerLock(LockQueue);
Timer->Header.SignalState = 1;
/* Get the DPC and period */
TimerDpc = Timer->Dpc;
Period = Timer->Period;
/* Check if there's any waiters */
if (!IsListEmpty(&Timer->Header.WaitListHead))
{
/* Check the type of event */
if (Timer->Header.Type == TimerNotificationObject)
{
/* Unwait the thread */
KxUnwaitThread(&Timer->Header, IO_NO_INCREMENT);
}
else
{
/* Otherwise unwait the thread and signal the timer */
KxUnwaitThreadForEvent((PKEVENT)Timer, IO_NO_INCREMENT);
}
}
/* Check if we have a period */
if (Period)
{
/* Calculate the interval and insert the timer */
Interval.QuadPart = Int32x32To64(Period, -10000);
while (!KiInsertTreeTimer(Timer, Interval));
}
/* Check if we have a DPC */
if (TimerDpc)
{
#ifdef CONFIG_SMP
/*
* If the DPC is targeted to another processor,
* then insert it into that processor's DPC queue
* instead of delivering it now.
* If the DPC is a threaded DPC, and the current CPU
* has threaded DPCs enabled (KiExecuteDpc is actively parsing DPCs),
* then also insert it into the DPC queue for threaded delivery,
* instead of doing it here.
*/
if (((TimerDpc->Number >= MAXIMUM_PROCESSORS) &&
((TimerDpc->Number - MAXIMUM_PROCESSORS) != Prcb->Number)) ||
((TimerDpc->Type == ThreadedDpcObject) && (Prcb->ThreadDpcEnable)))
{
/* Queue it */
KeInsertQueueDpc(TimerDpc,
UlongToPtr(SystemTime.LowPart),
UlongToPtr(SystemTime.HighPart));
}
else
#endif
{
/* Setup the DPC Entry */
DpcEntry[DpcCalls].Dpc = TimerDpc;
DpcEntry[DpcCalls].Routine = TimerDpc->DeferredRoutine;
DpcEntry[DpcCalls].Context = TimerDpc->DeferredContext;
DpcCalls++;
ASSERT(DpcCalls < MAX_TIMER_DPCS);
}
}
/* Check if we're done processing */
if (!(ActiveTimers) || !(Timers))
{
/* Release the dispatcher while doing DPCs */
KiReleaseDispatcherLock(DISPATCH_LEVEL);
/* Start looping all DPC Entries */
for (i = 0; DpcCalls; DpcCalls--, i++)
{
#if DBG
/* Clear DPC Time */
Prcb->DebugDpcTime = 0;
#endif
/* Call the DPC */
DpcEntry[i].Routine(DpcEntry[i].Dpc,
DpcEntry[i].Context,
UlongToPtr(SystemTime.LowPart),
UlongToPtr(SystemTime.HighPart));
}
/* Reset accounting */
Timers = 24;
ActiveTimers = 4;
/* Lock the dispatcher database */
KiAcquireDispatcherLock();
}
}
else
{
/* Check if the timer list is empty */
if (NextEntry != ListHead)
{
/* Sanity check */
ASSERT(KiTimerTableListHead[Index].Time.QuadPart <=
Timer->DueTime.QuadPart);
/* Update the time */
_disable();
KiTimerTableListHead[Index].Time.QuadPart =
Timer->DueTime.QuadPart;
_enable();
}
/* Release the lock */
KiReleaseTimerLock(LockQueue);
/* Check if we've scanned all the timers we could */
if (!Timers)
{
/* Release the dispatcher while doing DPCs */
KiReleaseDispatcherLock(DISPATCH_LEVEL);
/* Start looping all DPC Entries */
for (i = 0; DpcCalls; DpcCalls--, i++)
{
#if DBG
/* Clear DPC Time */
Prcb->DebugDpcTime = 0;
#endif
/* Call the DPC */
DpcEntry[i].Routine(DpcEntry[i].Dpc,
DpcEntry[i].Context,
UlongToPtr(SystemTime.LowPart),
UlongToPtr(SystemTime.HighPart));
}
/* Reset accounting */
Timers = 24;
ActiveTimers = 4;
/* Lock the dispatcher database */
KiAcquireDispatcherLock();
}
/* Done looping */
break;
}
}
} while (Index != Limit);
/* Verify the timer table, on debug builds */
if (KeNumberProcessors == 1) KiCheckTimerTable(InterruptTime);
/* Check if we still have DPC entries */
if (DpcCalls)
{
/* Release the dispatcher while doing DPCs */
KiReleaseDispatcherLock(DISPATCH_LEVEL);
/* Start looping all DPC Entries */
for (i = 0; DpcCalls; DpcCalls--, i++)
{
#if DBG
/* Clear DPC Time */
Prcb->DebugDpcTime = 0;
#endif
/* Call the DPC */
DpcEntry[i].Routine(DpcEntry[i].Dpc,
DpcEntry[i].Context,
UlongToPtr(SystemTime.LowPart),
UlongToPtr(SystemTime.HighPart));
}
/* Lower IRQL if we need to */
if (OldIrql != DISPATCH_LEVEL) KeLowerIrql(OldIrql);
}
else
{
/* Unlock the dispatcher */
KiReleaseDispatcherLock(OldIrql);
}
}
VOID
FASTCALL
KiTimerListExpire(IN PLIST_ENTRY ExpiredListHead,
IN KIRQL OldIrql)
{
ULARGE_INTEGER SystemTime;
LARGE_INTEGER Interval;
LONG i;
ULONG DpcCalls = 0;
PKTIMER Timer;
PKDPC TimerDpc;
ULONG Period;
DPC_QUEUE_ENTRY DpcEntry[MAX_TIMER_DPCS];
PKPRCB Prcb = KeGetCurrentPrcb();
/* Query system */
KeQuerySystemTime((PLARGE_INTEGER)&SystemTime);
/* Loop expired list */
while (ExpiredListHead->Flink != ExpiredListHead)
{
/* Get the current timer */
Timer = CONTAINING_RECORD(ExpiredListHead->Flink, KTIMER, TimerListEntry);
/* Remove it */
RemoveEntryList(&Timer->TimerListEntry);
/* Not inserted */
Timer->Header.Inserted = FALSE;
/* Signal it */
Timer->Header.SignalState = 1;
/* Get the DPC and period */
TimerDpc = Timer->Dpc;
Period = Timer->Period;
/* Check if there's any waiters */
if (!IsListEmpty(&Timer->Header.WaitListHead))
{
/* Check the type of event */
if (Timer->Header.Type == TimerNotificationObject)
{
/* Unwait the thread */
KxUnwaitThread(&Timer->Header, IO_NO_INCREMENT);
}
else
{
/* Otherwise unwait the thread and signal the timer */
KxUnwaitThreadForEvent((PKEVENT)Timer, IO_NO_INCREMENT);
}
}
/* Check if we have a period */
if (Period)
{
/* Calculate the interval and insert the timer */
Interval.QuadPart = Int32x32To64(Period, -10000);
while (!KiInsertTreeTimer(Timer, Interval));
}
/* Check if we have a DPC */
if (TimerDpc)
{
#ifdef CONFIG_SMP
/*
* If the DPC is targeted to another processor,
* then insert it into that processor's DPC queue
* instead of delivering it now.
* If the DPC is a threaded DPC, and the current CPU
* has threaded DPCs enabled (KiExecuteDpc is actively parsing DPCs),
* then also insert it into the DPC queue for threaded delivery,
* instead of doing it here.
*/
if (((TimerDpc->Number >= MAXIMUM_PROCESSORS) &&
((TimerDpc->Number - MAXIMUM_PROCESSORS) != Prcb->Number)) ||
((TimerDpc->Type == ThreadedDpcObject) && (Prcb->ThreadDpcEnable)))
{
/* Queue it */
KeInsertQueueDpc(TimerDpc,
UlongToPtr(SystemTime.LowPart),
UlongToPtr(SystemTime.HighPart));
}
else
#endif
{
/* Setup the DPC Entry */
DpcEntry[DpcCalls].Dpc = TimerDpc;
DpcEntry[DpcCalls].Routine = TimerDpc->DeferredRoutine;
DpcEntry[DpcCalls].Context = TimerDpc->DeferredContext;
DpcCalls++;
ASSERT(DpcCalls < MAX_TIMER_DPCS);
}
}
}
/* Check if we still have DPC entries */
if (DpcCalls)
{
/* Release the dispatcher while doing DPCs */
KiReleaseDispatcherLock(DISPATCH_LEVEL);
/* Start looping all DPC Entries */
for (i = 0; DpcCalls; DpcCalls--, i++)
{
#if DBG
/* Clear DPC Time */
Prcb->DebugDpcTime = 0;
#endif
/* Call the DPC */
DpcEntry[i].Routine(DpcEntry[i].Dpc,
DpcEntry[i].Context,
UlongToPtr(SystemTime.LowPart),
UlongToPtr(SystemTime.HighPart));
}
/* Lower IRQL */
KeLowerIrql(OldIrql);
}
else
{
/* Unlock the dispatcher */
KiReleaseDispatcherLock(OldIrql);
}
}
_Requires_lock_not_held_(Prcb->PrcbLock)
VOID
NTAPI
KiQuantumEnd(VOID)
{
PKPRCB Prcb = KeGetCurrentPrcb();
PKTHREAD NextThread, Thread = Prcb->CurrentThread;
/* Check if a DPC Event was requested to be signaled */
if (InterlockedExchange(&Prcb->DpcSetEventRequest, 0))
{
/* Signal it */
KeSetEvent(&Prcb->DpcEvent, 0, 0);
}
/* Raise to synchronization level and lock the PRCB and thread */
KeRaiseIrqlToSynchLevel();
KiAcquireThreadLock(Thread);
KiAcquirePrcbLock(Prcb);
/* Check if Quantum expired */
if (Thread->Quantum <= 0)
{
/* 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;
/* Calculate new priority */
Thread->Priority = KiComputeNewPriority(Thread, 1);
/* Check if a new thread is scheduled */
if (!Prcb->NextThread)
{
/* Get a new ready thread */
NextThread = KiSelectReadyThread(Thread->Priority, Prcb);
if (NextThread)
{
/* Found one, set it on standby */
NextThread->State = Standby;
Prcb->NextThread = NextThread;
}
}
else
{
/* Otherwise, make sure that this thread doesn't get preempted */
Thread->Preempted = FALSE;
}
}
}
/* Release the thread lock */
KiReleaseThreadLock(Thread);
/* Check if there's no thread scheduled */
if (!Prcb->NextThread)
{
/* Just leave now */
KiReleasePrcbLock(Prcb);
KeLowerIrql(DISPATCH_LEVEL);
return;
}
/* Get the next thread now */
NextThread = Prcb->NextThread;
/* Set current thread's swap busy to true */
KiSetThreadSwapBusy(Thread);
/* Switch threads in PRCB */
Prcb->NextThread = NULL;
Prcb->CurrentThread = NextThread;
/* Set thread to running and the switch reason to Quantum End */
NextThread->State = Running;
Thread->WaitReason = WrQuantumEnd;
/* Queue it on the ready lists */
KxQueueReadyThread(Thread, Prcb);
/* Set wait IRQL to APC_LEVEL */
Thread->WaitIrql = APC_LEVEL;
/* Swap threads */
KiSwapContext(APC_LEVEL, Thread);
/* Lower IRQL back to DISPATCH_LEVEL */
KeLowerIrql(DISPATCH_LEVEL);
}
VOID
FASTCALL
KiRetireDpcList(IN PKPRCB Prcb)
{
PKDPC_DATA DpcData;
PLIST_ENTRY ListHead, DpcEntry;
PKDPC Dpc;
PKDEFERRED_ROUTINE DeferredRoutine;
PVOID DeferredContext, SystemArgument1, SystemArgument2;
ULONG_PTR TimerHand;
#ifdef CONFIG_SMP
KIRQL OldIrql;
#endif
/* Get data and list variables before starting anything else */
DpcData = &Prcb->DpcData[DPC_NORMAL];
ListHead = &DpcData->DpcListHead;
/* Main outer loop */
do
{
/* Set us as active */
Prcb->DpcRoutineActive = TRUE;
/* 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 interrupts enabled */
_enable();
KiTimerExpiration(NULL, NULL, (PVOID)TimerHand, NULL);
_disable();
}
/* Loop while we have entries in the queue */
while (DpcData->DpcQueueDepth != 0)
{
/* Lock the DPC data and get the DPC entry*/
KeAcquireSpinLockAtDpcLevel(&DpcData->DpcLock);
DpcEntry = ListHead->Flink;
/* Make sure we have an entry */
if (DpcEntry != ListHead)
{
/* Remove the DPC from the list */
RemoveEntryList(DpcEntry);
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--;
#if DBG
/* Clear DPC Time */
Prcb->DebugDpcTime = 0;
#endif
/* Release the lock */
KeReleaseSpinLockFromDpcLevel(&DpcData->DpcLock);
/* Re-enable interrupts */
_enable();
/* Call the DPC */
DeferredRoutine(Dpc,
DeferredContext,
SystemArgument1,
SystemArgument2);
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
/* Disable interrupts and keep looping */
_disable();
}
else
{
/* The queue should be flushed now */
ASSERT(DpcData->DpcQueueDepth == 0);
/* Release DPC Lock */
KeReleaseSpinLockFromDpcLevel(&DpcData->DpcLock);
}
}
/* Clear DPC Flags */
Prcb->DpcRoutineActive = FALSE;
Prcb->DpcInterruptRequested = FALSE;
#ifdef CONFIG_SMP
/* Check if we have deferred threads */
if (Prcb->DeferredReadyListHead.Next)
{
/* Re-enable interrupts and raise to synch */
_enable();
OldIrql = KeRaiseIrqlToSynchLevel();
/* Process deferred threads */
KiProcessDeferredReadyList(Prcb);
/* Lower IRQL back and disable interrupts */
KeLowerIrql(OldIrql);
_disable();
}
#endif
} while (DpcData->DpcQueueDepth != 0);
}
VOID
NTAPI
KiInitializeDpc(IN PKDPC Dpc,
IN PKDEFERRED_ROUTINE DeferredRoutine,
IN PVOID DeferredContext,
IN KOBJECTS Type)
{
/* Setup the DPC Object */
Dpc->Type = Type;
Dpc->Number = 0;
Dpc->Importance= MediumImportance;
Dpc->DeferredRoutine = DeferredRoutine;
Dpc->DeferredContext = DeferredContext;
Dpc->DpcData = NULL;
}
/* PUBLIC FUNCTIONS **********************************************************/
/*
* @implemented
*/
VOID
NTAPI
KeInitializeThreadedDpc(IN PKDPC Dpc,
IN PKDEFERRED_ROUTINE DeferredRoutine,
IN PVOID DeferredContext)
{
/* Call the internal routine */
KiInitializeDpc(Dpc, DeferredRoutine, DeferredContext, ThreadedDpcObject);
}
/*
* @implemented
*/
VOID
NTAPI
KeInitializeDpc(IN PKDPC Dpc,
IN PKDEFERRED_ROUTINE DeferredRoutine,
IN PVOID DeferredContext)
{
/* Call the internal routine */
KiInitializeDpc(Dpc, DeferredRoutine, DeferredContext, DpcObject);
}
/*
* @implemented
*/
BOOLEAN
NTAPI
KeInsertQueueDpc(IN PKDPC Dpc,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2)
{
KIRQL OldIrql;
PKPRCB Prcb, CurrentPrcb;
ULONG Cpu;
PKDPC_DATA DpcData;
BOOLEAN DpcConfigured = FALSE, DpcInserted = FALSE;
ASSERT_DPC(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)
{
/* Then substract the maximum and get that PRCB. */
Cpu = Dpc->Number - MAXIMUM_PROCESSORS;
Prcb = KiProcessorBlock[Cpu];
}
else
{
/* Use the current one */
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))
{
/* Then use the threaded data */
DpcData = &Prcb->DpcData[DPC_THREADED];
}
else
{
/* Otherwise, use the regular data */
DpcData = &Prcb->DpcData[DPC_NORMAL];
}
/* Acquire the DPC lock */
KiAcquireSpinLock(&DpcData->DpcLock);
/* Get the DPC Data */
if (!InterlockedCompareExchangePointer(&Dpc->DpcData, DpcData, NULL))
{
/* Now we can play with the DPC safely */
Dpc->SystemArgument1 = SystemArgument1;
Dpc->SystemArgument2 = SystemArgument2;
DpcData->DpcQueueDepth++;
DpcData->DpcCount++;
DpcConfigured = TRUE;
/* Check if this is a high importance DPC */
if (Dpc->Importance == HighImportance)
{
/* Pre-empty other DPCs */
InsertHeadList(&DpcData->DpcListHead, &Dpc->DpcListEntry);
}
else
{
/* Add it at the end */
InsertTailList(&DpcData->DpcListHead, &Dpc->DpcListEntry);
}
/* Check if this is the DPC on the threaded list */
if (&Prcb->DpcData[DPC_THREADED] == DpcData)
{
/* Make sure a threaded DPC isn't already active */
if (!(Prcb->DpcThreadActive) && !(Prcb->DpcThreadRequested))
{
/* FIXME: Setup Threaded DPC */
UNIMPLEMENTED_FATAL("Threaded DPC not supported\n");
}
}
else
{
/* Make sure a DPC isn't executing already */
if (!(Prcb->DpcRoutineActive) && !(Prcb->DpcInterruptRequested))
{
/* Check if this is the same CPU */
if (Prcb != CurrentPrcb)
{
/*
* Check if the DPC is of high importance or above the
* maximum depth. If it is, then make sure that the CPU
* isn't idle, or that it's sleeping.
*/
if (((Dpc->Importance == HighImportance) ||
(DpcData->DpcQueueDepth >=
Prcb->MaximumDpcQueueDepth)) &&
(!(AFFINITY_MASK(Cpu) & KiIdleSummary) ||
(Prcb->Sleeping)))
{
/* Set interrupt requested */
Prcb->DpcInterruptRequested = TRUE;
/* Set DPC inserted */
DpcInserted = TRUE;
}
}
else
{
/* Check if the DPC is of anything but low importance */
if ((Dpc->Importance != LowImportance) ||
(DpcData->DpcQueueDepth >=
Prcb->MaximumDpcQueueDepth) ||
(Prcb->DpcRequestRate < Prcb->MinimumDpcRate))
{
/* Set interrupt requested */
Prcb->DpcInterruptRequested = TRUE;
/* Set DPC inserted */
DpcInserted = TRUE;
}
}
}
}
}
/* Release the lock */
KiReleaseSpinLock(&DpcData->DpcLock);
/* Check if the DPC was inserted */
if (DpcInserted)
{
/* Check if this was SMP */
if (Prcb != CurrentPrcb)
{
/* It was, request and IPI */
KiIpiSend(AFFINITY_MASK(Cpu), IPI_DPC);
}
else
{
/* It wasn't, request an interrupt from HAL */
HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
}
}
/* Lower IRQL */
KeLowerIrql(OldIrql);
return DpcConfigured;
}
/*
* @implemented
*/
BOOLEAN
NTAPI
KeRemoveQueueDpc(IN PKDPC Dpc)
{
PKDPC_DATA DpcData;
BOOLEAN Enable;
ASSERT_DPC(Dpc);
/* Disable interrupts */
Enable = KeDisableInterrupts();
/* Get DPC data */
DpcData = Dpc->DpcData;
if (DpcData)
{
/* Acquire the DPC lock */
KiAcquireSpinLock(&DpcData->DpcLock);
/* Make sure that the data didn't change */
if (DpcData == Dpc->DpcData)
{
/* Remove the DPC */
DpcData->DpcQueueDepth--;
RemoveEntryList(&Dpc->DpcListEntry);
Dpc->DpcData = NULL;
}
/* Release the lock */
KiReleaseSpinLock(&DpcData->DpcLock);
}
/* Re-enable interrupts */
KeRestoreInterrupts(Enable);
/* Return if the DPC was in the queue or not */
return DpcData ? TRUE : FALSE;
}
/*
* @implemented
*/
_IRQL_requires_max_(APC_LEVEL)
VOID
NTAPI
KeFlushQueuedDpcs(VOID)
{
ULONG ProcessorIndex;
PKPRCB TargetPrcb;
PAGED_CODE();
ASSERT(KeGetCurrentThread()->SystemAffinityActive == FALSE);
/* Loop all processors */
for (ProcessorIndex = 0; ProcessorIndex < KeNumberProcessors; ProcessorIndex++)
{
/* Get the target processor's PRCB */
TargetPrcb = KiProcessorBlock[ProcessorIndex];
/* Check if there are DPCs on either queues */
if ((TargetPrcb->DpcData[DPC_NORMAL].DpcQueueDepth > 0) ||
(TargetPrcb->DpcData[DPC_THREADED].DpcQueueDepth > 0))
{
/* Check if this is the current processor */
if (TargetPrcb == KeGetCurrentPrcb())
{
/* Request a DPC interrupt */
HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
}
else
{
/* Attach to the target processor. This will cause a DPC
interrupt on the target processor and flush all DPCs. */
KeSetSystemAffinityThread(TargetPrcb->SetMember);
}
}
}
/* Revert back to user affinity */
if (KeGetCurrentThread()->SystemAffinityActive)
{
KeRevertToUserAffinityThread();
}
}
/*
* @implemented
*/
BOOLEAN
NTAPI
KeIsExecutingDpc(VOID)
{
/* Return if the Dpc Routine is active */
return KeGetCurrentPrcb()->DpcRoutineActive;
}
/*
* @implemented
*/
VOID
NTAPI
KeSetImportanceDpc (IN PKDPC Dpc,
IN KDPC_IMPORTANCE Importance)
{
/* Set the DPC Importance */
ASSERT_DPC(Dpc);
Dpc->Importance = Importance;
}
/*
* @implemented
*/
VOID
NTAPI
KeSetTargetProcessorDpc(IN PKDPC Dpc,
IN CCHAR Number)
{
/* Set a target CPU */
ASSERT_DPC(Dpc);
Dpc->Number = Number + MAXIMUM_PROCESSORS;
}
/*
* @implemented
*/
VOID
NTAPI
KeGenericCallDpc(IN PKDEFERRED_ROUTINE Routine,
IN PVOID Context)
{
ULONG Barrier = KeNumberProcessors;
KIRQL OldIrql;
DEFERRED_REVERSE_BARRIER ReverseBarrier;
ASSERT(KeGetCurrentIrql () < DISPATCH_LEVEL);
//
// The barrier is the number of processors, each processor will decrement it
// by one, so when all processors have run the DPC, the barrier reaches zero
//
ReverseBarrier.Barrier = Barrier;
ReverseBarrier.TotalProcessors = Barrier;
//
// But we don't need the barrier on UP, since we can simply call the routine
// directly while at DISPATCH_LEVEL and not worry about anything else
//
KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
Routine(&KeGetCurrentPrcb()->CallDpc, Context, &Barrier, &ReverseBarrier);
KeLowerIrql(OldIrql);
}
/*
* @implemented
*/
VOID
NTAPI
KeSignalCallDpcDone(IN PVOID SystemArgument1)
{
//
// Decrement the barrier, which is actually the processor count
//
InterlockedDecrement((PLONG)SystemArgument1);
}
/*
* @implemented
*/
BOOLEAN
NTAPI
KeSignalCallDpcSynchronize(IN PVOID SystemArgument2)
{
//
// There is nothing to do on UP systems -- the processor calling this wins
//
UNREFERENCED_PARAMETER(SystemArgument2);
return TRUE;
}
/* EOF */