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