Git conversion: Make reactos the root directory, move rosapps, rostests, wallpapers...
[reactos.git] / 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: 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
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
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
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 VOID
464 NTAPI
465 KiQuantumEnd(VOID)
466 {
467 PKPRCB Prcb = KeGetCurrentPrcb();
468 PKTHREAD NextThread, Thread = Prcb->CurrentThread;
469
470 /* Check if a DPC Event was requested to be signaled */
471 if (InterlockedExchange(&Prcb->DpcSetEventRequest, 0))
472 {
473 /* Signal it */
474 KeSetEvent(&Prcb->DpcEvent, 0, 0);
475 }
476
477 /* Raise to synchronization level and lock the PRCB and thread */
478 KeRaiseIrqlToSynchLevel();
479 KiAcquireThreadLock(Thread);
480 KiAcquirePrcbLock(Prcb);
481
482 /* Check if Quantum expired */
483 if (Thread->Quantum <= 0)
484 {
485 /* Check if we're real-time and with quantums disabled */
486 if ((Thread->Priority >= LOW_REALTIME_PRIORITY) &&
487 (Thread->ApcState.Process->DisableQuantum))
488 {
489 /* Otherwise, set maximum quantum */
490 Thread->Quantum = MAX_QUANTUM;
491 }
492 else
493 {
494 /* Reset the new Quantum */
495 Thread->Quantum = Thread->QuantumReset;
496
497 /* Calculate new priority */
498 Thread->Priority = KiComputeNewPriority(Thread, 1);
499
500 /* Check if a new thread is scheduled */
501 if (!Prcb->NextThread)
502 {
503 /* Get a new ready thread */
504 NextThread = KiSelectReadyThread(Thread->Priority, Prcb);
505 if (NextThread)
506 {
507 /* Found one, set it on standby */
508 NextThread->State = Standby;
509 Prcb->NextThread = NextThread;
510 }
511 }
512 else
513 {
514 /* Otherwise, make sure that this thread doesn't get preempted */
515 Thread->Preempted = FALSE;
516 }
517 }
518 }
519
520 /* Release the thread lock */
521 KiReleaseThreadLock(Thread);
522
523 /* Check if there's no thread scheduled */
524 if (!Prcb->NextThread)
525 {
526 /* Just leave now */
527 KiReleasePrcbLock(Prcb);
528 KeLowerIrql(DISPATCH_LEVEL);
529 return;
530 }
531
532 /* Get the next thread now */
533 NextThread = Prcb->NextThread;
534
535 /* Set current thread's swap busy to true */
536 KiSetThreadSwapBusy(Thread);
537
538 /* Switch threads in PRCB */
539 Prcb->NextThread = NULL;
540 Prcb->CurrentThread = NextThread;
541
542 /* Set thread to running and the switch reason to Quantum End */
543 NextThread->State = Running;
544 Thread->WaitReason = WrQuantumEnd;
545
546 /* Queue it on the ready lists */
547 KxQueueReadyThread(Thread, Prcb);
548
549 /* Set wait IRQL to APC_LEVEL */
550 Thread->WaitIrql = APC_LEVEL;
551
552 /* Swap threads */
553 KiSwapContext(APC_LEVEL, Thread);
554
555 /* Lower IRQL back to DISPATCH_LEVEL */
556 KeLowerIrql(DISPATCH_LEVEL);
557 }
558
559 VOID
560 FASTCALL
561 KiRetireDpcList(IN PKPRCB Prcb)
562 {
563 PKDPC_DATA DpcData;
564 PLIST_ENTRY ListHead, DpcEntry;
565 PKDPC Dpc;
566 PKDEFERRED_ROUTINE DeferredRoutine;
567 PVOID DeferredContext, SystemArgument1, SystemArgument2;
568 ULONG_PTR TimerHand;
569 #ifdef CONFIG_SMP
570 KIRQL OldIrql;
571 #endif
572
573 /* Get data and list variables before starting anything else */
574 DpcData = &Prcb->DpcData[DPC_NORMAL];
575 ListHead = &DpcData->DpcListHead;
576
577 /* Main outer loop */
578 do
579 {
580 /* Set us as active */
581 Prcb->DpcRoutineActive = TRUE;
582
583 /* Check if this is a timer expiration request */
584 if (Prcb->TimerRequest)
585 {
586 /* It is, get the timer hand and disable timer request */
587 TimerHand = Prcb->TimerHand;
588 Prcb->TimerRequest = 0;
589
590 /* Expire timers with interrups enabled */
591 _enable();
592 KiTimerExpiration(NULL, NULL, (PVOID)TimerHand, NULL);
593 _disable();
594 }
595
596 /* Loop while we have entries in the queue */
597 while (DpcData->DpcQueueDepth != 0)
598 {
599 /* Lock the DPC data and get the DPC entry*/
600 KeAcquireSpinLockAtDpcLevel(&DpcData->DpcLock);
601 DpcEntry = ListHead->Flink;
602
603 /* Make sure we have an entry */
604 if (DpcEntry != ListHead)
605 {
606 /* Remove the DPC from the list */
607 RemoveEntryList(DpcEntry);
608 Dpc = CONTAINING_RECORD(DpcEntry, KDPC, DpcListEntry);
609
610 /* Clear its DPC data and save its parameters */
611 Dpc->DpcData = NULL;
612 DeferredRoutine = Dpc->DeferredRoutine;
613 DeferredContext = Dpc->DeferredContext;
614 SystemArgument1 = Dpc->SystemArgument1;
615 SystemArgument2 = Dpc->SystemArgument2;
616
617 /* Decrease the queue depth */
618 DpcData->DpcQueueDepth--;
619
620 #if DBG
621 /* Clear DPC Time */
622 Prcb->DebugDpcTime = 0;
623 #endif
624
625 /* Release the lock */
626 KeReleaseSpinLockFromDpcLevel(&DpcData->DpcLock);
627
628 /* Re-enable interrupts */
629 _enable();
630
631 /* Call the DPC */
632 DeferredRoutine(Dpc,
633 DeferredContext,
634 SystemArgument1,
635 SystemArgument2);
636 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
637
638 /* Disable interrupts and keep looping */
639 _disable();
640 }
641 else
642 {
643 /* The queue should be flushed now */
644 ASSERT(DpcData->DpcQueueDepth == 0);
645
646 /* Release DPC Lock */
647 KeReleaseSpinLockFromDpcLevel(&DpcData->DpcLock);
648 }
649 }
650
651 /* Clear DPC Flags */
652 Prcb->DpcRoutineActive = FALSE;
653 Prcb->DpcInterruptRequested = FALSE;
654
655 #ifdef CONFIG_SMP
656 /* Check if we have deferred threads */
657 if (Prcb->DeferredReadyListHead.Next)
658 {
659
660 /* Re-enable interrupts and raise to synch */
661 _enable();
662 OldIrql = KeRaiseIrqlToSynchLevel();
663
664 /* Process deferred threads */
665 KiProcessDeferredReadyList(Prcb);
666
667 /* Lower IRQL back and disable interrupts */
668 KeLowerIrql(OldIrql);
669 _disable();
670 }
671 #endif
672 } while (DpcData->DpcQueueDepth != 0);
673 }
674
675 VOID
676 NTAPI
677 KiInitializeDpc(IN PKDPC Dpc,
678 IN PKDEFERRED_ROUTINE DeferredRoutine,
679 IN PVOID DeferredContext,
680 IN KOBJECTS Type)
681 {
682 /* Setup the DPC Object */
683 Dpc->Type = Type;
684 Dpc->Number = 0;
685 Dpc->Importance= MediumImportance;
686 Dpc->DeferredRoutine = DeferredRoutine;
687 Dpc->DeferredContext = DeferredContext;
688 Dpc->DpcData = NULL;
689 }
690
691 /* PUBLIC FUNCTIONS **********************************************************/
692
693 /*
694 * @implemented
695 */
696 VOID
697 NTAPI
698 KeInitializeThreadedDpc(IN PKDPC Dpc,
699 IN PKDEFERRED_ROUTINE DeferredRoutine,
700 IN PVOID DeferredContext)
701 {
702 /* Call the internal routine */
703 KiInitializeDpc(Dpc, DeferredRoutine, DeferredContext, ThreadedDpcObject);
704 }
705
706 /*
707 * @implemented
708 */
709 VOID
710 NTAPI
711 KeInitializeDpc(IN PKDPC Dpc,
712 IN PKDEFERRED_ROUTINE DeferredRoutine,
713 IN PVOID DeferredContext)
714 {
715 /* Call the internal routine */
716 KiInitializeDpc(Dpc, DeferredRoutine, DeferredContext, DpcObject);
717 }
718
719 /*
720 * @implemented
721 */
722 BOOLEAN
723 NTAPI
724 KeInsertQueueDpc(IN PKDPC Dpc,
725 IN PVOID SystemArgument1,
726 IN PVOID SystemArgument2)
727 {
728 KIRQL OldIrql;
729 PKPRCB Prcb, CurrentPrcb;
730 ULONG Cpu;
731 PKDPC_DATA DpcData;
732 BOOLEAN DpcConfigured = FALSE, DpcInserted = FALSE;
733 ASSERT_DPC(Dpc);
734
735 /* Check IRQL and Raise it to HIGH_LEVEL */
736 KeRaiseIrql(HIGH_LEVEL, &OldIrql);
737 CurrentPrcb = KeGetCurrentPrcb();
738
739 /* Check if the DPC has more then the maximum number of CPUs */
740 if (Dpc->Number >= MAXIMUM_PROCESSORS)
741 {
742 /* Then substract the maximum and get that PRCB. */
743 Cpu = Dpc->Number - MAXIMUM_PROCESSORS;
744 Prcb = KiProcessorBlock[Cpu];
745 }
746 else
747 {
748 /* Use the current one */
749 Prcb = CurrentPrcb;
750 Cpu = Prcb->Number;
751 }
752
753 /* ROS Sanity Check */
754 ASSERT(Prcb == CurrentPrcb);
755
756 /* Check if this is a threaded DPC and threaded DPCs are enabled */
757 if ((Dpc->Type == ThreadedDpcObject) && (Prcb->ThreadDpcEnable))
758 {
759 /* Then use the threaded data */
760 DpcData = &Prcb->DpcData[DPC_THREADED];
761 }
762 else
763 {
764 /* Otherwise, use the regular data */
765 DpcData = &Prcb->DpcData[DPC_NORMAL];
766 }
767
768 /* Acquire the DPC lock */
769 KiAcquireSpinLock(&DpcData->DpcLock);
770
771 /* Get the DPC Data */
772 if (!InterlockedCompareExchangePointer(&Dpc->DpcData, DpcData, NULL))
773 {
774 /* Now we can play with the DPC safely */
775 Dpc->SystemArgument1 = SystemArgument1;
776 Dpc->SystemArgument2 = SystemArgument2;
777 DpcData->DpcQueueDepth++;
778 DpcData->DpcCount++;
779 DpcConfigured = TRUE;
780
781 /* Check if this is a high importance DPC */
782 if (Dpc->Importance == HighImportance)
783 {
784 /* Pre-empty other DPCs */
785 InsertHeadList(&DpcData->DpcListHead, &Dpc->DpcListEntry);
786 }
787 else
788 {
789 /* Add it at the end */
790 InsertTailList(&DpcData->DpcListHead, &Dpc->DpcListEntry);
791 }
792
793 /* Check if this is the DPC on the threaded list */
794 if (&Prcb->DpcData[DPC_THREADED] == DpcData)
795 {
796 /* Make sure a threaded DPC isn't already active */
797 if (!(Prcb->DpcThreadActive) && !(Prcb->DpcThreadRequested))
798 {
799 /* FIXME: Setup Threaded DPC */
800 UNIMPLEMENTED_FATAL("Threaded DPC not supported\n");
801 }
802 }
803 else
804 {
805 /* Make sure a DPC isn't executing already */
806 if (!(Prcb->DpcRoutineActive) && !(Prcb->DpcInterruptRequested))
807 {
808 /* Check if this is the same CPU */
809 if (Prcb != CurrentPrcb)
810 {
811 /*
812 * Check if the DPC is of high importance or above the
813 * maximum depth. If it is, then make sure that the CPU
814 * isn't idle, or that it's sleeping.
815 */
816 if (((Dpc->Importance == HighImportance) ||
817 (DpcData->DpcQueueDepth >=
818 Prcb->MaximumDpcQueueDepth)) &&
819 (!(AFFINITY_MASK(Cpu) & KiIdleSummary) ||
820 (Prcb->Sleeping)))
821 {
822 /* Set interrupt requested */
823 Prcb->DpcInterruptRequested = TRUE;
824
825 /* Set DPC inserted */
826 DpcInserted = TRUE;
827 }
828 }
829 else
830 {
831 /* Check if the DPC is of anything but low importance */
832 if ((Dpc->Importance != LowImportance) ||
833 (DpcData->DpcQueueDepth >=
834 Prcb->MaximumDpcQueueDepth) ||
835 (Prcb->DpcRequestRate < Prcb->MinimumDpcRate))
836 {
837 /* Set interrupt requested */
838 Prcb->DpcInterruptRequested = TRUE;
839
840 /* Set DPC inserted */
841 DpcInserted = TRUE;
842 }
843 }
844 }
845 }
846 }
847
848 /* Release the lock */
849 KiReleaseSpinLock(&DpcData->DpcLock);
850
851 /* Check if the DPC was inserted */
852 if (DpcInserted)
853 {
854 /* Check if this was SMP */
855 if (Prcb != CurrentPrcb)
856 {
857 /* It was, request and IPI */
858 KiIpiSend(AFFINITY_MASK(Cpu), IPI_DPC);
859 }
860 else
861 {
862 /* It wasn't, request an interrupt from HAL */
863 HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
864 }
865 }
866
867 /* Lower IRQL */
868 KeLowerIrql(OldIrql);
869 return DpcConfigured;
870 }
871
872 /*
873 * @implemented
874 */
875 BOOLEAN
876 NTAPI
877 KeRemoveQueueDpc(IN PKDPC Dpc)
878 {
879 PKDPC_DATA DpcData;
880 BOOLEAN Enable;
881 ASSERT_DPC(Dpc);
882
883 /* Disable interrupts */
884 Enable = KeDisableInterrupts();
885
886 /* Get DPC data */
887 DpcData = Dpc->DpcData;
888 if (DpcData)
889 {
890 /* Acquire the DPC lock */
891 KiAcquireSpinLock(&DpcData->DpcLock);
892
893 /* Make sure that the data didn't change */
894 if (DpcData == Dpc->DpcData)
895 {
896 /* Remove the DPC */
897 DpcData->DpcQueueDepth--;
898 RemoveEntryList(&Dpc->DpcListEntry);
899 Dpc->DpcData = NULL;
900 }
901
902 /* Release the lock */
903 KiReleaseSpinLock(&DpcData->DpcLock);
904 }
905
906 /* Re-enable interrupts */
907 if (Enable) _enable();
908
909 /* Return if the DPC was in the queue or not */
910 return DpcData ? TRUE : FALSE;
911 }
912
913 /*
914 * @implemented
915 */
916 VOID
917 NTAPI
918 KeFlushQueuedDpcs(VOID)
919 {
920 PKPRCB CurrentPrcb = KeGetCurrentPrcb();
921 PAGED_CODE();
922
923 /* Check if this is an UP machine */
924 if (KeActiveProcessors == 1)
925 {
926 /* Check if there are DPCs on either queues */
927 if ((CurrentPrcb->DpcData[DPC_NORMAL].DpcQueueDepth > 0) ||
928 (CurrentPrcb->DpcData[DPC_THREADED].DpcQueueDepth > 0))
929 {
930 /* Request an interrupt */
931 HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
932 }
933 }
934 else
935 {
936 /* FIXME: SMP support required */
937 ASSERT(FALSE);
938 }
939 }
940
941 /*
942 * @implemented
943 */
944 BOOLEAN
945 NTAPI
946 KeIsExecutingDpc(VOID)
947 {
948 /* Return if the Dpc Routine is active */
949 return KeGetCurrentPrcb()->DpcRoutineActive;
950 }
951
952 /*
953 * @implemented
954 */
955 VOID
956 NTAPI
957 KeSetImportanceDpc (IN PKDPC Dpc,
958 IN KDPC_IMPORTANCE Importance)
959 {
960 /* Set the DPC Importance */
961 ASSERT_DPC(Dpc);
962 Dpc->Importance = Importance;
963 }
964
965 /*
966 * @implemented
967 */
968 VOID
969 NTAPI
970 KeSetTargetProcessorDpc(IN PKDPC Dpc,
971 IN CCHAR Number)
972 {
973 /* Set a target CPU */
974 ASSERT_DPC(Dpc);
975 Dpc->Number = Number + MAXIMUM_PROCESSORS;
976 }
977
978 /*
979 * @implemented
980 */
981 VOID
982 NTAPI
983 KeGenericCallDpc(IN PKDEFERRED_ROUTINE Routine,
984 IN PVOID Context)
985 {
986 ULONG Barrier = KeNumberProcessors;
987 KIRQL OldIrql;
988 DEFERRED_REVERSE_BARRIER ReverseBarrier;
989 ASSERT(KeGetCurrentIrql () < DISPATCH_LEVEL);
990
991 //
992 // The barrier is the number of processors, each processor will decrement it
993 // by one, so when all processors have run the DPC, the barrier reaches zero
994 //
995 ReverseBarrier.Barrier = Barrier;
996 ReverseBarrier.TotalProcessors = Barrier;
997
998 //
999 // But we don't need the barrier on UP, since we can simply call the routine
1000 // directly while at DISPATCH_LEVEL and not worry about anything else
1001 //
1002 KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
1003 Routine(&KeGetCurrentPrcb()->CallDpc, Context, &Barrier, &ReverseBarrier);
1004 KeLowerIrql(OldIrql);
1005 }
1006
1007 /*
1008 * @implemented
1009 */
1010 VOID
1011 NTAPI
1012 KeSignalCallDpcDone(IN PVOID SystemArgument1)
1013 {
1014 //
1015 // Decrement the barrier, which is actually the processor count
1016 //
1017 InterlockedDecrement((PLONG)SystemArgument1);
1018 }
1019
1020 /*
1021 * @implemented
1022 */
1023 BOOLEAN
1024 NTAPI
1025 KeSignalCallDpcSynchronize(IN PVOID SystemArgument2)
1026 {
1027 //
1028 // There is nothing to do on UP systems -- the processor calling this wins
1029 //
1030 UNREFERENCED_PARAMETER(SystemArgument2);
1031 return TRUE;
1032 }
1033
1034 /* EOF */