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 ******************************************************************/
13 #define NTDDI_VERSION NTDDI_WS03
19 /* GLOBALS *******************************************************************/
21 ULONG KiMaximumDpcQueueDepth
= 4;
22 ULONG KiMinimumDpcRate
= 3;
23 ULONG KiAdjustDpcThreshold
= 20;
24 ULONG KiIdealDpcRate
= 20;
25 BOOLEAN KeThreadDpcEnable
;
26 KMUTEX KiGenericCallDpcMutex
;
28 /* PRIVATE FUNCTIONS *********************************************************/
31 // This routine executes at the end of a thread's quantum.
32 // If the thread's quantum has expired, then a new thread is attempted
35 // If no candidate thread has been found, the routine will return, otherwise
36 // it will swap contexts to the next scheduled thread.
43 PKPRCB Prcb
= KeGetCurrentPrcb();
44 PKTHREAD NextThread
, Thread
= Prcb
->CurrentThread
;
46 /* Check if a DPC Event was requested to be signaled */
47 if (InterlockedExchange(&Prcb
->DpcSetEventRequest
, 0))
50 KeSetEvent(&Prcb
->DpcEvent
, 0, 0);
53 /* Raise to synchronization level and lock the PRCB and thread */
54 KeRaiseIrqlToSynchLevel();
55 KiAcquireThreadLock(Thread
);
56 KiAcquirePrcbLock(Prcb
);
58 /* Check if Quantum expired */
59 if (Thread
->Quantum
<= 0)
61 /* Make sure that we're not real-time or without a quantum */
62 if ((Thread
->Priority
< LOW_REALTIME_PRIORITY
) &&
63 !(Thread
->ApcState
.Process
->DisableQuantum
))
65 /* Reset the new Quantum */
66 Thread
->Quantum
= Thread
->QuantumReset
;
68 /* Calculate new priority */
69 Priority
= Thread
->Priority
= KiComputeNewPriority(Thread
);
71 /* Check if a new thread is scheduled */
72 if (!Prcb
->NextThread
)
74 /* FIXME: TODO. Add code from new scheduler */
78 /* Otherwise, make sure that this thread doesn't get preempted */
79 Thread
->Preempted
= FALSE
;
84 /* Otherwise, set maximum quantum */
85 Thread
->Quantum
= MAX_QUANTUM
;
89 /* Release the thread lock */
90 KiReleaseThreadLock(Thread
);
92 /* Check if there's no thread scheduled */
93 if (!Prcb
->NextThread
)
96 KiReleasePrcbLock(Prcb
);
97 KeLowerIrql(DISPATCH_LEVEL
);
98 KiDispatchThread(Ready
); // FIXME: ROS
102 /* This shouldn't happen on ROS yet */
103 DPRINT1("The impossible happened - Tell Alex\n");
106 /* Get the next thread now */
107 NextThread
= Prcb
->NextThread
;
109 /* Set current thread's swap busy to true */
110 KiSetThreadSwapBusy(Thread
);
112 /* Switch threads in PRCB */
113 Prcb
->NextThread
= NULL
;
114 Prcb
->CurrentThread
= NextThread
;
116 /* Set thread to running and the switch reason to Quantum End */
117 NextThread
->State
= Running
;
118 Thread
->WaitReason
= WrQuantumEnd
;
120 /* Queue it on the ready lists */
121 KxQueueReadyThread(Thread
, Prcb
);
123 /* Set wait IRQL to APC_LEVEL */
124 Thread
->WaitIrql
= APC_LEVEL
;
127 KiSwapContext(Thread
, NextThread
);
129 /* Lower IRQL back to DISPATCH_LEVEL */
130 KeLowerIrql(DISPATCH_LEVEL
);
135 KiRetireDpcList(IN PKPRCB Prcb
)
137 PKDPC_DATA DpcData
= Prcb
->DpcData
;
138 PLIST_ENTRY DpcEntry
;
140 PKDEFERRED_ROUTINE DeferredRoutine
;
141 PVOID DeferredContext
, SystemArgument1
, SystemArgument2
;
143 /* Main outer loop */
146 /* Set us as active */
147 Prcb
->DpcRoutineActive
= TRUE
;
149 /* Check if this is a timer expiration request */
150 if (Prcb
->TimerRequest
)
152 /* FIXME: Not yet implemented */
156 /* Loop while we have entries in the queue */
157 while (DpcData
->DpcQueueDepth
)
159 /* Lock the DPC data */
160 KefAcquireSpinLockAtDpcLevel(&DpcData
->DpcLock
);
162 /* Make sure we have an entry */
163 if (!IsListEmpty(&DpcData
->DpcListHead
))
165 /* Remove the DPC from the list */
166 DpcEntry
= RemoveHeadList(&DpcData
->DpcListHead
);
167 Dpc
= CONTAINING_RECORD(DpcEntry
, KDPC
, DpcListEntry
);
169 /* Clear its DPC data and save its parameters */
171 DeferredRoutine
= Dpc
->DeferredRoutine
;
172 DeferredContext
= Dpc
->DeferredContext
;
173 SystemArgument1
= Dpc
->SystemArgument1
;
174 SystemArgument2
= Dpc
->SystemArgument2
;
176 /* Decrease the queue depth */
177 DpcData
->DpcQueueDepth
--;
180 Prcb
->DebugDpcTime
= 0;
182 /* Release the lock */
183 KefReleaseSpinLockFromDpcLevel(&DpcData
->DpcLock
);
185 /* Re-enable interrupts */
193 ASSERT_IRQL(DISPATCH_LEVEL
);
195 /* Disable interrupts and keep looping */
200 /* The queue should be flushed now */
201 ASSERT(DpcData
->DpcQueueDepth
== 0);
203 /* Release DPC Lock */
204 KefReleaseSpinLockFromDpcLevel(&DpcData
->DpcLock
);
209 /* Clear DPC Flags */
210 Prcb
->DpcRoutineActive
= FALSE
;
211 Prcb
->DpcInterruptRequested
= FALSE
;
213 /* Check if we have deferred threads */
214 if (Prcb
->DeferredReadyListHead
.Next
)
216 /* FIXME: 2K3-style scheduling not implemeted */
219 } while (DpcData
->DpcQueueDepth
);
224 KiInitializeDpc(IN PKDPC Dpc
,
225 IN PKDEFERRED_ROUTINE DeferredRoutine
,
226 IN PVOID DeferredContext
,
229 /* Setup the DPC Object */
232 Dpc
->Importance
= MediumImportance
;
233 Dpc
->DeferredRoutine
= DeferredRoutine
;
234 Dpc
->DeferredContext
= DeferredContext
;
238 /* PUBLIC FUNCTIONS **********************************************************/
245 KeInitializeThreadedDpc(IN PKDPC Dpc
,
246 IN PKDEFERRED_ROUTINE DeferredRoutine
,
247 IN PVOID DeferredContext
)
249 /* Call the internal routine */
250 KiInitializeDpc(Dpc
, DeferredRoutine
, DeferredContext
, ThreadedDpcObject
);
258 KeInitializeDpc(IN PKDPC Dpc
,
259 IN PKDEFERRED_ROUTINE DeferredRoutine
,
260 IN PVOID DeferredContext
)
262 /* Call the internal routine */
263 KiInitializeDpc(Dpc
, DeferredRoutine
, DeferredContext
, DpcObject
);
271 KeInsertQueueDpc(IN PKDPC Dpc
,
272 IN PVOID SystemArgument1
,
273 IN PVOID SystemArgument2
)
276 PKPRCB Prcb
, CurrentPrcb
= KeGetCurrentPrcb();
279 BOOLEAN DpcConfigured
= FALSE
, DpcInserted
= FALSE
;
282 /* Check IRQL and Raise it to HIGH_LEVEL */
283 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
285 /* Check if the DPC has more then the maximum number of CPUs */
286 if (Dpc
->Number
>= MAXIMUM_PROCESSORS
)
288 /* Then substract the maximum and get that PRCB. */
289 Cpu
= Dpc
->Number
- MAXIMUM_PROCESSORS
;
290 Prcb
= KiProcessorBlock
[Cpu
];
294 /* Use the current one */
299 /* Check if this is a threaded DPC and threaded DPCs are enabled */
300 if ((Dpc
->Type
= ThreadedDpcObject
) && (Prcb
->ThreadDpcEnable
))
302 /* Then use the threaded data */
303 DpcData
= &Prcb
->DpcData
[DPC_THREADED
];
307 /* Otherwise, use the regular data */
308 DpcData
= &Prcb
->DpcData
[DPC_NORMAL
];
311 /* Acquire the DPC lock */
312 KiAcquireSpinLock(&DpcData
->DpcLock
);
314 /* Get the DPC Data */
315 if (!InterlockedCompareExchangePointer(&Dpc
->DpcData
, DpcData
, NULL
))
317 /* Now we can play with the DPC safely */
318 Dpc
->SystemArgument1
= SystemArgument1
;
319 Dpc
->SystemArgument2
= SystemArgument2
;
320 DpcData
->DpcQueueDepth
++;
322 DpcConfigured
= TRUE
;
324 /* Check if this is a high importance DPC */
325 if (Dpc
->Importance
== HighImportance
)
327 /* Pre-empty other DPCs */
328 InsertHeadList(&DpcData
->DpcListHead
, &Dpc
->DpcListEntry
);
332 /* Add it at the end */
333 InsertTailList(&DpcData
->DpcListHead
, &Dpc
->DpcListEntry
);
336 /* Check if this is the DPC on the threaded list */
337 if (&Prcb
->DpcData
[DPC_THREADED
].DpcListHead
== &DpcData
->DpcListHead
)
339 /* Make sure a threaded DPC isn't already active */
340 if (!(Prcb
->DpcThreadActive
) && (!Prcb
->DpcThreadRequested
))
342 /* FIXME: Setup Threaded DPC */
348 /* Make sure a DPC isn't executing already */
349 if ((!Prcb
->DpcRoutineActive
) && (!Prcb
->DpcInterruptRequested
))
351 /* Check if this is the same CPU */
352 if (Prcb
!= CurrentPrcb
)
355 * Check if the DPC is of high importance or above the
356 * maximum depth. If it is, then make sure that the CPU
357 * isn't idle, or that it's sleeping.
359 if (((Dpc
->Importance
== HighImportance
) ||
360 (DpcData
->DpcQueueDepth
>=
361 Prcb
->MaximumDpcQueueDepth
)) &&
362 (!(AFFINITY_MASK(Cpu
) & KiIdleSummary
) ||
365 /* Set interrupt requested */
366 Prcb
->DpcInterruptRequested
= TRUE
;
368 /* Set DPC inserted */
374 /* Check if the DPC is of anything but low importance */
375 if ((Dpc
->Importance
!= LowImportance
) ||
376 (DpcData
->DpcQueueDepth
>=
377 Prcb
->MaximumDpcQueueDepth
) ||
378 (Prcb
->DpcRequestRate
< Prcb
->MinimumDpcRate
))
380 /* Set interrupt requested */
381 Prcb
->DpcInterruptRequested
= TRUE
;
383 /* Set DPC inserted */
391 /* Release the lock */
392 KiReleaseSpinLock(&DpcData
->DpcLock
);
394 /* Check if the DPC was inserted */
397 /* Check if this was SMP */
398 if (Prcb
!= CurrentPrcb
)
400 /* It was, request and IPI */
401 KiIpiSendRequest(AFFINITY_MASK(Cpu
), IPI_DPC
);
405 /* It wasn't, request an interrupt from HAL */
406 HalRequestSoftwareInterrupt(DISPATCH_LEVEL
);
411 KeLowerIrql(OldIrql
);
412 return DpcConfigured
;
420 KeRemoveQueueDpc(IN PKDPC Dpc
)
426 /* Disable interrupts */
429 /* Get DPC data and type */
431 DpcData
= Dpc
->DpcData
;
434 /* Acquire the DPC lock */
435 KiAcquireSpinLock(&DpcData
->DpcLock
);
437 /* Make sure that the data didn't change */
438 if (DpcData
== Dpc
->DpcData
)
441 DpcData
->DpcQueueDepth
--;
442 RemoveEntryList(&Dpc
->DpcListEntry
);
446 /* Release the lock */
447 KiReleaseSpinLock(&DpcData
->DpcLock
);
450 /* Re-enable interrupts */
453 /* Return if the DPC was in the queue or not */
454 return DpcData
? TRUE
: FALSE
;
462 KeFlushQueuedDpcs(VOID
)
466 /* Check if this is an UP machine */
467 if (KeActiveProcessors
== 1)
469 /* Check if there are DPCs on either queues */
470 if ((KeGetCurrentPrcb()->DpcData
[DPC_NORMAL
].DpcQueueDepth
) ||
471 (KeGetCurrentPrcb()->DpcData
[DPC_THREADED
].DpcQueueDepth
))
473 /* Request an interrupt */
474 HalRequestSoftwareInterrupt(DISPATCH_LEVEL
);
479 /* FIXME: SMP support required */
489 KeIsExecutingDpc(VOID
)
491 /* Return if the Dpc Routine is active */
492 return KeGetCurrentPrcb()->DpcRoutineActive
;
500 KeSetImportanceDpc (IN PKDPC Dpc
,
501 IN KDPC_IMPORTANCE Importance
)
503 /* Set the DPC Importance */
505 Dpc
->Importance
= Importance
;
513 KeSetTargetProcessorDpc(IN PKDPC Dpc
,
516 /* Set a target CPU */
518 Dpc
->Number
= Number
+ MAXIMUM_PROCESSORS
;