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 KMUTEX KiGenericCallDpcMutex
;
27 /* PRIVATE FUNCTIONS *********************************************************/
30 // This routine executes at the end of a thread's quantum.
31 // If the thread's quantum has expired, then a new thread is attempted
34 // If no candidate thread has been found, the routine will return, otherwise
35 // it will swap contexts to the next scheduled thread.
42 PKPRCB Prcb
= KeGetCurrentPrcb();
43 PKTHREAD NextThread
, Thread
= Prcb
->CurrentThread
;
45 /* Check if a DPC Event was requested to be signaled */
46 if (InterlockedExchange(&Prcb
->DpcSetEventRequest
, 0))
49 KeSetEvent(&Prcb
->DpcEvent
, 0, 0);
52 /* Raise to synchronization level and lock the PRCB and thread */
53 KeRaiseIrqlToSynchLevel();
54 KiAcquireThreadLock(Thread
);
55 KiAcquirePrcbLock(Prcb
);
57 /* Check if Quantum expired */
58 if (Thread
->Quantum
<= 0)
60 /* Make sure that we're not real-time or without a quantum */
61 if ((Thread
->Priority
< LOW_REALTIME_PRIORITY
) &&
62 !(Thread
->ApcState
.Process
->DisableQuantum
))
64 /* Reset the new Quantum */
65 Thread
->Quantum
= Thread
->QuantumReset
;
67 /* Calculate new priority */
68 Priority
= Thread
->Priority
= KiComputeNewPriority(Thread
);
70 /* Check if a new thread is scheduled */
71 if (!Prcb
->NextThread
)
73 /* 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");
109 KiRetireDpcList(IN PKPRCB Prcb
)
111 PKDPC_DATA DpcData
= Prcb
->DpcData
;
112 PLIST_ENTRY DpcEntry
;
114 PKDEFERRED_ROUTINE DeferredRoutine
;
115 PVOID DeferredContext
, SystemArgument1
, SystemArgument2
;
117 /* Main outer loop */
120 /* Set us as active */
121 Prcb
->DpcRoutineActive
= TRUE
;
123 /* Check if this is a timer expiration request */
124 if (Prcb
->TimerRequest
)
126 /* FIXME: Not yet implemented */
130 /* Loop while we have entries in the queue */
131 while (DpcData
->DpcQueueDepth
)
133 /* Lock the DPC data */
134 KefAcquireSpinLockAtDpcLevel(&DpcData
->DpcLock
);
136 /* Make sure we have an entry */
137 if (!IsListEmpty(&DpcData
->DpcListHead
))
139 /* Remove the DPC from the list */
140 DpcEntry
= RemoveHeadList(&DpcData
->DpcListHead
);
141 Dpc
= CONTAINING_RECORD(DpcEntry
, KDPC
, DpcListEntry
);
143 /* Clear its DPC data and save its parameters */
145 DeferredRoutine
= Dpc
->DeferredRoutine
;
146 DeferredContext
= Dpc
->DeferredContext
;
147 SystemArgument1
= Dpc
->SystemArgument1
;
148 SystemArgument2
= Dpc
->SystemArgument2
;
150 /* Decrease the queue depth */
151 DpcData
->DpcQueueDepth
--;
154 Prcb
->DebugDpcTime
= 0;
156 /* Release the lock */
157 KefReleaseSpinLockFromDpcLevel(&DpcData
->DpcLock
);
159 /* Re-enable interrupts */
160 Ke386EnableInterrupts();
167 ASSERT_IRQL(DISPATCH_LEVEL
);
169 /* Disable interrupts and keep looping */
170 Ke386DisableInterrupts();
174 /* The queue should be flushed now */
175 ASSERT(DpcData
->DpcQueueDepth
== 0);
177 /* Release DPC Lock */
178 KefReleaseSpinLockFromDpcLevel(&DpcData
->DpcLock
);
183 /* Clear DPC Flags */
184 Prcb
->DpcRoutineActive
= FALSE
;
185 Prcb
->DpcInterruptRequested
= FALSE
;
187 /* Check if we have deferred threads */
188 if (Prcb
->DeferredReadyListHead
.Next
)
190 /* FIXME: 2K3-style scheduling not implemeted */
193 } while (DpcData
->DpcQueueDepth
);
198 KiInitializeDpc(IN PKDPC Dpc
,
199 IN PKDEFERRED_ROUTINE DeferredRoutine
,
200 IN PVOID DeferredContext
,
203 /* Setup the DPC Object */
206 Dpc
->Importance
= MediumImportance
;
207 Dpc
->DeferredRoutine
= DeferredRoutine
;
208 Dpc
->DeferredContext
= DeferredContext
;
212 /* PUBLIC FUNCTIONS **********************************************************/
219 KeInitializeThreadedDpc(IN PKDPC Dpc
,
220 IN PKDEFERRED_ROUTINE DeferredRoutine
,
221 IN PVOID DeferredContext
)
223 /* Call the internal routine */
224 KiInitializeDpc(Dpc
, DeferredRoutine
, DeferredContext
, ThreadedDpcObject
);
232 KeInitializeDpc(IN PKDPC Dpc
,
233 IN PKDEFERRED_ROUTINE DeferredRoutine
,
234 IN PVOID DeferredContext
)
236 /* Call the internal routine */
237 KiInitializeDpc(Dpc
, DeferredRoutine
, DeferredContext
, DpcObject
);
245 KeInsertQueueDpc(IN PKDPC Dpc
,
246 IN PVOID SystemArgument1
,
247 IN PVOID SystemArgument2
)
250 PKPRCB Prcb
, CurrentPrcb
= KeGetCurrentPrcb();
253 BOOLEAN DpcConfigured
= FALSE
, DpcInserted
= FALSE
;
256 /* Check IRQL and Raise it to HIGH_LEVEL */
257 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
259 /* Check if the DPC has more then the maximum number of CPUs */
260 if (Dpc
->Number
>= MAXIMUM_PROCESSORS
)
262 /* Then substract the maximum and get that PRCB. */
263 Cpu
= Dpc
->Number
- MAXIMUM_PROCESSORS
;
264 Prcb
= KiProcessorBlock
[Cpu
];
268 /* Use the current one */
273 /* Check if this is a threaded DPC and threaded DPCs are enabled */
274 if ((Dpc
->Type
= ThreadedDpcObject
) && (Prcb
->ThreadDpcEnable
))
276 /* Then use the threaded data */
277 DpcData
= &Prcb
->DpcData
[DPC_THREADED
];
281 /* Otherwise, use the regular data */
282 DpcData
= &Prcb
->DpcData
[DPC_NORMAL
];
285 /* Acquire the DPC lock */
286 KiAcquireSpinLock(&DpcData
->DpcLock
);
288 /* Get the DPC Data */
289 if (!InterlockedCompareExchangePointer(&Dpc
->DpcData
, DpcData
, NULL
))
291 /* Now we can play with the DPC safely */
292 Dpc
->SystemArgument1
= SystemArgument1
;
293 Dpc
->SystemArgument2
= SystemArgument2
;
294 DpcData
->DpcQueueDepth
++;
296 DpcConfigured
= TRUE
;
298 /* Check if this is a high importance DPC */
299 if (Dpc
->Importance
== HighImportance
)
301 /* Pre-empty other DPCs */
302 InsertHeadList(&DpcData
->DpcListHead
, &Dpc
->DpcListEntry
);
306 /* Add it at the end */
307 InsertTailList(&DpcData
->DpcListHead
, &Dpc
->DpcListEntry
);
310 /* Check if this is the DPC on the threaded list */
311 if (&Prcb
->DpcData
[DPC_THREADED
].DpcListHead
== &DpcData
->DpcListHead
)
313 /* Make sure a threaded DPC isn't already active */
314 if (!(Prcb
->DpcThreadActive
) && (!Prcb
->DpcThreadRequested
))
316 /* FIXME: Setup Threaded DPC */
322 /* Make sure a DPC isn't executing already */
323 if ((!Prcb
->DpcRoutineActive
) && (!Prcb
->DpcInterruptRequested
))
325 /* Check if this is the same CPU */
326 if (Prcb
!= CurrentPrcb
)
329 * Check if the DPC is of high importance or above the
330 * maximum depth. If it is, then make sure that the CPU
331 * isn't idle, or that it's sleeping.
333 if (((Dpc
->Importance
== HighImportance
) ||
334 (DpcData
->DpcQueueDepth
>=
335 Prcb
->MaximumDpcQueueDepth
)) &&
336 (!(AFFINITY_MASK(Cpu
) & KiIdleSummary
) ||
339 /* Set interrupt requested */
340 Prcb
->DpcInterruptRequested
= TRUE
;
342 /* Set DPC inserted */
348 /* Check if the DPC is of anything but low importance */
349 if ((Dpc
->Importance
!= LowImportance
) ||
350 (DpcData
->DpcQueueDepth
>=
351 Prcb
->MaximumDpcQueueDepth
) ||
352 (Prcb
->DpcRequestRate
< Prcb
->MinimumDpcRate
))
354 /* Set interrupt requested */
355 Prcb
->DpcInterruptRequested
= TRUE
;
357 /* Set DPC inserted */
365 /* Release the lock */
366 KiReleaseSpinLock(&DpcData
->DpcLock
);
368 /* Check if the DPC was inserted */
371 /* Check if this was SMP */
372 if (Prcb
!= CurrentPrcb
)
374 /* It was, request and IPI */
375 KiIpiSendRequest(AFFINITY_MASK(Cpu
), IPI_DPC
);
379 /* It wasn't, request an interrupt from HAL */
380 HalRequestSoftwareInterrupt(DISPATCH_LEVEL
);
385 KeLowerIrql(OldIrql
);
386 return DpcConfigured
;
394 KeRemoveQueueDpc(IN PKDPC Dpc
)
400 /* Disable interrupts */
401 Ke386DisableInterrupts();
403 /* Get DPC data and type */
405 DpcData
= Dpc
->DpcData
;
408 /* Acquire the DPC lock */
409 KiAcquireSpinLock(&DpcData
->DpcLock
);
411 /* Make sure that the data didn't change */
412 if (DpcData
== Dpc
->DpcData
)
415 DpcData
->DpcQueueDepth
--;
416 RemoveEntryList(&Dpc
->DpcListEntry
);
420 /* Release the lock */
421 KiReleaseSpinLock(&DpcData
->DpcLock
);
424 /* Re-enable interrupts */
425 Ke386EnableInterrupts();
427 /* Return if the DPC was in the queue or not */
428 return DpcData
? TRUE
: FALSE
;
436 KeFlushQueuedDpcs(VOID
)
440 /* Check if this is an UP machine */
441 if (KeActiveProcessors
== 1)
443 /* Check if there are DPCs on either queues */
444 if ((KeGetCurrentPrcb()->DpcData
[DPC_NORMAL
].DpcQueueDepth
) ||
445 (KeGetCurrentPrcb()->DpcData
[DPC_THREADED
].DpcQueueDepth
))
447 /* Request an interrupt */
448 HalRequestSoftwareInterrupt(DISPATCH_LEVEL
);
453 /* FIXME: SMP support required */
463 KeIsExecutingDpc(VOID
)
465 /* Return if the Dpc Routine is active */
466 return KeGetCurrentPrcb()->DpcRoutineActive
;
474 KeSetImportanceDpc (IN PKDPC Dpc
,
475 IN KDPC_IMPORTANCE Importance
)
477 /* Set the DPC Importance */
479 Dpc
->Importance
= Importance
;
487 KeSetTargetProcessorDpc(IN PKDPC Dpc
,
490 /* Set a target CPU */
492 Dpc
->Number
= Number
+ MAXIMUM_PROCESSORS
;