e6c1bd3a1beca4b8537ce03af6cf588fce1cfed1
[reactos.git] / reactos / ntoskrnl / ke / dpc.c
1 /*
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)
9 */
10
11 /* INCLUDES ******************************************************************/
12
13 #define NTDDI_VERSION NTDDI_WS03
14
15 #include <ntoskrnl.h>
16 #define NDEBUG
17 #include <debug.h>
18
19 /* GLOBALS *******************************************************************/
20
21 ULONG KiMaximumDpcQueueDepth = 4;
22 ULONG KiMinimumDpcRate = 3;
23 ULONG KiAdjustDpcThreshold = 20;
24 ULONG KiIdealDpcRate = 20;
25 KMUTEX KiGenericCallDpcMutex;
26
27 /* PRIVATE FUNCTIONS *********************************************************/
28
29 //
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
32 // to be scheduled.
33 //
34 // If no candidate thread has been found, the routine will return, otherwise
35 // it will swap contexts to the next scheduled thread.
36 //
37 VOID
38 NTAPI
39 KiQuantumEnd(VOID)
40 {
41 KPRIORITY Priority;
42 PKPRCB Prcb = KeGetCurrentPrcb();
43 PKTHREAD NextThread, Thread = Prcb->CurrentThread;
44
45 /* Check if a DPC Event was requested to be signaled */
46 if (InterlockedExchange(&Prcb->DpcSetEventRequest, 0))
47 {
48 /* Signal it */
49 KeSetEvent(&Prcb->DpcEvent, 0, 0);
50 }
51
52 /* Raise to synchronization level and lock the PRCB and thread */
53 KeRaiseIrqlToSynchLevel();
54 KiAcquireThreadLock(Thread);
55 KiAcquirePrcbLock(Prcb);
56
57 /* Check if Quantum expired */
58 if (Thread->Quantum <= 0)
59 {
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))
63 {
64 /* Reset the new Quantum */
65 Thread->Quantum = Thread->QuantumReset;
66
67 /* Calculate new priority */
68 Priority = Thread->Priority = KiComputeNewPriority(Thread);
69
70 /* Check if a new thread is scheduled */
71 if (!Prcb->NextThread)
72 {
73 /* FIXME: TODO. Add code from new scheduler */
74 NextThread = NULL;
75 }
76 else
77 {
78 /* Otherwise, make sure that this thread doesn't get preempted */
79 Thread->Preempted = FALSE;
80 }
81 }
82 else
83 {
84 /* Otherwise, set maximum quantum */
85 Thread->Quantum = MAX_QUANTUM;
86 }
87 }
88
89 /* Release the thread lock */
90 KiReleaseThreadLock(Thread);
91
92 /* Check if there's no thread scheduled */
93 if (!Prcb->NextThread)
94 {
95 /* Just leave now */
96 KiReleasePrcbLock(Prcb);
97 KeLowerIrql(DISPATCH_LEVEL);
98 KiDispatchThread(Ready); // FIXME: ROS
99 return;
100 }
101
102 /* This shouldn't happen on ROS yet */
103 DPRINT1("The impossible happened - Tell Alex\n");
104 ASSERT(FALSE);
105 }
106
107 VOID
108 FASTCALL
109 KiRetireDpcList(IN PKPRCB Prcb)
110 {
111 PKDPC_DATA DpcData = Prcb->DpcData;
112 PLIST_ENTRY DpcEntry;
113 PKDPC Dpc;
114 PKDEFERRED_ROUTINE DeferredRoutine;
115 PVOID DeferredContext, SystemArgument1, SystemArgument2;
116
117 /* Main outer loop */
118 do
119 {
120 /* Set us as active */
121 Prcb->DpcRoutineActive = TRUE;
122
123 /* Check if this is a timer expiration request */
124 if (Prcb->TimerRequest)
125 {
126 /* FIXME: Not yet implemented */
127 ASSERT(FALSE);
128 }
129
130 /* Loop while we have entries in the queue */
131 while (DpcData->DpcQueueDepth)
132 {
133 /* Lock the DPC data */
134 KefAcquireSpinLockAtDpcLevel(&DpcData->DpcLock);
135
136 /* Make sure we have an entry */
137 if (!IsListEmpty(&DpcData->DpcListHead))
138 {
139 /* Remove the DPC from the list */
140 DpcEntry = RemoveHeadList(&DpcData->DpcListHead);
141 Dpc = CONTAINING_RECORD(DpcEntry, KDPC, DpcListEntry);
142
143 /* Clear its DPC data and save its parameters */
144 Dpc->DpcData = NULL;
145 DeferredRoutine = Dpc->DeferredRoutine;
146 DeferredContext = Dpc->DeferredContext;
147 SystemArgument1 = Dpc->SystemArgument1;
148 SystemArgument2 = Dpc->SystemArgument2;
149
150 /* Decrease the queue depth */
151 DpcData->DpcQueueDepth--;
152
153 /* Clear DPC Time */
154 Prcb->DebugDpcTime = 0;
155
156 /* Release the lock */
157 KefReleaseSpinLockFromDpcLevel(&DpcData->DpcLock);
158
159 /* Re-enable interrupts */
160 Ke386EnableInterrupts();
161
162 /* Call the DPC */
163 DeferredRoutine(Dpc,
164 DeferredContext,
165 SystemArgument1,
166 SystemArgument2);
167 ASSERT_IRQL(DISPATCH_LEVEL);
168
169 /* Disable interrupts and keep looping */
170 Ke386DisableInterrupts();
171 }
172 else
173 {
174 /* The queue should be flushed now */
175 ASSERT(DpcData->DpcQueueDepth == 0);
176
177 /* Release DPC Lock */
178 KefReleaseSpinLockFromDpcLevel(&DpcData->DpcLock);
179 break;
180 }
181 }
182
183 /* Clear DPC Flags */
184 Prcb->DpcRoutineActive = FALSE;
185 Prcb->DpcInterruptRequested = FALSE;
186
187 /* Check if we have deferred threads */
188 if (Prcb->DeferredReadyListHead.Next)
189 {
190 /* FIXME: 2K3-style scheduling not implemeted */
191 ASSERT(FALSE);
192 }
193 } while (DpcData->DpcQueueDepth);
194 }
195
196 VOID
197 NTAPI
198 KiInitializeDpc(IN PKDPC Dpc,
199 IN PKDEFERRED_ROUTINE DeferredRoutine,
200 IN PVOID DeferredContext,
201 IN KOBJECTS Type)
202 {
203 /* Setup the DPC Object */
204 Dpc->Type = Type;
205 Dpc->Number= 0;
206 Dpc->Importance= MediumImportance;
207 Dpc->DeferredRoutine = DeferredRoutine;
208 Dpc->DeferredContext = DeferredContext;
209 Dpc->DpcData = NULL;
210 }
211
212 /* PUBLIC FUNCTIONS **********************************************************/
213
214 /*
215 * @implemented
216 */
217 VOID
218 NTAPI
219 KeInitializeThreadedDpc(IN PKDPC Dpc,
220 IN PKDEFERRED_ROUTINE DeferredRoutine,
221 IN PVOID DeferredContext)
222 {
223 /* Call the internal routine */
224 KiInitializeDpc(Dpc, DeferredRoutine, DeferredContext, ThreadedDpcObject);
225 }
226
227 /*
228 * @implemented
229 */
230 VOID
231 NTAPI
232 KeInitializeDpc(IN PKDPC Dpc,
233 IN PKDEFERRED_ROUTINE DeferredRoutine,
234 IN PVOID DeferredContext)
235 {
236 /* Call the internal routine */
237 KiInitializeDpc(Dpc, DeferredRoutine, DeferredContext, DpcObject);
238 }
239
240 /*
241 * @implemented
242 */
243 BOOLEAN
244 NTAPI
245 KeInsertQueueDpc(IN PKDPC Dpc,
246 IN PVOID SystemArgument1,
247 IN PVOID SystemArgument2)
248 {
249 KIRQL OldIrql;
250 PKPRCB Prcb, CurrentPrcb = KeGetCurrentPrcb();
251 ULONG Cpu;
252 PKDPC_DATA DpcData;
253 BOOLEAN DpcConfigured = FALSE, DpcInserted = FALSE;
254 ASSERT_DPC(Dpc);
255
256 /* Check IRQL and Raise it to HIGH_LEVEL */
257 KeRaiseIrql(HIGH_LEVEL, &OldIrql);
258
259 /* Check if the DPC has more then the maximum number of CPUs */
260 if (Dpc->Number >= MAXIMUM_PROCESSORS)
261 {
262 /* Then substract the maximum and get that PRCB. */
263 Cpu = Dpc->Number - MAXIMUM_PROCESSORS;
264 Prcb = KiProcessorBlock[Cpu];
265 }
266 else
267 {
268 /* Use the current one */
269 Prcb = CurrentPrcb;
270 Cpu = Prcb->Number;
271 }
272
273 /* Check if this is a threaded DPC and threaded DPCs are enabled */
274 if ((Dpc->Type = ThreadedDpcObject) && (Prcb->ThreadDpcEnable))
275 {
276 /* Then use the threaded data */
277 DpcData = &Prcb->DpcData[DPC_THREADED];
278 }
279 else
280 {
281 /* Otherwise, use the regular data */
282 DpcData = &Prcb->DpcData[DPC_NORMAL];
283 }
284
285 /* Acquire the DPC lock */
286 KiAcquireSpinLock(&DpcData->DpcLock);
287
288 /* Get the DPC Data */
289 if (!InterlockedCompareExchangePointer(&Dpc->DpcData, DpcData, NULL))
290 {
291 /* Now we can play with the DPC safely */
292 Dpc->SystemArgument1 = SystemArgument1;
293 Dpc->SystemArgument2 = SystemArgument2;
294 DpcData->DpcQueueDepth++;
295 DpcData->DpcCount++;
296 DpcConfigured = TRUE;
297
298 /* Check if this is a high importance DPC */
299 if (Dpc->Importance == HighImportance)
300 {
301 /* Pre-empty other DPCs */
302 InsertHeadList(&DpcData->DpcListHead, &Dpc->DpcListEntry);
303 }
304 else
305 {
306 /* Add it at the end */
307 InsertTailList(&DpcData->DpcListHead, &Dpc->DpcListEntry);
308 }
309
310 /* Check if this is the DPC on the threaded list */
311 if (&Prcb->DpcData[DPC_THREADED].DpcListHead == &DpcData->DpcListHead)
312 {
313 /* Make sure a threaded DPC isn't already active */
314 if (!(Prcb->DpcThreadActive) && (!Prcb->DpcThreadRequested))
315 {
316 /* FIXME: Setup Threaded DPC */
317 ASSERT(FALSE);
318 }
319 }
320 else
321 {
322 /* Make sure a DPC isn't executing already */
323 if ((!Prcb->DpcRoutineActive) && (!Prcb->DpcInterruptRequested))
324 {
325 /* Check if this is the same CPU */
326 if (Prcb != CurrentPrcb)
327 {
328 /*
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.
332 */
333 if (((Dpc->Importance == HighImportance) ||
334 (DpcData->DpcQueueDepth >=
335 Prcb->MaximumDpcQueueDepth)) &&
336 (!(AFFINITY_MASK(Cpu) & KiIdleSummary) ||
337 (Prcb->Sleeping)))
338 {
339 /* Set interrupt requested */
340 Prcb->DpcInterruptRequested = TRUE;
341
342 /* Set DPC inserted */
343 DpcInserted = TRUE;
344 }
345 }
346 else
347 {
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))
353 {
354 /* Set interrupt requested */
355 Prcb->DpcInterruptRequested = TRUE;
356
357 /* Set DPC inserted */
358 DpcInserted = TRUE;
359 }
360 }
361 }
362 }
363 }
364
365 /* Release the lock */
366 KiReleaseSpinLock(&DpcData->DpcLock);
367
368 /* Check if the DPC was inserted */
369 if (DpcInserted)
370 {
371 /* Check if this was SMP */
372 if (Prcb != CurrentPrcb)
373 {
374 /* It was, request and IPI */
375 KiIpiSendRequest(AFFINITY_MASK(Cpu), IPI_DPC);
376 }
377 else
378 {
379 /* It wasn't, request an interrupt from HAL */
380 HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
381 }
382 }
383
384 /* Lower IRQL */
385 KeLowerIrql(OldIrql);
386 return DpcConfigured;
387 }
388
389 /*
390 * @implemented
391 */
392 BOOLEAN
393 NTAPI
394 KeRemoveQueueDpc(IN PKDPC Dpc)
395 {
396 PKDPC_DATA DpcData;
397 UCHAR DpcType;
398 ASSERT_DPC(Dpc);
399
400 /* Disable interrupts */
401 Ke386DisableInterrupts();
402
403 /* Get DPC data and type */
404 DpcType = Dpc->Type;
405 DpcData = Dpc->DpcData;
406 if (DpcData)
407 {
408 /* Acquire the DPC lock */
409 KiAcquireSpinLock(&DpcData->DpcLock);
410
411 /* Make sure that the data didn't change */
412 if (DpcData == Dpc->DpcData)
413 {
414 /* Remove the DPC */
415 DpcData->DpcQueueDepth--;
416 RemoveEntryList(&Dpc->DpcListEntry);
417 Dpc->DpcData = NULL;
418 }
419
420 /* Release the lock */
421 KiReleaseSpinLock(&DpcData->DpcLock);
422 }
423
424 /* Re-enable interrupts */
425 Ke386EnableInterrupts();
426
427 /* Return if the DPC was in the queue or not */
428 return DpcData ? TRUE : FALSE;
429 }
430
431 /*
432 * @implemented
433 */
434 VOID
435 NTAPI
436 KeFlushQueuedDpcs(VOID)
437 {
438 PAGED_CODE();
439
440 /* Check if this is an UP machine */
441 if (KeActiveProcessors == 1)
442 {
443 /* Check if there are DPCs on either queues */
444 if ((KeGetCurrentPrcb()->DpcData[DPC_NORMAL].DpcQueueDepth) ||
445 (KeGetCurrentPrcb()->DpcData[DPC_THREADED].DpcQueueDepth))
446 {
447 /* Request an interrupt */
448 HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
449 }
450 }
451 else
452 {
453 /* FIXME: SMP support required */
454 ASSERT(FALSE);
455 }
456 }
457
458 /*
459 * @implemented
460 */
461 BOOLEAN
462 NTAPI
463 KeIsExecutingDpc(VOID)
464 {
465 /* Return if the Dpc Routine is active */
466 return KeGetCurrentPrcb()->DpcRoutineActive;
467 }
468
469 /*
470 * @implemented
471 */
472 VOID
473 NTAPI
474 KeSetImportanceDpc (IN PKDPC Dpc,
475 IN KDPC_IMPORTANCE Importance)
476 {
477 /* Set the DPC Importance */
478 ASSERT_DPC(Dpc);
479 Dpc->Importance = Importance;
480 }
481
482 /*
483 * @implemented
484 */
485 VOID
486 NTAPI
487 KeSetTargetProcessorDpc(IN PKDPC Dpc,
488 IN CCHAR Number)
489 {
490 /* Set a target CPU */
491 ASSERT_DPC(Dpc);
492 Dpc->Number = Number + MAXIMUM_PROCESSORS;
493 }
494
495 /* EOF */