2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ke/dpc.c
5 * PURPOSE: Routines for CPU-level support
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 * Philip Susi (phreak@iag.net)
8 * Eric Kohl (ekohl@abo.rhein-zeitung.de)
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
;
26 /* PRIVATE FUNCTIONS *********************************************************/
29 // This routine executes at the end of a thread's quantum.
30 // If the thread's quantum has expired, then a new thread is attempted
33 // If no candidate thread has been found, the routine will return, otherwise
34 // it will swap contexts to the next scheduled thread.
40 PKPRCB Prcb
= KeGetCurrentPrcb();
41 PKTHREAD NextThread
, Thread
= Prcb
->CurrentThread
;
43 /* Check if a DPC Event was requested to be signaled */
44 if (InterlockedExchange(&Prcb
->DpcSetEventRequest
, 0))
47 KeSetEvent(&Prcb
->DpcEvent
, 0, 0);
50 /* Raise to synchronization level and lock the PRCB and thread */
51 KeRaiseIrqlToSynchLevel();
52 KiAcquireThreadLock(Thread
);
53 KiAcquirePrcbLock(Prcb
);
55 /* Check if Quantum expired */
56 if (Thread
->Quantum
<= 0)
58 /* Make sure that we're not real-time or without a quantum */
59 if ((Thread
->Priority
< LOW_REALTIME_PRIORITY
) &&
60 !(Thread
->ApcState
.Process
->DisableQuantum
))
62 /* Reset the new Quantum */
63 Thread
->Quantum
= Thread
->QuantumReset
;
65 /* Calculate new priority */
66 Thread
->Priority
= KiComputeNewPriority(Thread
, 1);
68 /* Check if a new thread is scheduled */
69 if (!Prcb
->NextThread
)
72 /* Get a new ready thread */
73 NextThread
= KiSelectReadyThread(Thread
->Priority
, Prcb
);
76 /* Found one, set it on standby */
77 NextThread
->State
= Standby
;
78 Prcb
->NextThread
= NextThread
;
82 KiReleasePrcbLock(Prcb
);
83 KeLowerIrql(DISPATCH_LEVEL
);
84 KiDispatchThread(Ready
);
90 /* Otherwise, make sure that this thread doesn't get preempted */
91 Thread
->Preempted
= FALSE
;
96 /* Otherwise, set maximum quantum */
97 Thread
->Quantum
= MAX_QUANTUM
;
101 /* Release the thread lock */
102 KiReleaseThreadLock(Thread
);
104 /* Check if there's no thread scheduled */
105 if (!Prcb
->NextThread
)
108 KiReleasePrcbLock(Prcb
);
109 KeLowerIrql(DISPATCH_LEVEL
);
113 /* Get the next thread now */
114 NextThread
= Prcb
->NextThread
;
116 /* Set current thread's swap busy to true */
117 KiSetThreadSwapBusy(Thread
);
119 /* Switch threads in PRCB */
120 Prcb
->NextThread
= NULL
;
121 Prcb
->CurrentThread
= NextThread
;
123 /* Set thread to running and the switch reason to Quantum End */
124 NextThread
->State
= Running
;
125 Thread
->WaitReason
= WrQuantumEnd
;
127 /* Queue it on the ready lists */
128 KxQueueReadyThread(Thread
, Prcb
);
130 /* Set wait IRQL to APC_LEVEL */
131 Thread
->WaitIrql
= APC_LEVEL
;
134 KiSwapContext(Thread
, NextThread
);
136 /* Lower IRQL back to DISPATCH_LEVEL */
137 KeLowerIrql(DISPATCH_LEVEL
);
142 KiRetireDpcList(IN PKPRCB Prcb
)
144 PKDPC_DATA DpcData
= Prcb
->DpcData
;
145 PLIST_ENTRY DpcEntry
;
147 PKDEFERRED_ROUTINE DeferredRoutine
;
148 PVOID DeferredContext
, SystemArgument1
, SystemArgument2
;
150 /* Main outer loop */
153 /* Set us as active */
154 Prcb
->DpcRoutineActive
= TRUE
;
156 /* Check if this is a timer expiration request */
157 if (Prcb
->TimerRequest
)
159 /* FIXME: Not yet implemented */
163 /* Loop while we have entries in the queue */
164 while (DpcData
->DpcQueueDepth
)
166 /* Lock the DPC data */
167 KefAcquireSpinLockAtDpcLevel(&DpcData
->DpcLock
);
169 /* Make sure we have an entry */
170 if (!IsListEmpty(&DpcData
->DpcListHead
))
172 /* Remove the DPC from the list */
173 DpcEntry
= RemoveHeadList(&DpcData
->DpcListHead
);
174 Dpc
= CONTAINING_RECORD(DpcEntry
, KDPC
, DpcListEntry
);
176 /* Clear its DPC data and save its parameters */
178 DeferredRoutine
= Dpc
->DeferredRoutine
;
179 DeferredContext
= Dpc
->DeferredContext
;
180 SystemArgument1
= Dpc
->SystemArgument1
;
181 SystemArgument2
= Dpc
->SystemArgument2
;
183 /* Decrease the queue depth */
184 DpcData
->DpcQueueDepth
--;
187 Prcb
->DebugDpcTime
= 0;
189 /* Release the lock */
190 KefReleaseSpinLockFromDpcLevel(&DpcData
->DpcLock
);
192 /* Re-enable interrupts */
200 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
202 /* Disable interrupts and keep looping */
207 /* The queue should be flushed now */
208 ASSERT(DpcData
->DpcQueueDepth
== 0);
210 /* Release DPC Lock */
211 KefReleaseSpinLockFromDpcLevel(&DpcData
->DpcLock
);
216 /* Clear DPC Flags */
217 Prcb
->DpcRoutineActive
= FALSE
;
218 Prcb
->DpcInterruptRequested
= FALSE
;
220 /* Check if we have deferred threads */
221 if (Prcb
->DeferredReadyListHead
.Next
)
223 /* FIXME: 2K3-style scheduling not implemeted */
226 } while (DpcData
->DpcQueueDepth
);
231 KiInitializeDpc(IN PKDPC Dpc
,
232 IN PKDEFERRED_ROUTINE DeferredRoutine
,
233 IN PVOID DeferredContext
,
236 /* Setup the DPC Object */
239 Dpc
->Importance
= MediumImportance
;
240 Dpc
->DeferredRoutine
= DeferredRoutine
;
241 Dpc
->DeferredContext
= DeferredContext
;
245 /* PUBLIC FUNCTIONS **********************************************************/
252 KeInitializeThreadedDpc(IN PKDPC Dpc
,
253 IN PKDEFERRED_ROUTINE DeferredRoutine
,
254 IN PVOID DeferredContext
)
256 /* Call the internal routine */
257 KiInitializeDpc(Dpc
, DeferredRoutine
, DeferredContext
, ThreadedDpcObject
);
265 KeInitializeDpc(IN PKDPC Dpc
,
266 IN PKDEFERRED_ROUTINE DeferredRoutine
,
267 IN PVOID DeferredContext
)
269 /* Call the internal routine */
270 KiInitializeDpc(Dpc
, DeferredRoutine
, DeferredContext
, DpcObject
);
278 KeInsertQueueDpc(IN PKDPC Dpc
,
279 IN PVOID SystemArgument1
,
280 IN PVOID SystemArgument2
)
283 PKPRCB Prcb
, CurrentPrcb
= KeGetCurrentPrcb();
286 BOOLEAN DpcConfigured
= FALSE
, DpcInserted
= FALSE
;
289 /* Check IRQL and Raise it to HIGH_LEVEL */
290 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
292 /* Check if the DPC has more then the maximum number of CPUs */
293 if (Dpc
->Number
>= MAXIMUM_PROCESSORS
)
295 /* Then substract the maximum and get that PRCB. */
296 Cpu
= Dpc
->Number
- MAXIMUM_PROCESSORS
;
297 Prcb
= KiProcessorBlock
[Cpu
];
301 /* Use the current one */
306 /* Check if this is a threaded DPC and threaded DPCs are enabled */
307 if ((Dpc
->Type
== ThreadedDpcObject
) && (Prcb
->ThreadDpcEnable
))
309 /* Then use the threaded data */
310 DpcData
= &Prcb
->DpcData
[DPC_THREADED
];
314 /* Otherwise, use the regular data */
315 DpcData
= &Prcb
->DpcData
[DPC_NORMAL
];
318 /* Acquire the DPC lock */
319 KiAcquireSpinLock(&DpcData
->DpcLock
);
321 /* Get the DPC Data */
322 if (!InterlockedCompareExchangePointer(&Dpc
->DpcData
, DpcData
, NULL
))
324 /* Now we can play with the DPC safely */
325 Dpc
->SystemArgument1
= SystemArgument1
;
326 Dpc
->SystemArgument2
= SystemArgument2
;
327 DpcData
->DpcQueueDepth
++;
329 DpcConfigured
= TRUE
;
331 /* Check if this is a high importance DPC */
332 if (Dpc
->Importance
== HighImportance
)
334 /* Pre-empty other DPCs */
335 InsertHeadList(&DpcData
->DpcListHead
, &Dpc
->DpcListEntry
);
339 /* Add it at the end */
340 InsertTailList(&DpcData
->DpcListHead
, &Dpc
->DpcListEntry
);
343 /* Check if this is the DPC on the threaded list */
344 if (&Prcb
->DpcData
[DPC_THREADED
].DpcListHead
== &DpcData
->DpcListHead
)
346 /* Make sure a threaded DPC isn't already active */
347 if (!(Prcb
->DpcThreadActive
) && !(Prcb
->DpcThreadRequested
))
349 /* FIXME: Setup Threaded DPC */
355 /* Make sure a DPC isn't executing already */
356 if (!(Prcb
->DpcRoutineActive
) && !(Prcb
->DpcInterruptRequested
))
358 /* Check if this is the same CPU */
359 if (Prcb
!= CurrentPrcb
)
362 * Check if the DPC is of high importance or above the
363 * maximum depth. If it is, then make sure that the CPU
364 * isn't idle, or that it's sleeping.
366 if (((Dpc
->Importance
== HighImportance
) ||
367 (DpcData
->DpcQueueDepth
>=
368 Prcb
->MaximumDpcQueueDepth
)) &&
369 (!(AFFINITY_MASK(Cpu
) & KiIdleSummary
) ||
372 /* Set interrupt requested */
373 Prcb
->DpcInterruptRequested
= TRUE
;
375 /* Set DPC inserted */
381 /* Check if the DPC is of anything but low importance */
382 if ((Dpc
->Importance
!= LowImportance
) ||
383 (DpcData
->DpcQueueDepth
>=
384 Prcb
->MaximumDpcQueueDepth
) ||
385 (Prcb
->DpcRequestRate
< Prcb
->MinimumDpcRate
))
387 /* Set interrupt requested */
388 Prcb
->DpcInterruptRequested
= TRUE
;
390 /* Set DPC inserted */
398 /* Release the lock */
399 KiReleaseSpinLock(&DpcData
->DpcLock
);
401 /* Check if the DPC was inserted */
404 /* Check if this was SMP */
405 if (Prcb
!= CurrentPrcb
)
407 /* It was, request and IPI */
408 KiIpiSendRequest(AFFINITY_MASK(Cpu
), IPI_DPC
);
412 /* It wasn't, request an interrupt from HAL */
413 HalRequestSoftwareInterrupt(DISPATCH_LEVEL
);
418 KeLowerIrql(OldIrql
);
419 return DpcConfigured
;
427 KeRemoveQueueDpc(IN PKDPC Dpc
)
433 /* Disable interrupts */
436 /* Get DPC data and type */
438 DpcData
= Dpc
->DpcData
;
441 /* Acquire the DPC lock */
442 KiAcquireSpinLock(&DpcData
->DpcLock
);
444 /* Make sure that the data didn't change */
445 if (DpcData
== Dpc
->DpcData
)
448 DpcData
->DpcQueueDepth
--;
449 RemoveEntryList(&Dpc
->DpcListEntry
);
453 /* Release the lock */
454 KiReleaseSpinLock(&DpcData
->DpcLock
);
457 /* Re-enable interrupts */
460 /* Return if the DPC was in the queue or not */
461 return DpcData
? TRUE
: FALSE
;
469 KeFlushQueuedDpcs(VOID
)
473 /* Check if this is an UP machine */
474 if (KeActiveProcessors
== 1)
476 /* Check if there are DPCs on either queues */
477 if ((KeGetCurrentPrcb()->DpcData
[DPC_NORMAL
].DpcQueueDepth
) ||
478 (KeGetCurrentPrcb()->DpcData
[DPC_THREADED
].DpcQueueDepth
))
480 /* Request an interrupt */
481 HalRequestSoftwareInterrupt(DISPATCH_LEVEL
);
486 /* FIXME: SMP support required */
496 KeIsExecutingDpc(VOID
)
498 /* Return if the Dpc Routine is active */
499 return KeGetCurrentPrcb()->DpcRoutineActive
;
507 KeSetImportanceDpc (IN PKDPC Dpc
,
508 IN KDPC_IMPORTANCE Importance
)
510 /* Set the DPC Importance */
512 Dpc
->Importance
= Importance
;
520 KeSetTargetProcessorDpc(IN PKDPC Dpc
,
523 /* Set a target CPU */
525 Dpc
->Number
= Number
+ MAXIMUM_PROCESSORS
;