Something went wrong. Try again.
Reactos
Something went wrong. Try again.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054/* * 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 *********************************************************/
VOIDNTAPIKiCheckTimerTable(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}
VOIDNTAPIKiTimerExpiration(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); }}
VOIDFASTCALLKiTimerListExpire(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)VOIDNTAPIKiQuantumEnd(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);}
VOIDFASTCALLKiRetireDpcList(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);}
VOIDNTAPIKiInitializeDpc(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 */VOIDNTAPIKeInitializeThreadedDpc(IN PKDPC Dpc, IN PKDEFERRED_ROUTINE DeferredRoutine, IN PVOID DeferredContext){ /* Call the internal routine */ KiInitializeDpc(Dpc, DeferredRoutine, DeferredContext, ThreadedDpcObject);}
/* * @implemented */VOIDNTAPIKeInitializeDpc(IN PKDPC Dpc, IN PKDEFERRED_ROUTINE DeferredRoutine, IN PVOID DeferredContext){ /* Call the internal routine */ KiInitializeDpc(Dpc, DeferredRoutine, DeferredContext, DpcObject);}
/* * @implemented */BOOLEANNTAPIKeInsertQueueDpc(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 */BOOLEANNTAPIKeRemoveQueueDpc(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)VOIDNTAPIKeFlushQueuedDpcs(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 */BOOLEANNTAPIKeIsExecutingDpc(VOID){ /* Return if the Dpc Routine is active */ return KeGetCurrentPrcb()->DpcRoutineActive;}
/* * @implemented */VOIDNTAPIKeSetImportanceDpc (IN PKDPC Dpc, IN KDPC_IMPORTANCE Importance){ /* Set the DPC Importance */ ASSERT_DPC(Dpc); Dpc->Importance = Importance;}
/* * @implemented */VOIDNTAPIKeSetTargetProcessorDpc(IN PKDPC Dpc, IN CCHAR Number){ /* Set a target CPU */ ASSERT_DPC(Dpc); Dpc->Number = Number + MAXIMUM_PROCESSORS;}
/* * @implemented */VOIDNTAPIKeGenericCallDpc(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 */VOIDNTAPIKeSignalCallDpcDone(IN PVOID SystemArgument1){ // // Decrement the barrier, which is actually the processor count // InterlockedDecrement((PLONG)SystemArgument1);}
/* * @implemented */BOOLEANNTAPIKeSignalCallDpcSynchronize(IN PVOID SystemArgument2){ // // There is nothing to do on UP systems -- the processor calling this wins // UNREFERENCED_PARAMETER(SystemArgument2); return TRUE;}
/* EOF */