fixed uninitialized variable warning
[reactos.git] / reactos / ntoskrnl / ke / dpc.c
1 /* $Id$
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/ke/dpc.c
6 * PURPOSE: Handle DPCs (Delayed Procedure Calls)
7 *
8 * PROGRAMMERS: David Welch (welch@mcmail.com)
9 * Philip Susi (phreak@iag.net)
10 * Eric Kohl (ekohl@abo.rhein-zeitung.de)
11 * Alex Ionescu (alex@relsoft.net)
12 */
13
14 /*
15 * NOTE: See also the higher level support routines in ntoskrnl/io/dpc.c
16 */
17
18 /* INCLUDES ***************************************************************/
19
20 #include <ntoskrnl.h>
21 #define NDEBUG
22 #include <internal/debug.h>
23
24 /* TYPES *******************************************************************/
25
26 #define MAX_QUANTUM 0x7F
27
28 /* FUNCTIONS ****************************************************************/
29
30 /*
31 * FUNCTION: Initialize DPC handling
32 */
33 VOID
34 INIT_FUNCTION
35 NTAPI
36 KeInitDpc(PKPRCB Prcb)
37 {
38 InitializeListHead(&Prcb->DpcData[0].DpcListHead);
39 #if 0
40 /*
41 * FIXME:
42 * Prcb->DpcEvent is a NULL pointer.
43 */
44 KeInitializeEvent(Prcb->DpcEvent, 0, 0);
45 #endif
46 KeInitializeSpinLock(&Prcb->DpcData[0].DpcLock);
47 Prcb->MaximumDpcQueueDepth = 4;
48 Prcb->MinimumDpcRate = 3;
49 Prcb->DpcData[0].DpcQueueDepth = 0;
50 }
51
52 /*
53 * @implemented
54 */
55 VOID
56 STDCALL
57 KeInitializeThreadedDpc(PKDPC Dpc,
58 PKDEFERRED_ROUTINE DeferredRoutine,
59 PVOID DeferredContext)
60 /*
61 * FUNCTION:
62 * Initalizes a Threaded DPC and registers the DeferredRoutine for it.
63 * ARGUMENTS:
64 * Dpc = Pointer to a caller supplied DPC to be initialized. The caller must allocate this memory.
65 * DeferredRoutine = Pointer to associated DPC callback routine.
66 * DeferredContext = Parameter to be passed to the callback routine.
67 * NOTE: Callers can be running at any IRQL.
68 */
69 {
70 DPRINT("Threaded DPC Initializing: %x with Routine: %x\n", Dpc, DeferredRoutine);
71 Dpc->Type = ThreadedDpcObject;
72 Dpc->Number= 0;
73 Dpc->Importance= MediumImportance;
74 Dpc->DeferredRoutine = DeferredRoutine;
75 Dpc->DeferredContext = DeferredContext;
76 Dpc->DpcData = NULL;
77 }
78
79 /*
80 * @implemented
81 *
82 * FUNCTION:
83 * Initalizes a DPC and registers the DeferredRoutine for it.
84 * ARGUMENTS:
85 * Dpc = Pointer to a caller supplied DPC to be initialized. The caller must allocate this memory.
86 * DeferredRoutine = Pointer to associated DPC callback routine.
87 * DeferredContext = Parameter to be passed to the callback routine.
88 * NOTE: Callers can be running at any IRQL.
89 */
90 VOID
91 STDCALL
92 KeInitializeDpc(PKDPC Dpc,
93 PKDEFERRED_ROUTINE DeferredRoutine,
94 PVOID DeferredContext)
95 {
96 DPRINT("DPC Initializing: %x with Routine: %x\n", Dpc, DeferredRoutine);
97 Dpc->Type = DpcObject;
98 Dpc->Number= 0;
99 Dpc->Importance= MediumImportance;
100 Dpc->DeferredRoutine = DeferredRoutine;
101 Dpc->DeferredContext = DeferredContext;
102 Dpc->DpcData = NULL;
103 }
104
105 /*
106 * @implemented
107 *
108 * FUNCTION:
109 * Queues a DPC for execution when the IRQL of a processor
110 * drops below DISPATCH_LEVEL
111 * ARGUMENTS:
112 * Dpc = Pointed to a DPC Object Initalized by KeInitializeDpc.
113 * SystemArgument1 = Driver Determined context data
114 * SystemArgument2 = Driver Determined context data
115 * RETURNS:
116 * TRUE if the DPC object wasn't already in the queue
117 * FALSE otherwise
118 * NOTES:
119 * If there is currently a DPC active on the target processor, or a DPC
120 * interrupt has already been requested on the target processor when a
121 * DPC is queued, then no further action is necessary. The DPC will be
122 * executed on the target processor when its queue entry is processed.
123 *
124 * If there is not a DPC active on the target processor and a DPC interrupt
125 * has not been requested on the target processor, then the exact treatment
126 * of the DPC is dependent on whether the host system is a UP system or an
127 * MP system.
128 *
129 * UP system.
130 * ----------
131 * If the DPC is of medium or high importance, the current DPC queue depth
132 * is greater than the maximum target depth, or current DPC request rate is
133 * less the minimum target rate, then a DPC interrupt is requested on the
134 * host processor and the DPC will be processed when the interrupt occurs.
135 * Otherwise, no DPC interupt is requested and the DPC execution will be
136 * delayed until the DPC queue depth is greater that the target depth or the
137 * minimum DPC rate is less than the target rate.
138 *
139 * MP system.
140 * ----------
141 * If the DPC is being queued to another processor and the depth of the DPC
142 * queue on the target processor is greater than the maximum target depth or
143 * the DPC is of high importance, then a DPC interrupt is requested on the
144 * target processor and the DPC will be processed when the interrupt occurs.
145 * Otherwise, the DPC execution will be delayed on the target processor until
146 * the DPC queue depth on the target processor is greater that the maximum
147 * target depth or the minimum DPC rate on the target processor is less than
148 * the target mimimum rate.
149 *
150 * If the DPC is being queued to the current processor and the DPC is not of
151 * low importance, the current DPC queue depth is greater than the maximum
152 * target depth, or the minimum DPC rate is less than the minimum target rate,
153 * then a DPC interrupt is request on the current processor and the DPV will
154 * be processed whne the interrupt occurs. Otherwise, no DPC interupt is
155 * requested and the DPC execution will be delayed until the DPC queue depth
156 * is greater that the target depth or the minimum DPC rate is less than the
157 * target rate.
158 */
159 BOOLEAN
160 STDCALL
161 KeInsertQueueDpc(PKDPC Dpc,
162 PVOID SystemArgument1,
163 PVOID SystemArgument2)
164 {
165 KIRQL OldIrql;
166 PKPRCB Prcb;
167
168 DPRINT("KeInsertQueueDpc(DPC %x, SystemArgument1 %x, SystemArgument2 %x)\n",
169 Dpc, SystemArgument1, SystemArgument2);
170
171 /* Check IRQL and Raise it to HIGH_LEVEL */
172 ASSERT(KeGetCurrentIrql()>=DISPATCH_LEVEL);
173 KeRaiseIrql(HIGH_LEVEL, &OldIrql);
174
175 /* Check if this is a Thread DPC, which we don't support (yet) */
176 if (Dpc->Type == ThreadedDpcObject) {
177 return FALSE;
178 KeLowerIrql(OldIrql);
179 }
180
181 #ifdef CONFIG_SMP
182 /* Get the right PCR for this CPU */
183 if (Dpc->Number >= MAXIMUM_PROCESSORS) {
184
185 ASSERT (Dpc->Number - MAXIMUM_PROCESSORS < KeNumberProcessors);
186 Prcb = ((PKPCR)((ULONG_PTR)KPCR_BASE + ((Dpc->Number - MAXIMUM_PROCESSORS) * PAGE_SIZE)))->Prcb;
187
188 } else {
189
190 ASSERT (Dpc->Number < KeNumberProcessors);
191 Prcb = KeGetCurrentPrcb();
192 Dpc->Number = KeGetCurrentProcessorNumber();
193 }
194
195 KiAcquireSpinLock(&Prcb->DpcData[0].DpcLock);
196 #else
197 Prcb = ((PKPCR)KPCR_BASE)->Prcb;
198 #endif
199
200 /* Get the DPC Data */
201 if (InterlockedCompareExchangeUL(&Dpc->DpcData, &Prcb->DpcData[0].DpcLock, 0)) {
202
203 DPRINT("DPC Already Inserted\n");
204 #ifdef CONFIG_SMP
205 KiReleaseSpinLock(&Prcb->DpcData[0].DpcLock);
206 #endif
207 KeLowerIrql(OldIrql);
208 return(FALSE);
209 }
210
211 /* Make sure the lists are free if the Queue is 0 */
212 if (Prcb->DpcData[0].DpcQueueDepth == 0) {
213
214 ASSERT(IsListEmpty(&Prcb->DpcData[0].DpcListHead));
215 } else {
216
217 ASSERT(!IsListEmpty(&Prcb->DpcData[0].DpcListHead));
218 }
219
220 /* Now we can play with the DPC safely */
221 Dpc->SystemArgument1=SystemArgument1;
222 Dpc->SystemArgument2=SystemArgument2;
223 Prcb->DpcData[0].DpcQueueDepth++;
224 Prcb->DpcData[0].DpcCount++;
225
226 /* Insert the DPC into the list. HighImportance DPCs go at the beginning */
227 if (Dpc->Importance == HighImportance) {
228
229 InsertHeadList(&Prcb->DpcData[0].DpcListHead, &Dpc->DpcListEntry);
230 } else {
231
232 InsertTailList(&Prcb->DpcData[0].DpcListHead, &Dpc->DpcListEntry);
233 }
234 DPRINT("New DPC Added. Dpc->DpcListEntry.Flink %x\n", Dpc->DpcListEntry.Flink);
235
236 /* Make sure a DPC isn't executing already and respect rules outlined above. */
237 if ((!Prcb->DpcRoutineActive) && (!Prcb->DpcInterruptRequested)) {
238
239 #ifdef CONFIG_SMP
240 /* Check if this is the same CPU */
241 if (Prcb != KeGetCurrentPrcb()) {
242
243 /* Send IPI if High Importance */
244 if ((Dpc->Importance == HighImportance) ||
245 (Prcb->DpcData[0].DpcQueueDepth >= Prcb->MaximumDpcQueueDepth)) {
246
247 if (Dpc->Number >= MAXIMUM_PROCESSORS) {
248
249 KiIpiSendRequest(1 << (Dpc->Number - MAXIMUM_PROCESSORS), IPI_DPC);
250 } else {
251
252 KiIpiSendRequest(1 << Dpc->Number, IPI_DPC);
253 }
254
255 }
256 } else {
257
258 /* Request an Interrupt only if the DPC isn't low priority */
259 if ((Dpc->Importance != LowImportance) ||
260 (Prcb->DpcData[0].DpcQueueDepth >= Prcb->MaximumDpcQueueDepth) ||
261 (Prcb->DpcRequestRate < Prcb->MinimumDpcRate)) {
262
263 /* Request Interrupt */
264 HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
265 Prcb->DpcInterruptRequested = TRUE;
266 }
267 }
268 #else
269 DPRINT("Requesting Interrupt. Importance: %x. QueueDepth: %x. MaxQueue: %x . RequestRate: %x. MinRate:%x \n", Dpc->Importance, Prcb->DpcData[0].DpcQueueDepth, Prcb->MaximumDpcQueueDepth, Prcb->DpcRequestRate, Prcb->MinimumDpcRate);
270
271 /* Request an Interrupt only if the DPC isn't low priority */
272 if ((Dpc->Importance != LowImportance) ||
273 (Prcb->DpcData[0].DpcQueueDepth >= Prcb->MaximumDpcQueueDepth) ||
274 (Prcb->DpcRequestRate < Prcb->MinimumDpcRate)) {
275
276 /* Request Interrupt */
277 DPRINT("Requesting Interrupt\n");
278 HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
279 Prcb->DpcInterruptRequested = TRUE;
280 }
281 #endif
282 }
283 #ifdef CONFIG_SMP
284 KiReleaseSpinLock(&Prcb->DpcData[0].DpcLock);
285 #endif
286 /* Lower IRQL */
287 KeLowerIrql(OldIrql);
288 return(TRUE);
289 }
290
291 /*
292 * @implemented
293 *
294 * FUNCTION:
295 * Removes DPC object from the system dpc queue
296 * ARGUMENTS:
297 * Dpc = Pointer to DPC to remove from the queue.
298 * RETURNS:
299 * TRUE if the DPC was in the queue
300 * FALSE otherwise
301 */
302 BOOLEAN
303 STDCALL
304 KeRemoveQueueDpc(PKDPC Dpc)
305 {
306 BOOLEAN WasInQueue;
307 KIRQL OldIrql;
308
309 /* Raise IRQL */
310 DPRINT("Removing DPC: %x\n", Dpc);
311 KeRaiseIrql(HIGH_LEVEL, &OldIrql);
312 #ifdef CONFIG_SMP
313 KiAcquireSpinLock(&((PKDPC_DATA)Dpc->DpcData)->DpcLock);
314 #endif
315
316 /* First make sure the DPC lock isn't being held */
317 WasInQueue = Dpc->DpcData ? TRUE : FALSE;
318 if (Dpc->DpcData) {
319
320 /* Remove the DPC */
321 ((PKDPC_DATA)Dpc->DpcData)->DpcQueueDepth--;
322 RemoveEntryList(&Dpc->DpcListEntry);
323
324 }
325 #ifdef CONFIG_SMP
326 KiReleaseSpinLock(&((PKDPC_DATA)Dpc->DpcData)->DpcLock);
327 #endif
328
329 /* Return if the DPC was in the queue or not */
330 KeLowerIrql(OldIrql);
331 return WasInQueue;
332 }
333
334 /*
335 * @implemented
336 */
337 VOID
338 STDCALL
339 KeFlushQueuedDpcs(VOID)
340 /*
341 * FUNCTION:
342 * Called to Deliver DPCs if any are pending.
343 * NOTES:
344 * Called when deleting a Driver.
345 */
346 {
347 /* Request an interrupt if needed */
348 if (KeGetCurrentPrcb()->DpcData[0].DpcQueueDepth) HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
349 }
350
351 /*
352 * @implemented
353 */
354 BOOLEAN
355 STDCALL
356 KeIsExecutingDpc(
357 VOID
358 )
359 {
360 /* Return if the Dpc Routine is active */
361 return KeGetCurrentPrcb()->DpcRoutineActive;
362 }
363
364 /*
365 * FUNCTION: Specifies the DPCs importance
366 * ARGUMENTS:
367 * Dpc = Initalizes DPC
368 * Importance = DPC importance
369 * RETURNS: None
370 *
371 * @implemented
372 */
373 VOID
374 STDCALL
375 KeSetImportanceDpc (IN PKDPC Dpc,
376 IN KDPC_IMPORTANCE Importance)
377 {
378 /* Set the DPC Importance */
379 Dpc->Importance = Importance;
380 }
381
382 /*
383 * @implemented
384 *
385 * FUNCTION: Specifies on which processor the DPC will run
386 * ARGUMENTS:
387 * Dpc = Initalizes DPC
388 * Number = Processor number
389 * RETURNS: None
390 */
391 VOID
392 STDCALL
393 KeSetTargetProcessorDpc(IN PKDPC Dpc,
394 IN CCHAR Number)
395 {
396 /* Check how many CPUs are on the system */
397 if (Number >= MAXIMUM_PROCESSORS) {
398
399 /* No CPU Number */
400 Dpc->Number = 0;
401
402 } else {
403
404 /* Set the Number Specified */
405 ASSERT(Number < KeNumberProcessors);
406 Dpc->Number = Number + MAXIMUM_PROCESSORS;
407 }
408 }
409
410 /*
411 * FUNCTION:
412 * Called when a quantum end occurs to check if priority should be changed
413 * and wether a new thread should be dispatched.
414 * NOTES:
415 * Called when deleting a Driver.
416 */
417 VOID
418 STDCALL
419 KiQuantumEnd(VOID)
420 {
421 PKPRCB Prcb;
422 PKTHREAD CurrentThread;
423 KIRQL OldIrql;
424 PKPROCESS Process;
425 KPRIORITY OldPriority;
426 KPRIORITY NewPriority;
427
428 /* Lock dispatcher, get current thread */
429 Prcb = KeGetCurrentPrcb();
430 CurrentThread = KeGetCurrentThread();
431 OldIrql = KeRaiseIrqlToSynchLevel();
432
433 /* Get the Thread's Process */
434 Process = CurrentThread->ApcState.Process;
435
436 /* Set DPC Event if requested */
437 if (Prcb->DpcSetEventRequest) {
438 /*
439 * FIXME:
440 * Prcb->DpcEvent is not initialized.
441 */
442 KEBUGCHECK(0);
443 KeSetEvent(Prcb->DpcEvent, 0, 0);
444 }
445
446 /* Check if Quantum expired */
447 if (CurrentThread->Quantum <= 0) {
448
449 /* Reset the new Quantum */
450 CurrentThread->Quantum = CurrentThread->QuantumReset;
451
452 /* Calculate new priority */
453 OldPriority = CurrentThread->Priority;
454 if (OldPriority < LOW_REALTIME_PRIORITY) {
455
456 /* Set the New Priority and add the Priority Decrement */
457 NewPriority = OldPriority - CurrentThread->PriorityDecrement - 1;
458
459 /* Don't go out of bounds */
460 if (NewPriority < CurrentThread->BasePriority) NewPriority = CurrentThread->BasePriority;
461
462 /* Reset the priority decrement */
463 CurrentThread->PriorityDecrement = 0;
464
465 /* Set a new priority if needed */
466 if (OldPriority != NewPriority) {
467
468 /* Set new Priority */
469 BOOLEAN Dummy; /* <- This is a hack anyways... */
470 KiSetPriorityThread(CurrentThread, NewPriority, &Dummy);
471
472 } else {
473
474 /* Queue new thread if none is already */
475 if (Prcb->NextThread == NULL) {
476
477 /* FIXME: Schedule a New Thread, when ROS will have NT Scheduler */
478
479 } else {
480
481 /* Make the current thread non-premeptive if a new thread is queued */
482 CurrentThread->Preempted = FALSE;
483 }
484 }
485
486
487 } else {
488 /* Set the Quantum back to Maximum */
489 //if (CurrentThread->DisableQuantum) {
490 // CurrentThread->Quantum = MAX_QUANTUM;
491 //}
492 }
493 }
494
495 /* Dispatch the Thread */
496 KeLowerIrql(DISPATCH_LEVEL);
497 KiDispatchThread(Ready);
498 }
499
500 /*
501 * @implemented
502 *
503 * FUNCTION:
504 * Called whenever a system interrupt is generated at DISPATCH_LEVEL.
505 * It delivers queued DPCs and dispatches a new thread if need be.
506 */
507 VOID
508 STDCALL
509 KiDispatchInterrupt(VOID)
510 {
511 PLIST_ENTRY DpcEntry;
512 PKDPC Dpc;
513 KIRQL OldIrql;
514 PKPRCB Prcb;
515
516 DPRINT("Dispatching Interrupts\n");
517 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
518
519 /* Set DPC Deliver to Active */
520 Prcb = KeGetCurrentPrcb();
521
522 if (Prcb->DpcData[0].DpcQueueDepth > 0) {
523 /* Raise IRQL */
524 KeRaiseIrql(HIGH_LEVEL, &OldIrql);
525 #ifdef CONFIG_SMP
526 KiAcquireSpinLock(&Prcb->DpcData[0].DpcLock);
527 #endif
528 Prcb->DpcRoutineActive = TRUE;
529
530 DPRINT("&Prcb->DpcData[0].DpcListHead: %x\n", &Prcb->DpcData[0].DpcListHead);
531 /* Loop while we have entries */
532 while (!IsListEmpty(&Prcb->DpcData[0].DpcListHead)) {
533
534 ASSERT(Prcb->DpcData[0].DpcQueueDepth > 0);
535 DPRINT("Queue Depth: %x\n", Prcb->DpcData[0].DpcQueueDepth);
536
537 /* Get the DPC call it */
538 DpcEntry = RemoveHeadList(&Prcb->DpcData[0].DpcListHead);
539 Dpc = CONTAINING_RECORD(DpcEntry, KDPC, DpcListEntry);
540 DPRINT("Dpc->DpcListEntry.Flink %x\n", Dpc->DpcListEntry.Flink);
541 Dpc->DpcData = NULL;
542 Prcb->DpcData[0].DpcQueueDepth--;
543 #ifdef CONFIG_SMP
544 KiReleaseSpinLock(&Prcb->DpcData[0].DpcLock);
545 #endif
546 /* Disable/Enabled Interrupts and Call the DPC */
547 KeLowerIrql(OldIrql);
548 DPRINT("Calling DPC: %x\n", Dpc);
549 Dpc->DeferredRoutine(Dpc,
550 Dpc->DeferredContext,
551 Dpc->SystemArgument1,
552 Dpc->SystemArgument2);
553 KeRaiseIrql(HIGH_LEVEL, &OldIrql);
554
555 #ifdef CONFIG_SMP
556 KiAcquireSpinLock(&Prcb->DpcData[0].DpcLock);
557 /*
558 * If the dpc routine drops the irql below DISPATCH_LEVEL,
559 * a thread switch can occur and after the next thread switch
560 * the execution may start on an other processor.
561 */
562 if (Prcb != KeGetCurrentPrcb()) {
563
564 Prcb->DpcRoutineActive = FALSE;
565 KiReleaseSpinLock(&Prcb->DpcData[0].DpcLock);
566 Prcb = KeGetCurrentPrcb();
567 KiAcquireSpinLock(&Prcb->DpcData[0].DpcLock);
568 Prcb->DpcRoutineActive = TRUE;
569 }
570 #endif
571 }
572 /* Clear DPC Flags */
573 Prcb->DpcRoutineActive = FALSE;
574 Prcb->DpcInterruptRequested = FALSE;
575 #ifdef CONFIG_SMP
576 KiReleaseSpinLock(&Prcb->DpcData[0].DpcLock);
577 #endif
578
579 /* DPC Dispatching Ended, re-enable interrupts */
580 KeLowerIrql(OldIrql);
581 }
582
583 DPRINT("Checking for Quantum End\n");
584
585 /* If we have Quantum End, call the function */
586 if (Prcb->QuantumEnd) {
587
588 Prcb->QuantumEnd = FALSE;
589 KiQuantumEnd();
590 }
591 }
592
593 /* EOF */