2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ke/dpc.c
5 * PURPOSE: Deferred Procedure Call (DPC) Support
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 * Philip Susi (phreak@iag.net)
11 /* INCLUDES ******************************************************************/
17 /* GLOBALS *******************************************************************/
19 ULONG KiMaximumDpcQueueDepth
= 4;
20 ULONG KiMinimumDpcRate
= 3;
21 ULONG KiAdjustDpcThreshold
= 20;
22 ULONG KiIdealDpcRate
= 20;
23 BOOLEAN KeThreadDpcEnable
;
24 FAST_MUTEX KiGenericCallDpcMutex
;
25 KDPC KiTimerExpireDpc
;
26 ULONG KiTimeLimitIsrMicroseconds
;
27 ULONG KiDPCTimeout
= 110;
29 /* PRIVATE FUNCTIONS *********************************************************/
33 KiCheckTimerTable(IN ULARGE_INTEGER CurrentTime
)
37 PLIST_ENTRY ListHead
, NextEntry
;
41 /* Raise IRQL to high and loop timers */
42 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
45 /* Loop the current list */
46 ListHead
= &KiTimerTableListHead
[i
].Entry
;
47 NextEntry
= ListHead
->Flink
;
48 while (NextEntry
!= ListHead
)
50 /* Get the timer and move to the next one */
51 Timer
= CONTAINING_RECORD(NextEntry
, KTIMER
, TimerListEntry
);
52 NextEntry
= NextEntry
->Flink
;
54 /* Check if it expired */
55 if (Timer
->DueTime
.QuadPart
<= CurrentTime
.QuadPart
)
57 /* Check if the DPC was queued, but didn't run */
58 if (!(KeGetCurrentPrcb()->TimerRequest
) &&
59 !(*((volatile PULONG
*)(&KiTimerExpireDpc
.DpcData
))))
61 /* This is bad, breakpoint! */
62 DPRINT1("Invalid timer state!\n");
68 /* Move to the next timer */
70 } while(i
< TIMER_TABLE_SIZE
);
72 /* Lower IRQL and return */
79 KiTimerExpiration(IN PKDPC Dpc
,
80 IN PVOID DeferredContext
,
81 IN PVOID SystemArgument1
,
82 IN PVOID SystemArgument2
)
84 ULARGE_INTEGER SystemTime
, InterruptTime
;
85 LARGE_INTEGER Interval
;
87 ULONG Timers
, ActiveTimers
, DpcCalls
;
88 PLIST_ENTRY ListHead
, NextEntry
;
93 DPC_QUEUE_ENTRY DpcEntry
[MAX_TIMER_DPCS
];
94 PKSPIN_LOCK_QUEUE LockQueue
;
95 PKPRCB Prcb
= KeGetCurrentPrcb();
97 /* Disable interrupts */
100 /* Query system and interrupt time */
101 KeQuerySystemTime((PLARGE_INTEGER
)&SystemTime
);
102 InterruptTime
.QuadPart
= KeQueryInterruptTime();
103 Limit
= KeTickCount
.LowPart
;
105 /* Bring interrupts back */
108 /* Get the index of the timer and normalize it */
109 Index
= PtrToLong(SystemArgument1
);
110 if ((Limit
- Index
) >= TIMER_TABLE_SIZE
)
113 Limit
= Index
+ TIMER_TABLE_SIZE
- 1;
116 /* Setup index and actual limit */
118 Limit
&= (TIMER_TABLE_SIZE
- 1);
120 /* Setup accounting data */
125 /* Lock the Database and Raise IRQL */
126 OldIrql
= KiAcquireDispatcherLock();
128 /* Start expiration loop */
131 /* Get the current index */
132 Index
= (Index
+ 1) & (TIMER_TABLE_SIZE
- 1);
134 /* Get list pointers and loop the list */
135 ListHead
= &KiTimerTableListHead
[Index
].Entry
;
136 while (ListHead
!= ListHead
->Flink
)
138 /* Lock the timer and go to the next entry */
139 LockQueue
= KiAcquireTimerLock(Index
);
140 NextEntry
= ListHead
->Flink
;
142 /* Get the current timer and check its due time */
144 Timer
= CONTAINING_RECORD(NextEntry
, KTIMER
, TimerListEntry
);
145 if ((NextEntry
!= ListHead
) &&
146 (Timer
->DueTime
.QuadPart
<= InterruptTime
.QuadPart
))
148 /* It's expired, remove it */
150 KiRemoveEntryTimer(Timer
);
152 /* Make it non-inserted, unlock it, and signal it */
153 Timer
->Header
.Inserted
= FALSE
;
154 KiReleaseTimerLock(LockQueue
);
155 Timer
->Header
.SignalState
= 1;
157 /* Get the DPC and period */
158 TimerDpc
= Timer
->Dpc
;
159 Period
= Timer
->Period
;
161 /* Check if there's any waiters */
162 if (!IsListEmpty(&Timer
->Header
.WaitListHead
))
164 /* Check the type of event */
165 if (Timer
->Header
.Type
== TimerNotificationObject
)
167 /* Unwait the thread */
168 KxUnwaitThread(&Timer
->Header
, IO_NO_INCREMENT
);
172 /* Otherwise unwait the thread and signal the timer */
173 KxUnwaitThreadForEvent((PKEVENT
)Timer
, IO_NO_INCREMENT
);
177 /* Check if we have a period */
180 /* Calculate the interval and insert the timer */
181 Interval
.QuadPart
= Int32x32To64(Period
, -10000);
182 while (!KiInsertTreeTimer(Timer
, Interval
));
185 /* Check if we have a DPC */
190 * If the DPC is targeted to another processor,
191 * then insert it into that processor's DPC queue
192 * instead of delivering it now.
193 * If the DPC is a threaded DPC, and the current CPU
194 * has threaded DPCs enabled (KiExecuteDpc is actively parsing DPCs),
195 * then also insert it into the DPC queue for threaded delivery,
196 * instead of doing it here.
198 if (((TimerDpc
->Number
>= MAXIMUM_PROCESSORS
) &&
199 ((TimerDpc
->Number
- MAXIMUM_PROCESSORS
) != Prcb
->Number
)) ||
200 ((TimerDpc
->Type
== ThreadedDpcObject
) && (Prcb
->ThreadDpcEnable
)))
203 KeInsertQueueDpc(TimerDpc
,
204 UlongToPtr(SystemTime
.LowPart
),
205 UlongToPtr(SystemTime
.HighPart
));
210 /* Setup the DPC Entry */
211 DpcEntry
[DpcCalls
].Dpc
= TimerDpc
;
212 DpcEntry
[DpcCalls
].Routine
= TimerDpc
->DeferredRoutine
;
213 DpcEntry
[DpcCalls
].Context
= TimerDpc
->DeferredContext
;
215 ASSERT(DpcCalls
< MAX_TIMER_DPCS
);
219 /* Check if we're done processing */
220 if (!(ActiveTimers
) || !(Timers
))
222 /* Release the dispatcher while doing DPCs */
223 KiReleaseDispatcherLock(DISPATCH_LEVEL
);
225 /* Start looping all DPC Entries */
226 for (i
= 0; DpcCalls
; DpcCalls
--, i
++)
230 Prcb
->DebugDpcTime
= 0;
234 DpcEntry
[i
].Routine(DpcEntry
[i
].Dpc
,
236 UlongToPtr(SystemTime
.LowPart
),
237 UlongToPtr(SystemTime
.HighPart
));
240 /* Reset accounting */
244 /* Lock the dispatcher database */
245 KiAcquireDispatcherLock();
250 /* Check if the timer list is empty */
251 if (NextEntry
!= ListHead
)
254 ASSERT(KiTimerTableListHead
[Index
].Time
.QuadPart
<=
255 Timer
->DueTime
.QuadPart
);
257 /* Update the time */
259 KiTimerTableListHead
[Index
].Time
.QuadPart
=
260 Timer
->DueTime
.QuadPart
;
264 /* Release the lock */
265 KiReleaseTimerLock(LockQueue
);
267 /* Check if we've scanned all the timers we could */
270 /* Release the dispatcher while doing DPCs */
271 KiReleaseDispatcherLock(DISPATCH_LEVEL
);
273 /* Start looping all DPC Entries */
274 for (i
= 0; DpcCalls
; DpcCalls
--, i
++)
278 Prcb
->DebugDpcTime
= 0;
282 DpcEntry
[i
].Routine(DpcEntry
[i
].Dpc
,
284 UlongToPtr(SystemTime
.LowPart
),
285 UlongToPtr(SystemTime
.HighPart
));
288 /* Reset accounting */
292 /* Lock the dispatcher database */
293 KiAcquireDispatcherLock();
300 } while (Index
!= Limit
);
302 /* Verify the timer table, on debug builds */
303 if (KeNumberProcessors
== 1) KiCheckTimerTable(InterruptTime
);
305 /* Check if we still have DPC entries */
308 /* Release the dispatcher while doing DPCs */
309 KiReleaseDispatcherLock(DISPATCH_LEVEL
);
311 /* Start looping all DPC Entries */
312 for (i
= 0; DpcCalls
; DpcCalls
--, i
++)
316 Prcb
->DebugDpcTime
= 0;
320 DpcEntry
[i
].Routine(DpcEntry
[i
].Dpc
,
322 UlongToPtr(SystemTime
.LowPart
),
323 UlongToPtr(SystemTime
.HighPart
));
326 /* Lower IRQL if we need to */
327 if (OldIrql
!= DISPATCH_LEVEL
) KeLowerIrql(OldIrql
);
331 /* Unlock the dispatcher */
332 KiReleaseDispatcherLock(OldIrql
);
338 KiTimerListExpire(IN PLIST_ENTRY ExpiredListHead
,
341 ULARGE_INTEGER SystemTime
;
342 LARGE_INTEGER Interval
;
348 DPC_QUEUE_ENTRY DpcEntry
[MAX_TIMER_DPCS
];
349 PKPRCB Prcb
= KeGetCurrentPrcb();
352 KeQuerySystemTime((PLARGE_INTEGER
)&SystemTime
);
354 /* Loop expired list */
355 while (ExpiredListHead
->Flink
!= ExpiredListHead
)
357 /* Get the current timer */
358 Timer
= CONTAINING_RECORD(ExpiredListHead
->Flink
, KTIMER
, TimerListEntry
);
361 RemoveEntryList(&Timer
->TimerListEntry
);
364 Timer
->Header
.Inserted
= FALSE
;
367 Timer
->Header
.SignalState
= 1;
369 /* Get the DPC and period */
370 TimerDpc
= Timer
->Dpc
;
371 Period
= Timer
->Period
;
373 /* Check if there's any waiters */
374 if (!IsListEmpty(&Timer
->Header
.WaitListHead
))
376 /* Check the type of event */
377 if (Timer
->Header
.Type
== TimerNotificationObject
)
379 /* Unwait the thread */
380 KxUnwaitThread(&Timer
->Header
, IO_NO_INCREMENT
);
384 /* Otherwise unwait the thread and signal the timer */
385 KxUnwaitThreadForEvent((PKEVENT
)Timer
, IO_NO_INCREMENT
);
389 /* Check if we have a period */
392 /* Calculate the interval and insert the timer */
393 Interval
.QuadPart
= Int32x32To64(Period
, -10000);
394 while (!KiInsertTreeTimer(Timer
, Interval
));
397 /* Check if we have a DPC */
402 * If the DPC is targeted to another processor,
403 * then insert it into that processor's DPC queue
404 * instead of delivering it now.
405 * If the DPC is a threaded DPC, and the current CPU
406 * has threaded DPCs enabled (KiExecuteDpc is actively parsing DPCs),
407 * then also insert it into the DPC queue for threaded delivery,
408 * instead of doing it here.
410 if (((TimerDpc
->Number
>= MAXIMUM_PROCESSORS
) &&
411 ((TimerDpc
->Number
- MAXIMUM_PROCESSORS
) != Prcb
->Number
)) ||
412 ((TimerDpc
->Type
== ThreadedDpcObject
) && (Prcb
->ThreadDpcEnable
)))
415 KeInsertQueueDpc(TimerDpc
,
416 UlongToPtr(SystemTime
.LowPart
),
417 UlongToPtr(SystemTime
.HighPart
));
422 /* Setup the DPC Entry */
423 DpcEntry
[DpcCalls
].Dpc
= TimerDpc
;
424 DpcEntry
[DpcCalls
].Routine
= TimerDpc
->DeferredRoutine
;
425 DpcEntry
[DpcCalls
].Context
= TimerDpc
->DeferredContext
;
427 ASSERT(DpcCalls
< MAX_TIMER_DPCS
);
432 /* Check if we still have DPC entries */
435 /* Release the dispatcher while doing DPCs */
436 KiReleaseDispatcherLock(DISPATCH_LEVEL
);
438 /* Start looping all DPC Entries */
439 for (i
= 0; DpcCalls
; DpcCalls
--, i
++)
443 Prcb
->DebugDpcTime
= 0;
447 DpcEntry
[i
].Routine(DpcEntry
[i
].Dpc
,
449 UlongToPtr(SystemTime
.LowPart
),
450 UlongToPtr(SystemTime
.HighPart
));
454 KeLowerIrql(OldIrql
);
458 /* Unlock the dispatcher */
459 KiReleaseDispatcherLock(OldIrql
);
467 PKPRCB Prcb
= KeGetCurrentPrcb();
468 PKTHREAD NextThread
, Thread
= Prcb
->CurrentThread
;
470 /* Check if a DPC Event was requested to be signaled */
471 if (InterlockedExchange(&Prcb
->DpcSetEventRequest
, 0))
474 KeSetEvent(&Prcb
->DpcEvent
, 0, 0);
477 /* Raise to synchronization level and lock the PRCB and thread */
478 KeRaiseIrqlToSynchLevel();
479 KiAcquireThreadLock(Thread
);
480 KiAcquirePrcbLock(Prcb
);
482 /* Check if Quantum expired */
483 if (Thread
->Quantum
<= 0)
485 /* Check if we're real-time and with quantums disabled */
486 if ((Thread
->Priority
>= LOW_REALTIME_PRIORITY
) &&
487 (Thread
->ApcState
.Process
->DisableQuantum
))
489 /* Otherwise, set maximum quantum */
490 Thread
->Quantum
= MAX_QUANTUM
;
494 /* Reset the new Quantum */
495 Thread
->Quantum
= Thread
->QuantumReset
;
497 /* Calculate new priority */
498 Thread
->Priority
= KiComputeNewPriority(Thread
, 1);
500 /* Check if a new thread is scheduled */
501 if (!Prcb
->NextThread
)
503 /* Get a new ready thread */
504 NextThread
= KiSelectReadyThread(Thread
->Priority
, Prcb
);
507 /* Found one, set it on standby */
508 NextThread
->State
= Standby
;
509 Prcb
->NextThread
= NextThread
;
514 /* Otherwise, make sure that this thread doesn't get preempted */
515 Thread
->Preempted
= FALSE
;
520 /* Release the thread lock */
521 KiReleaseThreadLock(Thread
);
523 /* Check if there's no thread scheduled */
524 if (!Prcb
->NextThread
)
527 KiReleasePrcbLock(Prcb
);
528 KeLowerIrql(DISPATCH_LEVEL
);
532 /* Get the next thread now */
533 NextThread
= Prcb
->NextThread
;
535 /* Set current thread's swap busy to true */
536 KiSetThreadSwapBusy(Thread
);
538 /* Switch threads in PRCB */
539 Prcb
->NextThread
= NULL
;
540 Prcb
->CurrentThread
= NextThread
;
542 /* Set thread to running and the switch reason to Quantum End */
543 NextThread
->State
= Running
;
544 Thread
->WaitReason
= WrQuantumEnd
;
546 /* Queue it on the ready lists */
547 KxQueueReadyThread(Thread
, Prcb
);
549 /* Set wait IRQL to APC_LEVEL */
550 Thread
->WaitIrql
= APC_LEVEL
;
553 KiSwapContext(APC_LEVEL
, Thread
);
555 /* Lower IRQL back to DISPATCH_LEVEL */
556 KeLowerIrql(DISPATCH_LEVEL
);
561 KiRetireDpcList(IN PKPRCB Prcb
)
564 PLIST_ENTRY ListHead
, DpcEntry
;
566 PKDEFERRED_ROUTINE DeferredRoutine
;
567 PVOID DeferredContext
, SystemArgument1
, SystemArgument2
;
573 /* Get data and list variables before starting anything else */
574 DpcData
= &Prcb
->DpcData
[DPC_NORMAL
];
575 ListHead
= &DpcData
->DpcListHead
;
577 /* Main outer loop */
580 /* Set us as active */
581 Prcb
->DpcRoutineActive
= TRUE
;
583 /* Check if this is a timer expiration request */
584 if (Prcb
->TimerRequest
)
586 /* It is, get the timer hand and disable timer request */
587 TimerHand
= Prcb
->TimerHand
;
588 Prcb
->TimerRequest
= 0;
590 /* Expire timers with interrups enabled */
592 KiTimerExpiration(NULL
, NULL
, (PVOID
)TimerHand
, NULL
);
596 /* Loop while we have entries in the queue */
597 while (DpcData
->DpcQueueDepth
!= 0)
599 /* Lock the DPC data and get the DPC entry*/
600 KeAcquireSpinLockAtDpcLevel(&DpcData
->DpcLock
);
601 DpcEntry
= ListHead
->Flink
;
603 /* Make sure we have an entry */
604 if (DpcEntry
!= ListHead
)
606 /* Remove the DPC from the list */
607 RemoveEntryList(DpcEntry
);
608 Dpc
= CONTAINING_RECORD(DpcEntry
, KDPC
, DpcListEntry
);
610 /* Clear its DPC data and save its parameters */
612 DeferredRoutine
= Dpc
->DeferredRoutine
;
613 DeferredContext
= Dpc
->DeferredContext
;
614 SystemArgument1
= Dpc
->SystemArgument1
;
615 SystemArgument2
= Dpc
->SystemArgument2
;
617 /* Decrease the queue depth */
618 DpcData
->DpcQueueDepth
--;
622 Prcb
->DebugDpcTime
= 0;
625 /* Release the lock */
626 KeReleaseSpinLockFromDpcLevel(&DpcData
->DpcLock
);
628 /* Re-enable interrupts */
636 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
638 /* Disable interrupts and keep looping */
643 /* The queue should be flushed now */
644 ASSERT(DpcData
->DpcQueueDepth
== 0);
646 /* Release DPC Lock */
647 KeReleaseSpinLockFromDpcLevel(&DpcData
->DpcLock
);
651 /* Clear DPC Flags */
652 Prcb
->DpcRoutineActive
= FALSE
;
653 Prcb
->DpcInterruptRequested
= FALSE
;
656 /* Check if we have deferred threads */
657 if (Prcb
->DeferredReadyListHead
.Next
)
660 /* Re-enable interrupts and raise to synch */
662 OldIrql
= KeRaiseIrqlToSynchLevel();
664 /* Process deferred threads */
665 KiProcessDeferredReadyList(Prcb
);
667 /* Lower IRQL back and disable interrupts */
668 KeLowerIrql(OldIrql
);
672 } while (DpcData
->DpcQueueDepth
!= 0);
677 KiInitializeDpc(IN PKDPC Dpc
,
678 IN PKDEFERRED_ROUTINE DeferredRoutine
,
679 IN PVOID DeferredContext
,
682 /* Setup the DPC Object */
685 Dpc
->Importance
= MediumImportance
;
686 Dpc
->DeferredRoutine
= DeferredRoutine
;
687 Dpc
->DeferredContext
= DeferredContext
;
691 /* PUBLIC FUNCTIONS **********************************************************/
698 KeInitializeThreadedDpc(IN PKDPC Dpc
,
699 IN PKDEFERRED_ROUTINE DeferredRoutine
,
700 IN PVOID DeferredContext
)
702 /* Call the internal routine */
703 KiInitializeDpc(Dpc
, DeferredRoutine
, DeferredContext
, ThreadedDpcObject
);
711 KeInitializeDpc(IN PKDPC Dpc
,
712 IN PKDEFERRED_ROUTINE DeferredRoutine
,
713 IN PVOID DeferredContext
)
715 /* Call the internal routine */
716 KiInitializeDpc(Dpc
, DeferredRoutine
, DeferredContext
, DpcObject
);
724 KeInsertQueueDpc(IN PKDPC Dpc
,
725 IN PVOID SystemArgument1
,
726 IN PVOID SystemArgument2
)
729 PKPRCB Prcb
, CurrentPrcb
;
732 BOOLEAN DpcConfigured
= FALSE
, DpcInserted
= FALSE
;
735 /* Check IRQL and Raise it to HIGH_LEVEL */
736 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
737 CurrentPrcb
= KeGetCurrentPrcb();
739 /* Check if the DPC has more then the maximum number of CPUs */
740 if (Dpc
->Number
>= MAXIMUM_PROCESSORS
)
742 /* Then substract the maximum and get that PRCB. */
743 Cpu
= Dpc
->Number
- MAXIMUM_PROCESSORS
;
744 Prcb
= KiProcessorBlock
[Cpu
];
748 /* Use the current one */
753 /* ROS Sanity Check */
754 ASSERT(Prcb
== CurrentPrcb
);
756 /* Check if this is a threaded DPC and threaded DPCs are enabled */
757 if ((Dpc
->Type
== ThreadedDpcObject
) && (Prcb
->ThreadDpcEnable
))
759 /* Then use the threaded data */
760 DpcData
= &Prcb
->DpcData
[DPC_THREADED
];
764 /* Otherwise, use the regular data */
765 DpcData
= &Prcb
->DpcData
[DPC_NORMAL
];
768 /* Acquire the DPC lock */
769 KiAcquireSpinLock(&DpcData
->DpcLock
);
771 /* Get the DPC Data */
772 if (!InterlockedCompareExchangePointer(&Dpc
->DpcData
, DpcData
, NULL
))
774 /* Now we can play with the DPC safely */
775 Dpc
->SystemArgument1
= SystemArgument1
;
776 Dpc
->SystemArgument2
= SystemArgument2
;
777 DpcData
->DpcQueueDepth
++;
779 DpcConfigured
= TRUE
;
781 /* Check if this is a high importance DPC */
782 if (Dpc
->Importance
== HighImportance
)
784 /* Pre-empty other DPCs */
785 InsertHeadList(&DpcData
->DpcListHead
, &Dpc
->DpcListEntry
);
789 /* Add it at the end */
790 InsertTailList(&DpcData
->DpcListHead
, &Dpc
->DpcListEntry
);
793 /* Check if this is the DPC on the threaded list */
794 if (&Prcb
->DpcData
[DPC_THREADED
] == DpcData
)
796 /* Make sure a threaded DPC isn't already active */
797 if (!(Prcb
->DpcThreadActive
) && !(Prcb
->DpcThreadRequested
))
799 /* FIXME: Setup Threaded DPC */
800 UNIMPLEMENTED_FATAL("Threaded DPC not supported\n");
805 /* Make sure a DPC isn't executing already */
806 if (!(Prcb
->DpcRoutineActive
) && !(Prcb
->DpcInterruptRequested
))
808 /* Check if this is the same CPU */
809 if (Prcb
!= CurrentPrcb
)
812 * Check if the DPC is of high importance or above the
813 * maximum depth. If it is, then make sure that the CPU
814 * isn't idle, or that it's sleeping.
816 if (((Dpc
->Importance
== HighImportance
) ||
817 (DpcData
->DpcQueueDepth
>=
818 Prcb
->MaximumDpcQueueDepth
)) &&
819 (!(AFFINITY_MASK(Cpu
) & KiIdleSummary
) ||
822 /* Set interrupt requested */
823 Prcb
->DpcInterruptRequested
= TRUE
;
825 /* Set DPC inserted */
831 /* Check if the DPC is of anything but low importance */
832 if ((Dpc
->Importance
!= LowImportance
) ||
833 (DpcData
->DpcQueueDepth
>=
834 Prcb
->MaximumDpcQueueDepth
) ||
835 (Prcb
->DpcRequestRate
< Prcb
->MinimumDpcRate
))
837 /* Set interrupt requested */
838 Prcb
->DpcInterruptRequested
= TRUE
;
840 /* Set DPC inserted */
848 /* Release the lock */
849 KiReleaseSpinLock(&DpcData
->DpcLock
);
851 /* Check if the DPC was inserted */
854 /* Check if this was SMP */
855 if (Prcb
!= CurrentPrcb
)
857 /* It was, request and IPI */
858 KiIpiSend(AFFINITY_MASK(Cpu
), IPI_DPC
);
862 /* It wasn't, request an interrupt from HAL */
863 HalRequestSoftwareInterrupt(DISPATCH_LEVEL
);
868 KeLowerIrql(OldIrql
);
869 return DpcConfigured
;
877 KeRemoveQueueDpc(IN PKDPC Dpc
)
883 /* Disable interrupts */
884 Enable
= KeDisableInterrupts();
887 DpcData
= Dpc
->DpcData
;
890 /* Acquire the DPC lock */
891 KiAcquireSpinLock(&DpcData
->DpcLock
);
893 /* Make sure that the data didn't change */
894 if (DpcData
== Dpc
->DpcData
)
897 DpcData
->DpcQueueDepth
--;
898 RemoveEntryList(&Dpc
->DpcListEntry
);
902 /* Release the lock */
903 KiReleaseSpinLock(&DpcData
->DpcLock
);
906 /* Re-enable interrupts */
907 if (Enable
) _enable();
909 /* Return if the DPC was in the queue or not */
910 return DpcData
? TRUE
: FALSE
;
918 KeFlushQueuedDpcs(VOID
)
920 PKPRCB CurrentPrcb
= KeGetCurrentPrcb();
923 /* Check if this is an UP machine */
924 if (KeActiveProcessors
== 1)
926 /* Check if there are DPCs on either queues */
927 if ((CurrentPrcb
->DpcData
[DPC_NORMAL
].DpcQueueDepth
> 0) ||
928 (CurrentPrcb
->DpcData
[DPC_THREADED
].DpcQueueDepth
> 0))
930 /* Request an interrupt */
931 HalRequestSoftwareInterrupt(DISPATCH_LEVEL
);
936 /* FIXME: SMP support required */
946 KeIsExecutingDpc(VOID
)
948 /* Return if the Dpc Routine is active */
949 return KeGetCurrentPrcb()->DpcRoutineActive
;
957 KeSetImportanceDpc (IN PKDPC Dpc
,
958 IN KDPC_IMPORTANCE Importance
)
960 /* Set the DPC Importance */
962 Dpc
->Importance
= Importance
;
970 KeSetTargetProcessorDpc(IN PKDPC Dpc
,
973 /* Set a target CPU */
975 Dpc
->Number
= Number
+ MAXIMUM_PROCESSORS
;
983 KeGenericCallDpc(IN PKDEFERRED_ROUTINE Routine
,
986 ULONG Barrier
= KeNumberProcessors
;
988 DEFERRED_REVERSE_BARRIER ReverseBarrier
;
989 ASSERT(KeGetCurrentIrql () < DISPATCH_LEVEL
);
992 // The barrier is the number of processors, each processor will decrement it
993 // by one, so when all processors have run the DPC, the barrier reaches zero
995 ReverseBarrier
.Barrier
= Barrier
;
996 ReverseBarrier
.TotalProcessors
= Barrier
;
999 // But we don't need the barrier on UP, since we can simply call the routine
1000 // directly while at DISPATCH_LEVEL and not worry about anything else
1002 KeRaiseIrql(DISPATCH_LEVEL
, &OldIrql
);
1003 Routine(&KeGetCurrentPrcb()->CallDpc
, Context
, &Barrier
, &ReverseBarrier
);
1004 KeLowerIrql(OldIrql
);
1012 KeSignalCallDpcDone(IN PVOID SystemArgument1
)
1015 // Decrement the barrier, which is actually the processor count
1017 InterlockedDecrement((PLONG
)SystemArgument1
);
1025 KeSignalCallDpcSynchronize(IN PVOID SystemArgument2
)
1028 // There is nothing to do on UP systems -- the processor calling this wins
1030 UNREFERENCED_PARAMETER(SystemArgument2
);