3 * Copyright (C) 2000, 1999, 1998 David Welch <welch@cwcom.net>,
4 * Philip Susi <phreak@iag.net>,
5 * Eric Kohl <ekohl@abo.rhein-zeitung.de>
6 * Alex Ionescu <alex@relsoft.net>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 /* $Id: dpc.c,v 1.49 2004/11/27 19:27:31 hbirr Exp $
24 * COPYRIGHT: See COPYING in the top level directory
25 * PROJECT: ReactOS kernel
26 * FILE: ntoskrnl/ke/dpc.c
27 * PURPOSE: Handle DPCs (Delayed Procedure Calls)
28 * PROGRAMMER: David Welch (welch@mcmail.com)
31 * 12/3/99: Phillip Susi: Fixed IRQL problem
32 * 12/11/04: Alex Ionescu - Major rewrite.
36 * NOTE: See also the higher level support routines in ntoskrnl/io/dpc.c
39 /* INCLUDES ***************************************************************/
43 #include <internal/debug.h>
45 /* TYPES *******************************************************************/
47 #define MAX_QUANTUM 0x7F
48 /* GLOBALS ******************************************************************/
50 /* FUNCTIONS ****************************************************************/
55 * FUNCTION: Initialize DPC handling
58 InitializeListHead(&Pcr
->PrcbData
.DpcData
[0].DpcListHead
);
59 KeInitializeEvent(Pcr
->PrcbData
.DpcEvent
, 0, 0);
60 KeInitializeSpinLock(&Pcr
->PrcbData
.DpcData
[0].DpcLock
);
61 Pcr
->PrcbData
.MaximumDpcQueueDepth
= 4;
62 Pcr
->PrcbData
.MinimumDpcRate
= 3;
63 Pcr
->PrcbData
.DpcData
[0].DpcQueueDepth
= 0;
71 KeInitializeThreadedDpc(PKDPC Dpc
,
72 PKDEFERRED_ROUTINE DeferredRoutine
,
73 PVOID DeferredContext
)
76 * Initalizes a Threaded DPC and registers the DeferredRoutine for it.
78 * Dpc = Pointer to a caller supplied DPC to be initialized. The caller must allocate this memory.
79 * DeferredRoutine = Pointer to associated DPC callback routine.
80 * DeferredContext = Parameter to be passed to the callback routine.
81 * NOTE: Callers can be running at any IRQL.
84 DPRINT("Threaded DPC Initializing: %x with Routine: %x\n", Dpc
, DeferredRoutine
);
85 //Dpc->Type = KThreadedDpc;
87 Dpc
->Importance
= MediumImportance
;
88 Dpc
->DeferredRoutine
= DeferredRoutine
;
89 Dpc
->DeferredContext
= DeferredContext
;
98 KeInitializeDpc (PKDPC Dpc
,
99 PKDEFERRED_ROUTINE DeferredRoutine
,
100 PVOID DeferredContext
)
103 * Initalizes a DPC and registers the DeferredRoutine for it.
105 * Dpc = Pointer to a caller supplied DPC to be initialized. The caller must allocate this memory.
106 * DeferredRoutine = Pointer to associated DPC callback routine.
107 * DeferredContext = Parameter to be passed to the callback routine.
108 * NOTE: Callers can be running at any IRQL.
111 DPRINT("DPC Initializing: %x with Routine: %x\n", Dpc
, DeferredRoutine
);
114 Dpc
->Importance
= MediumImportance
;
115 Dpc
->DeferredRoutine
= DeferredRoutine
;
116 Dpc
->DeferredContext
= DeferredContext
;
124 KeInsertQueueDpc (PKDPC Dpc
,
125 PVOID SystemArgument1
,
126 PVOID SystemArgument2
)
129 * Queues a DPC for execution when the IRQL of a processor
130 * drops below DISPATCH_LEVEL
132 * Dpc = Pointed to a DPC Object Initalized by KeInitializeDpc.
133 * SystemArgument1 = Driver Determined context data
134 * SystemArgument2 = Driver Determined context data
136 * TRUE if the DPC object wasn't already in the queue
139 * If there is currently a DPC active on the target processor, or a DPC
140 * interrupt has already been requested on the target processor when a
141 * DPC is queued, then no further action is necessary. The DPC will be
142 * executed on the target processor when its queue entry is processed.
144 * If there is not a DPC active on the target processor and a DPC interrupt
145 * has not been requested on the target processor, then the exact treatment
146 * of the DPC is dependent on whether the host system is a UP system or an
151 * If the DPC is of medium or high importance, the current DPC queue depth
152 * is greater than the maximum target depth, or current DPC request rate is
153 * less the minimum target rate, then a DPC interrupt is requested on the
154 * host processor and the DPC will be processed when the interrupt occurs.
155 * Otherwise, no DPC interupt is requested and the DPC execution will be
156 * delayed until the DPC queue depth is greater that the target depth or the
157 * minimum DPC rate is less than the target rate.
161 * If the DPC is being queued to another processor and the depth of the DPC
162 * queue on the target processor is greater than the maximum target depth or
163 * the DPC is of high importance, then a DPC interrupt is requested on the
164 * target processor and the DPC will be processed when the interrupt occurs.
165 * Otherwise, the DPC execution will be delayed on the target processor until
166 * the DPC queue depth on the target processor is greater that the maximum
167 * target depth or the minimum DPC rate on the target processor is less than
168 * the target mimimum rate.
170 * If the DPC is being queued to the current processor and the DPC is not of
171 * low importance, the current DPC queue depth is greater than the maximum
172 * target depth, or the minimum DPC rate is less than the minimum target rate,
173 * then a DPC interrupt is request on the current processor and the DPV will
174 * be processed whne the interrupt occurs. Otherwise, no DPC interupt is
175 * requested and the DPC execution will be delayed until the DPC queue depth
176 * is greater that the target depth or the minimum DPC rate is less than the
183 DPRINT("KeInsertQueueDpc(DPC %x, SystemArgument1 %x, SystemArgument2 %x)\n",
184 Dpc
, SystemArgument1
, SystemArgument2
);
186 /* Check IRQL and Raise it to HIGH_LEVEL */
187 ASSERT(KeGetCurrentIrql()>=DISPATCH_LEVEL
);
188 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
190 /* Check if this is a Thread DPC, which we don't support (yet) */
191 //if (Dpc->Type == KThreadedDpc) {
193 // KeLowerIrql(OldIrql);
197 /* Get the right PCR for this CPU */
198 if (Dpc
->Number
>= MAXIMUM_PROCESSORS
) {
199 ASSERT (Dpc
->Number
- MAXIMUM_PROCESSORS
< KeNumberProcessors
);
200 Pcr
= (PKPCR
)(KPCR_BASE
+ (Dpc
->Number
- MAXIMUM_PROCESSORS
) * PAGE_SIZE
);
202 ASSERT (Dpc
->Number
< KeNumberProcessors
);
203 Pcr
= KeGetCurrentKPCR();
204 Dpc
->Number
= KeGetCurrentProcessorNumber();
206 KiAcquireSpinLock(&Pcr
->PrcbData
.DpcData
[0].DpcLock
);
208 Pcr
= (PKPCR
)KPCR_BASE
;
211 /* Get the DPC Data */
212 if (InterlockedCompareExchange((LONG
*)&Dpc
->DpcData
, (LONG
)&Pcr
->PrcbData
.DpcData
[0].DpcLock
, 0)) {
213 DPRINT("DPC Already Inserted");
215 KiReleaseSpinLock(&Pcr
->PrcbData
.DpcData
[0].DpcLock
);
217 KeLowerIrql(OldIrql
);
221 /* Make sure the lists are free if the Queue is 0 */
222 if (Pcr
->PrcbData
.DpcData
[0].DpcQueueDepth
== 0) {
223 ASSERT(IsListEmpty(&Pcr
->PrcbData
.DpcData
[0].DpcListHead
));
225 ASSERT(!IsListEmpty(&Pcr
->PrcbData
.DpcData
[0].DpcListHead
));
228 /* Now we can play with the DPC safely */
229 Dpc
->SystemArgument1
=SystemArgument1
;
230 Dpc
->SystemArgument2
=SystemArgument2
;
231 Pcr
->PrcbData
.DpcData
[0].DpcQueueDepth
++;
232 Pcr
->PrcbData
.DpcData
[0].DpcCount
++;
234 /* Insert the DPC into the list. HighImportance DPCs go at the beginning */
235 if (Dpc
->Importance
== HighImportance
) {
236 InsertHeadList(&Pcr
->PrcbData
.DpcData
[0].DpcListHead
, &Dpc
->DpcListEntry
);
238 InsertTailList(&Pcr
->PrcbData
.DpcData
[0].DpcListHead
, &Dpc
->DpcListEntry
);
240 DPRINT("New DPC Added. Dpc->DpcListEntry.Flink %x\n", Dpc
->DpcListEntry
.Flink
);
242 /* Make sure a DPC isn't executing already and respect rules outlined above. */
243 if ((!Pcr
->PrcbData
.DpcRoutineActive
) && (!Pcr
->PrcbData
.DpcInterruptRequested
)) {
246 /* Check if this is the same CPU */
247 if (Pcr
!= KeGetCurrentKPCR()) {
248 /* Send IPI if High Importance */
249 if ((Dpc
->Importance
== HighImportance
) ||
250 (Pcr
->PrcbData
.DpcData
[0].DpcQueueDepth
>= Pcr
->PrcbData
.MaximumDpcQueueDepth
)) {
251 if (Dpc
->Number
>= MAXIMUM_PROCESSORS
) {
252 KiIpiSendRequest(1 << (Dpc
->Number
- MAXIMUM_PROCESSORS
), IPI_REQUEST_DPC
);
254 KiIpiSendRequest(1 << Dpc
->Number
, IPI_REQUEST_DPC
);
259 /* Request an Interrupt only if the DPC isn't low priority */
260 if ((Dpc
->Importance
!= LowImportance
) ||
261 (Pcr
->PrcbData
.DpcData
[0].DpcQueueDepth
>= Pcr
->PrcbData
.MaximumDpcQueueDepth
) ||
262 (Pcr
->PrcbData
.DpcRequestRate
< Pcr
->PrcbData
.MinimumDpcRate
)) {
264 /* Request Interrupt */
265 HalRequestSoftwareInterrupt(DISPATCH_LEVEL
);
266 Pcr
->PrcbData
.DpcInterruptRequested
= TRUE
;
270 DPRINT("Requesting Interrupt. Importance: %x. QueueDepth: %x. MaxQueue: %x . RequestRate: %x. MinRate:%x \n", Dpc
->Importance
, Pcr
->PrcbData
.DpcData
[0].DpcQueueDepth
, Pcr
->PrcbData
.MaximumDpcQueueDepth
, Pcr
->PrcbData
.DpcRequestRate
, Pcr
->PrcbData
.MinimumDpcRate
);
271 /* Request an Interrupt only if the DPC isn't low priority */
272 if ((Dpc
->Importance
!= LowImportance
) ||
273 (Pcr
->PrcbData
.DpcData
[0].DpcQueueDepth
>= Pcr
->PrcbData
.MaximumDpcQueueDepth
) ||
274 (Pcr
->PrcbData
.DpcRequestRate
< Pcr
->PrcbData
.MinimumDpcRate
)) {
276 /* Request Interrupt */
277 DPRINT("Requesting Interrupt\n");
278 HalRequestSoftwareInterrupt(DISPATCH_LEVEL
);
279 Pcr
->PrcbData
.DpcInterruptRequested
= TRUE
;
284 KiReleaseSpinLock(&Pcr
->PrcbData
.DpcData
[0].DpcLock
);
287 KeLowerIrql(OldIrql
);
295 KeRemoveQueueDpc (PKDPC Dpc
)
298 * Removes DPC object from the system dpc queue
300 * Dpc = Pointer to DPC to remove from the queue.
302 * TRUE if the DPC was in the queue
310 DPRINT("Removing DPC: %x\n", Dpc
);
311 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
313 KiAcquireSpinLock(&((PKDPC_DATA
)Dpc
->DpcData
)->DpcLock
);
316 /* First make sure the DPC lock isn't being held */
317 WasInQueue
= Dpc
->DpcData
? TRUE
: FALSE
;
321 ((PKDPC_DATA
)Dpc
->DpcData
)->DpcQueueDepth
--;
322 RemoveEntryList(&Dpc
->DpcListEntry
);
326 KiReleaseSpinLock(&((PKDPC_DATA
)Dpc
->DpcData
)->DpcLock
);
329 /* Return if the DPC was in the queue or not */
330 KeLowerIrql(OldIrql
);
339 KeFlushQueuedDpcs(VOID
)
342 * Called to Deliver DPCs if any are pending.
344 * Called when deleting a Driver.
347 if (KeGetCurrentKPCR()->PrcbData
.DpcData
[0].DpcQueueDepth
) HalRequestSoftwareInterrupt(DISPATCH_LEVEL
);
359 return KeGetCurrentKPCR()->PrcbData
.DpcRoutineActive
;
363 * FUNCTION: Specifies the DPCs importance
365 * Dpc = Initalizes DPC
366 * Importance = DPC importance
373 KeSetImportanceDpc (IN PKDPC Dpc
,
374 IN KDPC_IMPORTANCE Importance
)
376 Dpc
->Importance
= Importance
;
380 * FUNCTION: Specifies on which processor the DPC will run
382 * Dpc = Initalizes DPC
383 * Number = Processor number
389 KeSetTargetProcessorDpc (IN PKDPC Dpc
,
392 if (Number
>= MAXIMUM_PROCESSORS
)
398 ASSERT(Number
< KeNumberProcessors
);
399 Dpc
->Number
= Number
+ MAXIMUM_PROCESSORS
;
408 * Called when a quantum end occurs to check if priority should be changed
409 * and wether a new thread should be dispatched.
411 * Called when deleting a Driver.
415 PKTHREAD CurrentThread
;
418 KPRIORITY OldPriority
;
419 KPRIORITY NewPriority
;
421 /* Lock dispatcher, get current thread */
422 Prcb
= KeGetCurrentKPCR()->PrcbData
;
423 CurrentThread
= KeGetCurrentThread();
424 OldIrql
= KeRaiseIrqlToSynchLevel();
426 /* Get the Thread's Process */
427 Process
= CurrentThread
->ApcState
.Process
;
429 /* Set DPC Event if requested */
430 if (Prcb
.DpcSetEventRequest
) {
431 KeSetEvent(Prcb
.DpcEvent
, 0, 0);
434 /* Check if Quantum expired */
435 if (CurrentThread
->Quantum
<= 0) {
436 /* Set the new Quantum */
437 CurrentThread
->Quantum
= Process
->ThreadQuantum
;
439 /* Calculate new priority */
440 OldPriority
= CurrentThread
->Priority
;
441 if (OldPriority
< LOW_REALTIME_PRIORITY
) {
442 NewPriority
= OldPriority
- CurrentThread
->PriorityDecrement
- 1;
443 if (NewPriority
< CurrentThread
->BasePriority
) {
444 NewPriority
= CurrentThread
->BasePriority
;
446 CurrentThread
->PriorityDecrement
= 0;
447 if (OldPriority
!= NewPriority
) {
448 /* Set new Priority */
449 CurrentThread
->Priority
= NewPriority
;
451 /* Queue new thread if none is already */
452 if (Prcb
.NextThread
== NULL
) {
453 /* FIXME: Schedule a New Thread, when ROS will have NT Scheduler */
455 /* Make the current thread non-premeptive if a new thread is queued */
456 CurrentThread
->Preempted
= FALSE
;
460 /* Set the Quantum back to Maximum */
461 //if (CurrentThread->DisableQuantum) {
462 // CurrentThread->Quantum = MAX_QUANTUM;
466 /* Dispatch the Thread */
467 KeLowerIrql(DISPATCH_LEVEL
);
468 PsDispatchThread(THREAD_STATE_READY
);
476 KiDispatchInterrupt(VOID
)
479 * Called whenever a system interrupt is generated at DISPATCH_LEVEL.
480 * It delivers queued DPCs and dispatches a new thread if need be.
483 PLIST_ENTRY DpcEntry
;
488 DPRINT("Dispatching Interrupts\n");
489 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
491 /* Set DPC Deliver to Active */
492 Pcr
= KeGetCurrentKPCR();
494 if (Pcr
->PrcbData
.DpcData
[0].DpcQueueDepth
> 0) {
496 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
498 KiAcquireSpinLock(&Pcr
->PrcbData
.DpcData
[0].DpcLock
);
500 Pcr
->PrcbData
.DpcRoutineActive
= TRUE
;
502 DPRINT("&Pcr->PrcbData.DpcData[0].DpcListHead: %x\n", &Pcr
->PrcbData
.DpcData
[0].DpcListHead
);
503 /* Loop while we have entries */
504 while (!IsListEmpty(&Pcr
->PrcbData
.DpcData
[0].DpcListHead
)) {
505 ASSERT(Pcr
->PrcbData
.DpcData
[0].DpcQueueDepth
> 0);
506 DPRINT("Queue Depth: %x\n", Pcr
->PrcbData
.DpcData
[0].DpcQueueDepth
);
508 /* Get the DPC call it */
509 DpcEntry
= RemoveHeadList(&Pcr
->PrcbData
.DpcData
[0].DpcListHead
);
510 Dpc
= CONTAINING_RECORD(DpcEntry
, KDPC
, DpcListEntry
);
511 DPRINT("Dpc->DpcListEntry.Flink %x\n", Dpc
->DpcListEntry
.Flink
);
513 Pcr
->PrcbData
.DpcData
[0].DpcQueueDepth
--;
515 KiReleaseSpinLock(&Pcr
->PrcbData
.DpcData
[0].DpcLock
);
517 /* Disable/Enabled Interrupts and Call the DPC */
518 KeLowerIrql(OldIrql
);
519 DPRINT("Calling DPC: %x\n", Dpc
);
520 Dpc
->DeferredRoutine(Dpc
,
521 Dpc
->DeferredContext
,
522 Dpc
->SystemArgument1
,
523 Dpc
->SystemArgument2
);
524 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
527 KiAcquireSpinLock(&Pcr
->PrcbData
.DpcData
[0].DpcLock
);
529 * If the dpc routine drops the irql below DISPATCH_LEVEL,
530 * a thread switch can occur and after the next thread switch
531 * the execution may start on an other processor.
533 if (Pcr
!= KeGetCurrentKPCR()) {
534 Pcr
->PrcbData
.DpcRoutineActive
= FALSE
;
535 KiReleaseSpinLock(&Pcr
->PrcbData
.DpcData
[0].DpcLock
);
536 Pcr
= KeGetCurrentKPCR();
537 KiAcquireSpinLock(&Pcr
->PrcbData
.DpcData
[0].DpcLock
);
538 Pcr
->PrcbData
.DpcRoutineActive
= TRUE
;
542 /* Clear DPC Flags */
543 Pcr
->PrcbData
.DpcRoutineActive
= FALSE
;
544 Pcr
->PrcbData
.DpcInterruptRequested
= FALSE
;
546 KiReleaseSpinLock(&Pcr
->PrcbData
.DpcData
[0].DpcLock
);
549 /* DPC Dispatching Ended, re-enable interrupts */
550 KeLowerIrql(OldIrql
);
553 DPRINT("Checking for Quantum End\n");
554 /* If we have Quantum End, call the function */
555 if (Pcr
->PrcbData
.QuantumEnd
) {
556 Pcr
->PrcbData
.QuantumEnd
= FALSE
;