1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ke/dpc.c
5 * PURPOSE: Deferred Procedure Call (DPC) Support
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 * Philip Susi (phreak@iag.net)
8 * Eric Kohl
9 */
10
11 /* INCLUDES ******************************************************************/
12
13 #include <ntoskrnl.h>
14 #define NDEBUG
15 #include <debug.h>
16
17 /* GLOBALS *******************************************************************/
18
19 ULONG KiMaximumDpcQueueDepth = 4;
20 ULONG KiMinimumDpcRate = 3;
21 ULONG KiAdjustDpcThreshold = 20;
22 ULONG KiIdealDpcRate = 20;
23 BOOLEAN KeThreadDpcEnable;
24 FAST_MUTEX KiGenericCallDpcMutex;
25 KDPC KiTimerExpireDpc;
26 ULONG KiTimeLimitIsrMicroseconds;
27 ULONG KiDPCTimeout = 110;
28
29 /* PRIVATE FUNCTIONS *********************************************************/
30
31 VOID
32 NTAPI
KiCheckTimerTable(IN ULARGE_INTEGER CurrentTime)33 KiCheckTimerTable(IN ULARGE_INTEGER CurrentTime)
34 {
35 #if DBG
36 ULONG i = 0;
37 PLIST_ENTRY ListHead, NextEntry;
38 KIRQL OldIrql;
39 PKTIMER Timer;
40
41 /* Raise IRQL to high and loop timers */
42 KeRaiseIrql(HIGH_LEVEL, &OldIrql);
43 do
44 {
45 /* Loop the current list */
46 ListHead = &KiTimerTableListHead[i].Entry;
47 NextEntry = ListHead->Flink;
48 while (NextEntry != ListHead)
49 {
50 /* Get the timer and move to the next one */
51 Timer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry);
52 NextEntry = NextEntry->Flink;
53
54 /* Check if it expired */
55 if (Timer->DueTime.QuadPart <= CurrentTime.QuadPart)
56 {
57 /* Check if the DPC was queued, but didn't run */
58 if (!(KeGetCurrentPrcb()->TimerRequest) &&
59 !(*((volatile PULONG*)(&KiTimerExpireDpc.DpcData))))
60 {
61 /* This is bad, breakpoint! */
62 DPRINT1("Invalid timer state!\n");
63 DbgBreakPoint();
64 }
65 }
66 }
67
68 /* Move to the next timer */
69 i++;
70 } while(i < TIMER_TABLE_SIZE);
71
72 /* Lower IRQL and return */
73 KeLowerIrql(OldIrql);
74 #endif
75 }
76
77 VOID
78 NTAPI
KiTimerExpiration(IN PKDPC Dpc,IN PVOID DeferredContext,IN PVOID SystemArgument1,IN PVOID SystemArgument2)79 KiTimerExpiration(IN PKDPC Dpc,
80 IN PVOID DeferredContext,
81 IN PVOID SystemArgument1,
82 IN PVOID SystemArgument2)
83 {
84 ULARGE_INTEGER SystemTime, InterruptTime;
85 LARGE_INTEGER Interval;
86 LONG Limit, Index, i;
87 ULONG Timers, ActiveTimers, DpcCalls;
88 PLIST_ENTRY ListHead, NextEntry;
89 KIRQL OldIrql;
90 PKTIMER Timer;
91 PKDPC TimerDpc;
92 ULONG Period;
93 DPC_QUEUE_ENTRY DpcEntry[MAX_TIMER_DPCS];
94 PKSPIN_LOCK_QUEUE LockQueue;
95 PKPRCB Prcb = KeGetCurrentPrcb();
96
97 /* Disable interrupts */
98 _disable();
99
100 /* Query system and interrupt time */
101 KeQuerySystemTime((PLARGE_INTEGER)&SystemTime);
102 InterruptTime.QuadPart = KeQueryInterruptTime();
103 Limit = KeTickCount.LowPart;
104
105 /* Bring interrupts back */
106 _enable();
107
108 /* Get the index of the timer and normalize it */
109 Index = PtrToLong(SystemArgument1);
110 if ((Limit - Index) >= TIMER_TABLE_SIZE)
111 {
112 /* Normalize it */
113 Limit = Index + TIMER_TABLE_SIZE - 1;
114 }
115
116 /* Setup index and actual limit */
117 Index--;
118 Limit &= (TIMER_TABLE_SIZE - 1);
119
120 /* Setup accounting data */
121 DpcCalls = 0;
122 Timers = 24;
123 ActiveTimers = 4;
124
125 /* Lock the Database and Raise IRQL */
126 OldIrql = KiAcquireDispatcherLock();
127
128 /* Start expiration loop */
129 do
130 {
131 /* Get the current index */
132 Index = (Index + 1) & (TIMER_TABLE_SIZE - 1);
133
134 /* Get list pointers and loop the list */
135 ListHead = &KiTimerTableListHead[Index].Entry;
136 while (ListHead != ListHead->Flink)
137 {
138 /* Lock the timer and go to the next entry */
139 LockQueue = KiAcquireTimerLock(Index);
140 NextEntry = ListHead->Flink;
141
142 /* Get the current timer and check its due time */
143 Timers--;
144 Timer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry);
145 if ((NextEntry != ListHead) &&
146 (Timer->DueTime.QuadPart <= InterruptTime.QuadPart))
147 {
148 /* It's expired, remove it */
149 ActiveTimers--;
150 KiRemoveEntryTimer(Timer);
151
152 /* Make it non-inserted, unlock it, and signal it */
153 Timer->Header.Inserted = FALSE;
154 KiReleaseTimerLock(LockQueue);
155 Timer->Header.SignalState = 1;
156
157 /* Get the DPC and period */
158 TimerDpc = Timer->Dpc;
159 Period = Timer->Period;
160
161 /* Check if there's any waiters */
162 if (!IsListEmpty(&Timer->Header.WaitListHead))
163 {
164 /* Check the type of event */
165 if (Timer->Header.Type == TimerNotificationObject)
166 {
167 /* Unwait the thread */
168 KxUnwaitThread(&Timer->Header, IO_NO_INCREMENT);
169 }
170 else
171 {
172 /* Otherwise unwait the thread and signal the timer */
173 KxUnwaitThreadForEvent((PKEVENT)Timer, IO_NO_INCREMENT);
174 }
175 }
176
177 /* Check if we have a period */
178 if (Period)
179 {
180 /* Calculate the interval and insert the timer */
181 Interval.QuadPart = Int32x32To64(Period, -10000);
182 while (!KiInsertTreeTimer(Timer, Interval));
183 }
184
185 /* Check if we have a DPC */
186 if (TimerDpc)
187 {
188 #ifdef CONFIG_SMP
189 /*
190 * If the DPC is targeted to another processor,
191 * then insert it into that processor's DPC queue
192 * instead of delivering it now.
193 * If the DPC is a threaded DPC, and the current CPU
194 * has threaded DPCs enabled (KiExecuteDpc is actively parsing DPCs),
195 * then also insert it into the DPC queue for threaded delivery,
196 * instead of doing it here.
197 */
198 if (((TimerDpc->Number >= MAXIMUM_PROCESSORS) &&
199 ((TimerDpc->Number - MAXIMUM_PROCESSORS) != Prcb->Number)) ||
200 ((TimerDpc->Type == ThreadedDpcObject) && (Prcb->ThreadDpcEnable)))
201 {
202 /* Queue it */
203 KeInsertQueueDpc(TimerDpc,
204 UlongToPtr(SystemTime.LowPart),
205 UlongToPtr(SystemTime.HighPart));
206 }
207 else
208 #endif
209 {
210 /* Setup the DPC Entry */
211 DpcEntry[DpcCalls].Dpc = TimerDpc;
212 DpcEntry[DpcCalls].Routine = TimerDpc->DeferredRoutine;
213 DpcEntry[DpcCalls].Context = TimerDpc->DeferredContext;
214 DpcCalls++;
215 ASSERT(DpcCalls < MAX_TIMER_DPCS);
216 }
217 }
218
219 /* Check if we're done processing */
220 if (!(ActiveTimers) || !(Timers))
221 {
222 /* Release the dispatcher while doing DPCs */
223 KiReleaseDispatcherLock(DISPATCH_LEVEL);
224
225 /* Start looping all DPC Entries */
226 for (i = 0; DpcCalls; DpcCalls--, i++)
227 {
228 #if DBG
229 /* Clear DPC Time */
230 Prcb->DebugDpcTime = 0;
231 #endif
232
233 /* Call the DPC */
234 DpcEntry[i].Routine(DpcEntry[i].Dpc,
235 DpcEntry[i].Context,
236 UlongToPtr(SystemTime.LowPart),
237 UlongToPtr(SystemTime.HighPart));
238 }
239
240 /* Reset accounting */
241 Timers = 24;
242 ActiveTimers = 4;
243
244 /* Lock the dispatcher database */
245 KiAcquireDispatcherLock();
246 }
247 }
248 else
249 {
250 /* Check if the timer list is empty */
251 if (NextEntry != ListHead)
252 {
253 /* Sanity check */
254 ASSERT(KiTimerTableListHead[Index].Time.QuadPart <=
255 Timer->DueTime.QuadPart);
256
257 /* Update the time */
258 _disable();
259 KiTimerTableListHead[Index].Time.QuadPart =
260 Timer->DueTime.QuadPart;
261 _enable();
262 }
263
264 /* Release the lock */
265 KiReleaseTimerLock(LockQueue);
266
267 /* Check if we've scanned all the timers we could */
268 if (!Timers)
269 {
270 /* Release the dispatcher while doing DPCs */
271 KiReleaseDispatcherLock(DISPATCH_LEVEL);
272
273 /* Start looping all DPC Entries */
274 for (i = 0; DpcCalls; DpcCalls--, i++)
275 {
276 #if DBG
277 /* Clear DPC Time */
278 Prcb->DebugDpcTime = 0;
279 #endif
280
281 /* Call the DPC */
282 DpcEntry[i].Routine(DpcEntry[i].Dpc,
283 DpcEntry[i].Context,
284 UlongToPtr(SystemTime.LowPart),
285 UlongToPtr(SystemTime.HighPart));
286 }
287
288 /* Reset accounting */
289 Timers = 24;
290 ActiveTimers = 4;
291
292 /* Lock the dispatcher database */
293 KiAcquireDispatcherLock();
294 }
295
296 /* Done looping */
297 break;
298 }
299 }
300 } while (Index != Limit);
301
302 /* Verify the timer table, on debug builds */
303 if (KeNumberProcessors == 1) KiCheckTimerTable(InterruptTime);
304
305 /* Check if we still have DPC entries */
306 if (DpcCalls)
307 {
308 /* Release the dispatcher while doing DPCs */
309 KiReleaseDispatcherLock(DISPATCH_LEVEL);
310
311 /* Start looping all DPC Entries */
312 for (i = 0; DpcCalls; DpcCalls--, i++)
313 {
314 #if DBG
315 /* Clear DPC Time */
316 Prcb->DebugDpcTime = 0;
317 #endif
318
319 /* Call the DPC */
320 DpcEntry[i].Routine(DpcEntry[i].Dpc,
321 DpcEntry[i].Context,
322 UlongToPtr(SystemTime.LowPart),
323 UlongToPtr(SystemTime.HighPart));
324 }
325
326 /* Lower IRQL if we need to */
327 if (OldIrql != DISPATCH_LEVEL) KeLowerIrql(OldIrql);
328 }
329 else
330 {
331 /* Unlock the dispatcher */
332 KiReleaseDispatcherLock(OldIrql);
333 }
334 }
335
336 VOID
337 FASTCALL
KiTimerListExpire(IN PLIST_ENTRY ExpiredListHead,IN KIRQL OldIrql)338 KiTimerListExpire(IN PLIST_ENTRY ExpiredListHead,
339 IN KIRQL OldIrql)
340 {
341 ULARGE_INTEGER SystemTime;
342 LARGE_INTEGER Interval;
343 LONG i;
344 ULONG DpcCalls = 0;
345 PKTIMER Timer;
346 PKDPC TimerDpc;
347 ULONG Period;
348 DPC_QUEUE_ENTRY DpcEntry[MAX_TIMER_DPCS];
349 PKPRCB Prcb = KeGetCurrentPrcb();
350
351 /* Query system */
352 KeQuerySystemTime((PLARGE_INTEGER)&SystemTime);
353
354 /* Loop expired list */
355 while (ExpiredListHead->Flink != ExpiredListHead)
356 {
357 /* Get the current timer */
358 Timer = CONTAINING_RECORD(ExpiredListHead->Flink, KTIMER, TimerListEntry);
359
360 /* Remove it */
361 RemoveEntryList(&Timer->TimerListEntry);
362
363 /* Not inserted */
364 Timer->Header.Inserted = FALSE;
365
366 /* Signal it */
367 Timer->Header.SignalState = 1;
368
369 /* Get the DPC and period */
370 TimerDpc = Timer->Dpc;
371 Period = Timer->Period;
372
373 /* Check if there's any waiters */
374 if (!IsListEmpty(&Timer->Header.WaitListHead))
375 {
376 /* Check the type of event */
377 if (Timer->Header.Type == TimerNotificationObject)
378 {
379 /* Unwait the thread */
380 KxUnwaitThread(&Timer->Header, IO_NO_INCREMENT);
381 }
382 else
383 {
384 /* Otherwise unwait the thread and signal the timer */
385 KxUnwaitThreadForEvent((PKEVENT)Timer, IO_NO_INCREMENT);
386 }
387 }
388
389 /* Check if we have a period */
390 if (Period)
391 {
392 /* Calculate the interval and insert the timer */
393 Interval.QuadPart = Int32x32To64(Period, -10000);
394 while (!KiInsertTreeTimer(Timer, Interval));
395 }
396
397 /* Check if we have a DPC */
398 if (TimerDpc)
399 {
400 #ifdef CONFIG_SMP
401 /*
402 * If the DPC is targeted to another processor,
403 * then insert it into that processor's DPC queue
404 * instead of delivering it now.
405 * If the DPC is a threaded DPC, and the current CPU
406 * has threaded DPCs enabled (KiExecuteDpc is actively parsing DPCs),
407 * then also insert it into the DPC queue for threaded delivery,
408 * instead of doing it here.
409 */
410 if (((TimerDpc->Number >= MAXIMUM_PROCESSORS) &&
411 ((TimerDpc->Number - MAXIMUM_PROCESSORS) != Prcb->Number)) ||
412 ((TimerDpc->Type == ThreadedDpcObject) && (Prcb->ThreadDpcEnable)))
413 {
414 /* Queue it */
415 KeInsertQueueDpc(TimerDpc,
416 UlongToPtr(SystemTime.LowPart),
417 UlongToPtr(SystemTime.HighPart));
418 }
419 else
420 #endif
421 {
422 /* Setup the DPC Entry */
423 DpcEntry[DpcCalls].Dpc = TimerDpc;
424 DpcEntry[DpcCalls].Routine = TimerDpc->DeferredRoutine;
425 DpcEntry[DpcCalls].Context = TimerDpc->DeferredContext;
426 DpcCalls++;
427 ASSERT(DpcCalls < MAX_TIMER_DPCS);
428 }
429 }
430 }
431
432 /* Check if we still have DPC entries */
433 if (DpcCalls)
434 {
435 /* Release the dispatcher while doing DPCs */
436 KiReleaseDispatcherLock(DISPATCH_LEVEL);
437
438 /* Start looping all DPC Entries */
439 for (i = 0; DpcCalls; DpcCalls--, i++)
440 {
441 #if DBG
442 /* Clear DPC Time */
443 Prcb->DebugDpcTime = 0;
444 #endif
445
446 /* Call the DPC */
447 DpcEntry[i].Routine(DpcEntry[i].Dpc,
448 DpcEntry[i].Context,
449 UlongToPtr(SystemTime.LowPart),
450 UlongToPtr(SystemTime.HighPart));
451 }
452
453 /* Lower IRQL */
454 KeLowerIrql(OldIrql);
455 }
456 else
457 {
458 /* Unlock the dispatcher */
459 KiReleaseDispatcherLock(OldIrql);
460 }
461 }
462
463 _Requires_lock_not_held_(Prcb->PrcbLock)
464 VOID
465 NTAPI
KiQuantumEnd(VOID)466 KiQuantumEnd(VOID)
467 {
468 PKPRCB Prcb = KeGetCurrentPrcb();
469 PKTHREAD NextThread, Thread = Prcb->CurrentThread;
470
471 /* Check if a DPC Event was requested to be signaled */
472 if (InterlockedExchange(&Prcb->DpcSetEventRequest, 0))
473 {
474 /* Signal it */
475 KeSetEvent(&Prcb->DpcEvent, 0, 0);
476 }
477
478 /* Raise to synchronization level and lock the PRCB and thread */
479 KeRaiseIrqlToSynchLevel();
480 KiAcquireThreadLock(Thread);
481 KiAcquirePrcbLock(Prcb);
482
483 /* Check if Quantum expired */
484 if (Thread->Quantum <= 0)
485 {
486 /* Check if we're real-time and with quantums disabled */
487 if ((Thread->Priority >= LOW_REALTIME_PRIORITY) &&
488 (Thread->ApcState.Process->DisableQuantum))
489 {
490 /* Otherwise, set maximum quantum */
491 Thread->Quantum = MAX_QUANTUM;
492 }
493 else
494 {
495 /* Reset the new Quantum */
496 Thread->Quantum = Thread->QuantumReset;
497
498 /* Calculate new priority */
499 Thread->Priority = KiComputeNewPriority(Thread, 1);
500
501 /* Check if a new thread is scheduled */
502 if (!Prcb->NextThread)
503 {
504 /* Get a new ready thread */
505 NextThread = KiSelectReadyThread(Thread->Priority, Prcb);
506 if (NextThread)
507 {
508 /* Found one, set it on standby */
509 NextThread->State = Standby;
510 Prcb->NextThread = NextThread;
511 }
512 }
513 else
514 {
515 /* Otherwise, make sure that this thread doesn't get preempted */
516 Thread->Preempted = FALSE;
517 }
518 }
519 }
520
521 /* Release the thread lock */
522 KiReleaseThreadLock(Thread);
523
524 /* Check if there's no thread scheduled */
525 if (!Prcb->NextThread)
526 {
527 /* Just leave now */
528 KiReleasePrcbLock(Prcb);
529 KeLowerIrql(DISPATCH_LEVEL);
530 return;
531 }
532
533 /* Get the next thread now */
534 NextThread = Prcb->NextThread;
535
536 /* Set current thread's swap busy to true */
537 KiSetThreadSwapBusy(Thread);
538
539 /* Switch threads in PRCB */
540 Prcb->NextThread = NULL;
541 Prcb->CurrentThread = NextThread;
542
543 /* Set thread to running and the switch reason to Quantum End */
544 NextThread->State = Running;
545 Thread->WaitReason = WrQuantumEnd;
546
547 /* Queue it on the ready lists */
548 KxQueueReadyThread(Thread, Prcb);
549
550 /* Set wait IRQL to APC_LEVEL */
551 Thread->WaitIrql = APC_LEVEL;
552
553 /* Swap threads */
554 KiSwapContext(APC_LEVEL, Thread);
555
556 /* Lower IRQL back to DISPATCH_LEVEL */
557 KeLowerIrql(DISPATCH_LEVEL);
558 }
559
560 VOID
561 FASTCALL
KiRetireDpcList(IN PKPRCB Prcb)562 KiRetireDpcList(IN PKPRCB Prcb)
563 {
564 PKDPC_DATA DpcData;
565 PLIST_ENTRY ListHead, DpcEntry;
566 PKDPC Dpc;
567 PKDEFERRED_ROUTINE DeferredRoutine;
568 PVOID DeferredContext, SystemArgument1, SystemArgument2;
569 ULONG_PTR TimerHand;
570 #ifdef CONFIG_SMP
571 KIRQL OldIrql;
572 #endif
573
574 /* Get data and list variables before starting anything else */
575 DpcData = &Prcb->DpcData[DPC_NORMAL];
576 ListHead = &DpcData->DpcListHead;
577
578 /* Main outer loop */
579 do
580 {
581 /* Set us as active */
582 Prcb->DpcRoutineActive = TRUE;
583
584 /* Check if this is a timer expiration request */
585 if (Prcb->TimerRequest)
586 {
587 /* It is, get the timer hand and disable timer request */
588 TimerHand = Prcb->TimerHand;
589 Prcb->TimerRequest = 0;
590
591 /* Expire timers with interrupts enabled */
592 _enable();
593 KiTimerExpiration(NULL, NULL, (PVOID)TimerHand, NULL);
594 _disable();
595 }
596
597 /* Loop while we have entries in the queue */
598 while (DpcData->DpcQueueDepth != 0)
599 {
600 /* Lock the DPC data and get the DPC entry*/
601 KeAcquireSpinLockAtDpcLevel(&DpcData->DpcLock);
602 DpcEntry = ListHead->Flink;
603
604 /* Make sure we have an entry */
605 if (DpcEntry != ListHead)
606 {
607 /* Remove the DPC from the list */
608 RemoveEntryList(DpcEntry);
609 Dpc = CONTAINING_RECORD(DpcEntry, KDPC, DpcListEntry);
610
611 /* Clear its DPC data and save its parameters */
612 Dpc->DpcData = NULL;
613 DeferredRoutine = Dpc->DeferredRoutine;
614 DeferredContext = Dpc->DeferredContext;
615 SystemArgument1 = Dpc->SystemArgument1;
616 SystemArgument2 = Dpc->SystemArgument2;
617
618 /* Decrease the queue depth */
619 DpcData->DpcQueueDepth--;
620
621 #if DBG
622 /* Clear DPC Time */
623 Prcb->DebugDpcTime = 0;
624 #endif
625
626 /* Release the lock */
627 KeReleaseSpinLockFromDpcLevel(&DpcData->DpcLock);
628
629 /* Re-enable interrupts */
630 _enable();
631
632 /* Call the DPC */
633 DeferredRoutine(Dpc,
634 DeferredContext,
635 SystemArgument1,
636 SystemArgument2);
637 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
638
639 /* Disable interrupts and keep looping */
640 _disable();
641 }
642 else
643 {
644 /* The queue should be flushed now */
645 ASSERT(DpcData->DpcQueueDepth == 0);
646
647 /* Release DPC Lock */
648 KeReleaseSpinLockFromDpcLevel(&DpcData->DpcLock);
649 }
650 }
651
652 /* Clear DPC Flags */
653 Prcb->DpcRoutineActive = FALSE;
654 Prcb->DpcInterruptRequested = FALSE;
655
656 #ifdef CONFIG_SMP
657 /* Check if we have deferred threads */
658 if (Prcb->DeferredReadyListHead.Next)
659 {
660
661 /* Re-enable interrupts and raise to synch */
662 _enable();
663 OldIrql = KeRaiseIrqlToSynchLevel();
664
665 /* Process deferred threads */
666 KiProcessDeferredReadyList(Prcb);
667
668 /* Lower IRQL back and disable interrupts */
669 KeLowerIrql(OldIrql);
670 _disable();
671 }
672 #endif
673 } while (DpcData->DpcQueueDepth != 0);
674 }
675
676 VOID
677 NTAPI
KiInitializeDpc(IN PKDPC Dpc,IN PKDEFERRED_ROUTINE DeferredRoutine,IN PVOID DeferredContext,IN KOBJECTS Type)678 KiInitializeDpc(IN PKDPC Dpc,
679 IN PKDEFERRED_ROUTINE DeferredRoutine,
680 IN PVOID DeferredContext,
681 IN KOBJECTS Type)
682 {
683 /* Setup the DPC Object */
684 Dpc->Type = Type;
685 Dpc->Number = 0;
686 Dpc->Importance= MediumImportance;
687 Dpc->DeferredRoutine = DeferredRoutine;
688 Dpc->DeferredContext = DeferredContext;
689 Dpc->DpcData = NULL;
690 }
691
692 /* PUBLIC FUNCTIONS **********************************************************/
693
694 /*
695 * @implemented
696 */
697 VOID
698 NTAPI
KeInitializeThreadedDpc(IN PKDPC Dpc,IN PKDEFERRED_ROUTINE DeferredRoutine,IN PVOID DeferredContext)699 KeInitializeThreadedDpc(IN PKDPC Dpc,
700 IN PKDEFERRED_ROUTINE DeferredRoutine,
701 IN PVOID DeferredContext)
702 {
703 /* Call the internal routine */
704 KiInitializeDpc(Dpc, DeferredRoutine, DeferredContext, ThreadedDpcObject);
705 }
706
707 /*
708 * @implemented
709 */
710 VOID
711 NTAPI
KeInitializeDpc(IN PKDPC Dpc,IN PKDEFERRED_ROUTINE DeferredRoutine,IN PVOID DeferredContext)712 KeInitializeDpc(IN PKDPC Dpc,
713 IN PKDEFERRED_ROUTINE DeferredRoutine,
714 IN PVOID DeferredContext)
715 {
716 /* Call the internal routine */
717 KiInitializeDpc(Dpc, DeferredRoutine, DeferredContext, DpcObject);
718 }
719
720 /*
721 * @implemented
722 */
723 BOOLEAN
724 NTAPI
KeInsertQueueDpc(IN PKDPC Dpc,IN PVOID SystemArgument1,IN PVOID SystemArgument2)725 KeInsertQueueDpc(IN PKDPC Dpc,
726 IN PVOID SystemArgument1,
727 IN PVOID SystemArgument2)
728 {
729 KIRQL OldIrql;
730 PKPRCB Prcb, CurrentPrcb;
731 ULONG Cpu;
732 PKDPC_DATA DpcData;
733 BOOLEAN DpcConfigured = FALSE, DpcInserted = FALSE;
734 ASSERT_DPC(Dpc);
735
736 /* Check IRQL and Raise it to HIGH_LEVEL */
737 KeRaiseIrql(HIGH_LEVEL, &OldIrql);
738 CurrentPrcb = KeGetCurrentPrcb();
739
740 /* Check if the DPC has more then the maximum number of CPUs */
741 if (Dpc->Number >= MAXIMUM_PROCESSORS)
742 {
743 /* Then substract the maximum and get that PRCB. */
744 Cpu = Dpc->Number - MAXIMUM_PROCESSORS;
745 Prcb = KiProcessorBlock[Cpu];
746 }
747 else
748 {
749 /* Use the current one */
750 Prcb = CurrentPrcb;
751 Cpu = Prcb->Number;
752 }
753
754 /* ROS Sanity Check */
755 ASSERT(Prcb == CurrentPrcb);
756
757 /* Check if this is a threaded DPC and threaded DPCs are enabled */
758 if ((Dpc->Type == ThreadedDpcObject) && (Prcb->ThreadDpcEnable))
759 {
760 /* Then use the threaded data */
761 DpcData = &Prcb->DpcData[DPC_THREADED];
762 }
763 else
764 {
765 /* Otherwise, use the regular data */
766 DpcData = &Prcb->DpcData[DPC_NORMAL];
767 }
768
769 /* Acquire the DPC lock */
770 KiAcquireSpinLock(&DpcData->DpcLock);
771
772 /* Get the DPC Data */
773 if (!InterlockedCompareExchangePointer(&Dpc->DpcData, DpcData, NULL))
774 {
775 /* Now we can play with the DPC safely */
776 Dpc->SystemArgument1 = SystemArgument1;
777 Dpc->SystemArgument2 = SystemArgument2;
778 DpcData->DpcQueueDepth++;
779 DpcData->DpcCount++;
780 DpcConfigured = TRUE;
781
782 /* Check if this is a high importance DPC */
783 if (Dpc->Importance == HighImportance)
784 {
785 /* Pre-empty other DPCs */
786 InsertHeadList(&DpcData->DpcListHead, &Dpc->DpcListEntry);
787 }
788 else
789 {
790 /* Add it at the end */
791 InsertTailList(&DpcData->DpcListHead, &Dpc->DpcListEntry);
792 }
793
794 /* Check if this is the DPC on the threaded list */
795 if (&Prcb->DpcData[DPC_THREADED] == DpcData)
796 {
797 /* Make sure a threaded DPC isn't already active */
798 if (!(Prcb->DpcThreadActive) && !(Prcb->DpcThreadRequested))
799 {
800 /* FIXME: Setup Threaded DPC */
801 UNIMPLEMENTED_FATAL("Threaded DPC not supported\n");
802 }
803 }
804 else
805 {
806 /* Make sure a DPC isn't executing already */
807 if (!(Prcb->DpcRoutineActive) && !(Prcb->DpcInterruptRequested))
808 {
809 /* Check if this is the same CPU */
810 if (Prcb != CurrentPrcb)
811 {
812 /*
813 * Check if the DPC is of high importance or above the
814 * maximum depth. If it is, then make sure that the CPU
815 * isn't idle, or that it's sleeping.
816 */
817 if (((Dpc->Importance == HighImportance) ||
818 (DpcData->DpcQueueDepth >=
819 Prcb->MaximumDpcQueueDepth)) &&
820 (!(AFFINITY_MASK(Cpu) & KiIdleSummary) ||
821 (Prcb->Sleeping)))
822 {
823 /* Set interrupt requested */
824 Prcb->DpcInterruptRequested = TRUE;
825
826 /* Set DPC inserted */
827 DpcInserted = TRUE;
828 }
829 }
830 else
831 {
832 /* Check if the DPC is of anything but low importance */
833 if ((Dpc->Importance != LowImportance) ||
834 (DpcData->DpcQueueDepth >=
835 Prcb->MaximumDpcQueueDepth) ||
836 (Prcb->DpcRequestRate < Prcb->MinimumDpcRate))
837 {
838 /* Set interrupt requested */
839 Prcb->DpcInterruptRequested = TRUE;
840
841 /* Set DPC inserted */
842 DpcInserted = TRUE;
843 }
844 }
845 }
846 }
847 }
848
849 /* Release the lock */
850 KiReleaseSpinLock(&DpcData->DpcLock);
851
852 /* Check if the DPC was inserted */
853 if (DpcInserted)
854 {
855 /* Check if this was SMP */
856 if (Prcb != CurrentPrcb)
857 {
858 /* It was, request and IPI */
859 KiIpiSend(AFFINITY_MASK(Cpu), IPI_DPC);
860 }
861 else
862 {
863 /* It wasn't, request an interrupt from HAL */
864 HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
865 }
866 }
867
868 /* Lower IRQL */
869 KeLowerIrql(OldIrql);
870 return DpcConfigured;
871 }
872
873 /*
874 * @implemented
875 */
876 BOOLEAN
877 NTAPI
KeRemoveQueueDpc(IN PKDPC Dpc)878 KeRemoveQueueDpc(IN PKDPC Dpc)
879 {
880 PKDPC_DATA DpcData;
881 BOOLEAN Enable;
882 ASSERT_DPC(Dpc);
883
884 /* Disable interrupts */
885 Enable = KeDisableInterrupts();
886
887 /* Get DPC data */
888 DpcData = Dpc->DpcData;
889 if (DpcData)
890 {
891 /* Acquire the DPC lock */
892 KiAcquireSpinLock(&DpcData->DpcLock);
893
894 /* Make sure that the data didn't change */
895 if (DpcData == Dpc->DpcData)
896 {
897 /* Remove the DPC */
898 DpcData->DpcQueueDepth--;
899 RemoveEntryList(&Dpc->DpcListEntry);
900 Dpc->DpcData = NULL;
901 }
902
903 /* Release the lock */
904 KiReleaseSpinLock(&DpcData->DpcLock);
905 }
906
907 /* Re-enable interrupts */
908 if (Enable) _enable();
909
910 /* Return if the DPC was in the queue or not */
911 return DpcData ? TRUE : FALSE;
912 }
913
914 /*
915 * @implemented
916 */
_IRQL_requires_max_(APC_LEVEL)917 _IRQL_requires_max_(APC_LEVEL)
918 VOID
919 NTAPI
920 KeFlushQueuedDpcs(VOID)
921 {
922 ULONG ProcessorIndex;
923 PKPRCB TargetPrcb;
924
925 PAGED_CODE();
926 ASSERT(KeGetCurrentThread()->SystemAffinityActive == FALSE);
927
928 /* Loop all processors */
929 for (ProcessorIndex = 0; ProcessorIndex < KeNumberProcessors; ProcessorIndex++)
930 {
931 /* Get the target processor's PRCB */
932 TargetPrcb = KiProcessorBlock[ProcessorIndex];
933
934 /* Check if there are DPCs on either queues */
935 if ((TargetPrcb->DpcData[DPC_NORMAL].DpcQueueDepth > 0) ||
936 (TargetPrcb->DpcData[DPC_THREADED].DpcQueueDepth > 0))
937 {
938 /* Check if this is the current processor */
939 if (TargetPrcb == KeGetCurrentPrcb())
940 {
941 /* Request a DPC interrupt */
942 HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
943 }
944 else
945 {
946 /* Attach to the target processor. This will cause a DPC
947 interrupt on the target processor and flush all DPCs. */
948 KeSetSystemAffinityThread(TargetPrcb->SetMember);
949 }
950 }
951 }
952
953 /* Revert back to user affinity */
954 if (KeGetCurrentThread()->SystemAffinityActive)
955 {
956 KeRevertToUserAffinityThread();
957 }
958 }
959
960 /*
961 * @implemented
962 */
963 BOOLEAN
964 NTAPI
KeIsExecutingDpc(VOID)965 KeIsExecutingDpc(VOID)
966 {
967 /* Return if the Dpc Routine is active */
968 return KeGetCurrentPrcb()->DpcRoutineActive;
969 }
970
971 /*
972 * @implemented
973 */
974 VOID
975 NTAPI
KeSetImportanceDpc(IN PKDPC Dpc,IN KDPC_IMPORTANCE Importance)976 KeSetImportanceDpc (IN PKDPC Dpc,
977 IN KDPC_IMPORTANCE Importance)
978 {
979 /* Set the DPC Importance */
980 ASSERT_DPC(Dpc);
981 Dpc->Importance = Importance;
982 }
983
984 /*
985 * @implemented
986 */
987 VOID
988 NTAPI
KeSetTargetProcessorDpc(IN PKDPC Dpc,IN CCHAR Number)989 KeSetTargetProcessorDpc(IN PKDPC Dpc,
990 IN CCHAR Number)
991 {
992 /* Set a target CPU */
993 ASSERT_DPC(Dpc);
994 Dpc->Number = Number + MAXIMUM_PROCESSORS;
995 }
996
997 /*
998 * @implemented
999 */
1000 VOID
1001 NTAPI
KeGenericCallDpc(IN PKDEFERRED_ROUTINE Routine,IN PVOID Context)1002 KeGenericCallDpc(IN PKDEFERRED_ROUTINE Routine,
1003 IN PVOID Context)
1004 {
1005 ULONG Barrier = KeNumberProcessors;
1006 KIRQL OldIrql;
1007 DEFERRED_REVERSE_BARRIER ReverseBarrier;
1008 ASSERT(KeGetCurrentIrql () < DISPATCH_LEVEL);
1009
1010 //
1011 // The barrier is the number of processors, each processor will decrement it
1012 // by one, so when all processors have run the DPC, the barrier reaches zero
1013 //
1014 ReverseBarrier.Barrier = Barrier;
1015 ReverseBarrier.TotalProcessors = Barrier;
1016
1017 //
1018 // But we don't need the barrier on UP, since we can simply call the routine
1019 // directly while at DISPATCH_LEVEL and not worry about anything else
1020 //
1021 KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
1022 Routine(&KeGetCurrentPrcb()->CallDpc, Context, &Barrier, &ReverseBarrier);
1023 KeLowerIrql(OldIrql);
1024 }
1025
1026 /*
1027 * @implemented
1028 */
1029 VOID
1030 NTAPI
KeSignalCallDpcDone(IN PVOID SystemArgument1)1031 KeSignalCallDpcDone(IN PVOID SystemArgument1)
1032 {
1033 //
1034 // Decrement the barrier, which is actually the processor count
1035 //
1036 InterlockedDecrement((PLONG)SystemArgument1);
1037 }
1038
1039 /*
1040 * @implemented
1041 */
1042 BOOLEAN
1043 NTAPI
KeSignalCallDpcSynchronize(IN PVOID SystemArgument2)1044 KeSignalCallDpcSynchronize(IN PVOID SystemArgument2)
1045 {
1046 //
1047 // There is nothing to do on UP systems -- the processor calling this wins
1048 //
1049 UNREFERENCED_PARAMETER(SystemArgument2);
1050 return TRUE;
1051 }
1052
1053 /* EOF */
1054