3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/ke/dpc.c
6 * PURPOSE: Handle DPCs (Delayed Procedure Calls)
8 * PROGRAMMERS: David Welch (welch@mcmail.com)
9 * Philip Susi (phreak@iag.net)
10 * Eric Kohl (ekohl@abo.rhein-zeitung.de)
11 * Alex Ionescu (alex@relsoft.net)
15 * NOTE: See also the higher level support routines in ntoskrnl/io/dpc.c
18 /* INCLUDES ***************************************************************/
22 #include <internal/debug.h>
24 #if defined (ALLOC_PRAGMA)
25 #pragma alloc_text(INIT, KeInitDpc)
29 /* TYPES *******************************************************************/
31 #define MAX_QUANTUM 0x7F
33 /* FUNCTIONS ****************************************************************/
36 * FUNCTION: Initialize DPC handling
41 KeInitDpc(PKPRCB Prcb
)
43 InitializeListHead(&Prcb
->DpcData
[0].DpcListHead
);
47 * Prcb->DpcEvent is a NULL pointer.
49 KeInitializeEvent(Prcb
->DpcEvent
, 0, 0);
51 KeInitializeSpinLock(&Prcb
->DpcData
[0].DpcLock
);
52 Prcb
->MaximumDpcQueueDepth
= 4;
53 Prcb
->MinimumDpcRate
= 3;
54 Prcb
->DpcData
[0].DpcQueueDepth
= 0;
62 KeInitializeThreadedDpc(PKDPC Dpc
,
63 PKDEFERRED_ROUTINE DeferredRoutine
,
64 PVOID DeferredContext
)
67 * Initalizes a Threaded DPC and registers the DeferredRoutine for it.
69 * Dpc = Pointer to a caller supplied DPC to be initialized. The caller must allocate this memory.
70 * DeferredRoutine = Pointer to associated DPC callback routine.
71 * DeferredContext = Parameter to be passed to the callback routine.
72 * NOTE: Callers can be running at any IRQL.
75 DPRINT("Threaded DPC Initializing: %x with Routine: %x\n", Dpc
, DeferredRoutine
);
76 Dpc
->Type
= ThreadedDpcObject
;
78 Dpc
->Importance
= MediumImportance
;
79 Dpc
->DeferredRoutine
= DeferredRoutine
;
80 Dpc
->DeferredContext
= DeferredContext
;
88 * Initalizes a DPC and registers the DeferredRoutine for it.
90 * Dpc = Pointer to a caller supplied DPC to be initialized. The caller must allocate this memory.
91 * DeferredRoutine = Pointer to associated DPC callback routine.
92 * DeferredContext = Parameter to be passed to the callback routine.
93 * NOTE: Callers can be running at any IRQL.
97 KeInitializeDpc(PKDPC Dpc
,
98 PKDEFERRED_ROUTINE DeferredRoutine
,
99 PVOID DeferredContext
)
101 DPRINT("DPC Initializing: %x with Routine: %x\n", Dpc
, DeferredRoutine
);
102 Dpc
->Type
= DpcObject
;
104 Dpc
->Importance
= MediumImportance
;
105 Dpc
->DeferredRoutine
= DeferredRoutine
;
106 Dpc
->DeferredContext
= DeferredContext
;
114 * Queues a DPC for execution when the IRQL of a processor
115 * drops below DISPATCH_LEVEL
117 * Dpc = Pointed to a DPC Object Initalized by KeInitializeDpc.
118 * SystemArgument1 = Driver Determined context data
119 * SystemArgument2 = Driver Determined context data
121 * TRUE if the DPC object wasn't already in the queue
124 * If there is currently a DPC active on the target processor, or a DPC
125 * interrupt has already been requested on the target processor when a
126 * DPC is queued, then no further action is necessary. The DPC will be
127 * executed on the target processor when its queue entry is processed.
129 * If there is not a DPC active on the target processor and a DPC interrupt
130 * has not been requested on the target processor, then the exact treatment
131 * of the DPC is dependent on whether the host system is a UP system or an
136 * If the DPC is of medium or high importance, the current DPC queue depth
137 * is greater than the maximum target depth, or current DPC request rate is
138 * less the minimum target rate, then a DPC interrupt is requested on the
139 * host processor and the DPC will be processed when the interrupt occurs.
140 * Otherwise, no DPC interupt is requested and the DPC execution will be
141 * delayed until the DPC queue depth is greater that the target depth or the
142 * minimum DPC rate is less than the target rate.
146 * If the DPC is being queued to another processor and the depth of the DPC
147 * queue on the target processor is greater than the maximum target depth or
148 * the DPC is of high importance, then a DPC interrupt is requested on the
149 * target processor and the DPC will be processed when the interrupt occurs.
150 * Otherwise, the DPC execution will be delayed on the target processor until
151 * the DPC queue depth on the target processor is greater that the maximum
152 * target depth or the minimum DPC rate on the target processor is less than
153 * the target mimimum rate.
155 * If the DPC is being queued to the current processor and the DPC is not of
156 * low importance, the current DPC queue depth is greater than the maximum
157 * target depth, or the minimum DPC rate is less than the minimum target rate,
158 * then a DPC interrupt is request on the current processor and the DPV will
159 * be processed whne the interrupt occurs. Otherwise, no DPC interupt is
160 * requested and the DPC execution will be delayed until the DPC queue depth
161 * is greater that the target depth or the minimum DPC rate is less than the
166 KeInsertQueueDpc(PKDPC Dpc
,
167 PVOID SystemArgument1
,
168 PVOID SystemArgument2
)
173 DPRINT("KeInsertQueueDpc(DPC %x, SystemArgument1 %x, SystemArgument2 %x)\n",
174 Dpc
, SystemArgument1
, SystemArgument2
);
176 /* Check IRQL and Raise it to HIGH_LEVEL */
177 ASSERT(KeGetCurrentIrql()>=DISPATCH_LEVEL
);
178 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
180 /* Check if this is a Thread DPC, which we don't support (yet) */
181 if (Dpc
->Type
== ThreadedDpcObject
) {
183 KeLowerIrql(OldIrql
);
187 /* Get the right PCR for this CPU */
188 if (Dpc
->Number
>= MAXIMUM_PROCESSORS
) {
190 ASSERT (Dpc
->Number
- MAXIMUM_PROCESSORS
< KeNumberProcessors
);
191 Prcb
= ((PKPCR
)((ULONG_PTR
)KPCR_BASE
+ ((Dpc
->Number
- MAXIMUM_PROCESSORS
) * PAGE_SIZE
)))->Prcb
;
195 ASSERT (Dpc
->Number
< KeNumberProcessors
);
196 Prcb
= KeGetCurrentPrcb();
197 Dpc
->Number
= KeGetCurrentProcessorNumber();
200 KiAcquireSpinLock(&Prcb
->DpcData
[0].DpcLock
);
202 Prcb
= ((PKPCR
)KPCR_BASE
)->Prcb
;
205 /* Get the DPC Data */
206 if (InterlockedCompareExchangeUL(&Dpc
->DpcData
, &Prcb
->DpcData
[0].DpcLock
, 0)) {
208 DPRINT("DPC Already Inserted\n");
210 KiReleaseSpinLock(&Prcb
->DpcData
[0].DpcLock
);
212 KeLowerIrql(OldIrql
);
216 /* Make sure the lists are free if the Queue is 0 */
217 if (Prcb
->DpcData
[0].DpcQueueDepth
== 0) {
219 ASSERT(IsListEmpty(&Prcb
->DpcData
[0].DpcListHead
));
222 ASSERT(!IsListEmpty(&Prcb
->DpcData
[0].DpcListHead
));
225 /* Now we can play with the DPC safely */
226 Dpc
->SystemArgument1
=SystemArgument1
;
227 Dpc
->SystemArgument2
=SystemArgument2
;
228 Prcb
->DpcData
[0].DpcQueueDepth
++;
229 Prcb
->DpcData
[0].DpcCount
++;
231 /* Insert the DPC into the list. HighImportance DPCs go at the beginning */
232 if (Dpc
->Importance
== HighImportance
) {
234 InsertHeadList(&Prcb
->DpcData
[0].DpcListHead
, &Dpc
->DpcListEntry
);
237 InsertTailList(&Prcb
->DpcData
[0].DpcListHead
, &Dpc
->DpcListEntry
);
239 DPRINT("New DPC Added. Dpc->DpcListEntry.Flink %x\n", Dpc
->DpcListEntry
.Flink
);
241 /* Make sure a DPC isn't executing already and respect rules outlined above. */
242 if ((!Prcb
->DpcRoutineActive
) && (!Prcb
->DpcInterruptRequested
)) {
245 /* Check if this is the same CPU */
246 if (Prcb
!= KeGetCurrentPrcb()) {
248 /* Send IPI if High Importance */
249 if ((Dpc
->Importance
== HighImportance
) ||
250 (Prcb
->DpcData
[0].DpcQueueDepth
>= Prcb
->MaximumDpcQueueDepth
)) {
252 if (Dpc
->Number
>= MAXIMUM_PROCESSORS
) {
254 KiIpiSendRequest(1 << (Dpc
->Number
- MAXIMUM_PROCESSORS
), IPI_DPC
);
257 KiIpiSendRequest(1 << Dpc
->Number
, IPI_DPC
);
263 /* Request an Interrupt only if the DPC isn't low priority */
264 if ((Dpc
->Importance
!= LowImportance
) ||
265 (Prcb
->DpcData
[0].DpcQueueDepth
>= Prcb
->MaximumDpcQueueDepth
) ||
266 (Prcb
->DpcRequestRate
< Prcb
->MinimumDpcRate
)) {
268 /* Request Interrupt */
269 HalRequestSoftwareInterrupt(DISPATCH_LEVEL
);
270 Prcb
->DpcInterruptRequested
= TRUE
;
274 DPRINT("Requesting Interrupt. Importance: %x. QueueDepth: %x. MaxQueue: %x . RequestRate: %x. MinRate:%x \n", Dpc
->Importance
, Prcb
->DpcData
[0].DpcQueueDepth
, Prcb
->MaximumDpcQueueDepth
, Prcb
->DpcRequestRate
, Prcb
->MinimumDpcRate
);
276 /* Request an Interrupt only if the DPC isn't low priority */
277 if ((Dpc
->Importance
!= LowImportance
) ||
278 (Prcb
->DpcData
[0].DpcQueueDepth
>= Prcb
->MaximumDpcQueueDepth
) ||
279 (Prcb
->DpcRequestRate
< Prcb
->MinimumDpcRate
)) {
281 /* Request Interrupt */
282 DPRINT("Requesting Interrupt\n");
283 HalRequestSoftwareInterrupt(DISPATCH_LEVEL
);
284 Prcb
->DpcInterruptRequested
= TRUE
;
289 KiReleaseSpinLock(&Prcb
->DpcData
[0].DpcLock
);
292 KeLowerIrql(OldIrql
);
300 * Removes DPC object from the system dpc queue
302 * Dpc = Pointer to DPC to remove from the queue.
304 * TRUE if the DPC was in the queue
309 KeRemoveQueueDpc(PKDPC Dpc
)
315 DPRINT("Removing DPC: %x\n", Dpc
);
316 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
318 KiAcquireSpinLock(&((PKDPC_DATA
)Dpc
->DpcData
)->DpcLock
);
321 /* First make sure the DPC lock isn't being held */
322 WasInQueue
= Dpc
->DpcData
? TRUE
: FALSE
;
326 ((PKDPC_DATA
)Dpc
->DpcData
)->DpcQueueDepth
--;
327 RemoveEntryList(&Dpc
->DpcListEntry
);
331 KiReleaseSpinLock(&((PKDPC_DATA
)Dpc
->DpcData
)->DpcLock
);
334 /* Return if the DPC was in the queue or not */
335 KeLowerIrql(OldIrql
);
344 KeFlushQueuedDpcs(VOID
)
347 * Called to Deliver DPCs if any are pending.
349 * Called when deleting a Driver.
352 /* Request an interrupt if needed */
353 if (KeGetCurrentPrcb()->DpcData
[0].DpcQueueDepth
) HalRequestSoftwareInterrupt(DISPATCH_LEVEL
);
365 /* Return if the Dpc Routine is active */
366 return KeGetCurrentPrcb()->DpcRoutineActive
;
370 * FUNCTION: Specifies the DPCs importance
372 * Dpc = Initalizes DPC
373 * Importance = DPC importance
380 KeSetImportanceDpc (IN PKDPC Dpc
,
381 IN KDPC_IMPORTANCE Importance
)
383 /* Set the DPC Importance */
384 Dpc
->Importance
= Importance
;
390 * FUNCTION: Specifies on which processor the DPC will run
392 * Dpc = Initalizes DPC
393 * Number = Processor number
398 KeSetTargetProcessorDpc(IN PKDPC Dpc
,
401 /* Check how many CPUs are on the system */
402 if (Number
>= MAXIMUM_PROCESSORS
) {
409 /* Set the Number Specified */
410 ASSERT(Number
< KeNumberProcessors
);
411 Dpc
->Number
= Number
+ MAXIMUM_PROCESSORS
;
417 * Called when a quantum end occurs to check if priority should be changed
418 * and wether a new thread should be dispatched.
420 * Called when deleting a Driver.
427 PKTHREAD CurrentThread
;
430 KPRIORITY OldPriority
;
431 KPRIORITY NewPriority
;
433 /* Lock dispatcher, get current thread */
434 Prcb
= KeGetCurrentPrcb();
435 CurrentThread
= KeGetCurrentThread();
436 OldIrql
= KeRaiseIrqlToSynchLevel();
438 /* Get the Thread's Process */
439 Process
= CurrentThread
->ApcState
.Process
;
441 /* Set DPC Event if requested */
442 if (Prcb
->DpcSetEventRequest
) {
445 * Prcb->DpcEvent is not initialized.
448 KeSetEvent(Prcb
->DpcEvent
, 0, 0);
451 /* Check if Quantum expired */
452 if (CurrentThread
->Quantum
<= 0) {
454 /* Reset the new Quantum */
455 CurrentThread
->Quantum
= CurrentThread
->QuantumReset
;
457 /* Calculate new priority */
458 OldPriority
= CurrentThread
->Priority
;
459 if (OldPriority
< LOW_REALTIME_PRIORITY
) {
461 /* Set the New Priority and add the Priority Decrement */
462 NewPriority
= OldPriority
- CurrentThread
->PriorityDecrement
- 1;
464 /* Don't go out of bounds */
465 if (NewPriority
< CurrentThread
->BasePriority
) NewPriority
= CurrentThread
->BasePriority
;
467 /* Reset the priority decrement */
468 CurrentThread
->PriorityDecrement
= 0;
470 /* Set a new priority if needed */
471 if (OldPriority
!= NewPriority
) {
473 /* Set new Priority */
474 BOOLEAN Dummy
; /* <- This is a hack anyways... */
475 KiSetPriorityThread(CurrentThread
, NewPriority
, &Dummy
);
479 /* Queue new thread if none is already */
480 if (Prcb
->NextThread
== NULL
) {
482 /* FIXME: Schedule a New Thread, when ROS will have NT Scheduler */
486 /* Make the current thread non-premeptive if a new thread is queued */
487 CurrentThread
->Preempted
= FALSE
;
493 /* Set the Quantum back to Maximum */
494 //if (CurrentThread->DisableQuantum) {
495 // CurrentThread->Quantum = MAX_QUANTUM;
500 /* Dispatch the Thread */
501 KeLowerIrql(DISPATCH_LEVEL
);
502 KiDispatchThread(Ready
);
509 * Called whenever a system interrupt is generated at DISPATCH_LEVEL.
510 * It delivers queued DPCs and dispatches a new thread if need be.
514 KiDispatchInterrupt(VOID
)
516 PLIST_ENTRY DpcEntry
;
521 DPRINT("Dispatching Interrupts\n");
522 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
524 /* Set DPC Deliver to Active */
525 Prcb
= KeGetCurrentPrcb();
527 if (Prcb
->DpcData
[0].DpcQueueDepth
> 0) {
529 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
531 KiAcquireSpinLock(&Prcb
->DpcData
[0].DpcLock
);
533 Prcb
->DpcRoutineActive
= TRUE
;
535 DPRINT("&Prcb->DpcData[0].DpcListHead: %x\n", &Prcb
->DpcData
[0].DpcListHead
);
536 /* Loop while we have entries */
537 while (!IsListEmpty(&Prcb
->DpcData
[0].DpcListHead
)) {
539 ASSERT(Prcb
->DpcData
[0].DpcQueueDepth
> 0);
540 DPRINT("Queue Depth: %x\n", Prcb
->DpcData
[0].DpcQueueDepth
);
542 /* Get the DPC call it */
543 DpcEntry
= RemoveHeadList(&Prcb
->DpcData
[0].DpcListHead
);
544 Dpc
= CONTAINING_RECORD(DpcEntry
, KDPC
, DpcListEntry
);
545 DPRINT("Dpc->DpcListEntry.Flink %x\n", Dpc
->DpcListEntry
.Flink
);
547 Prcb
->DpcData
[0].DpcQueueDepth
--;
549 KiReleaseSpinLock(&Prcb
->DpcData
[0].DpcLock
);
551 /* Disable/Enabled Interrupts and Call the DPC */
552 KeLowerIrql(OldIrql
);
553 DPRINT("Calling DPC: %x\n", Dpc
);
554 Dpc
->DeferredRoutine(Dpc
,
555 Dpc
->DeferredContext
,
556 Dpc
->SystemArgument1
,
557 Dpc
->SystemArgument2
);
558 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
561 KiAcquireSpinLock(&Prcb
->DpcData
[0].DpcLock
);
563 * If the dpc routine drops the irql below DISPATCH_LEVEL,
564 * a thread switch can occur and after the next thread switch
565 * the execution may start on an other processor.
567 if (Prcb
!= KeGetCurrentPrcb()) {
569 Prcb
->DpcRoutineActive
= FALSE
;
570 KiReleaseSpinLock(&Prcb
->DpcData
[0].DpcLock
);
571 Prcb
= KeGetCurrentPrcb();
572 KiAcquireSpinLock(&Prcb
->DpcData
[0].DpcLock
);
573 Prcb
->DpcRoutineActive
= TRUE
;
577 /* Clear DPC Flags */
578 Prcb
->DpcRoutineActive
= FALSE
;
579 Prcb
->DpcInterruptRequested
= FALSE
;
581 KiReleaseSpinLock(&Prcb
->DpcData
[0].DpcLock
);
584 /* DPC Dispatching Ended, re-enable interrupts */
585 KeLowerIrql(OldIrql
);
588 DPRINT("Checking for Quantum End\n");
590 /* If we have Quantum End, call the function */
591 if (Prcb
->QuantumEnd
) {
593 Prcb
->QuantumEnd
= FALSE
;