Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygendpc.c
Go to the documentation of this file.
00001 /* 00002 * PROJECT: ReactOS Kernel 00003 * LICENSE: GPL - See COPYING in the top level directory 00004 * FILE: ntoskrnl/ke/dpc.c 00005 * PURPOSE: Deferred Procedure Call (DPC) Support 00006 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) 00007 * Philip Susi (phreak@iag.net) 00008 * Eric Kohl 00009 */ 00010 00011 /* INCLUDES ******************************************************************/ 00012 00013 #include <ntoskrnl.h> 00014 #define NDEBUG 00015 #include <debug.h> 00016 00017 /* GLOBALS *******************************************************************/ 00018 00019 ULONG KiMaximumDpcQueueDepth = 4; 00020 ULONG KiMinimumDpcRate = 3; 00021 ULONG KiAdjustDpcThreshold = 20; 00022 ULONG KiIdealDpcRate = 20; 00023 BOOLEAN KeThreadDpcEnable; 00024 FAST_MUTEX KiGenericCallDpcMutex; 00025 KDPC KiTimerExpireDpc; 00026 ULONG KiTimeLimitIsrMicroseconds; 00027 ULONG KiDPCTimeout = 110; 00028 00029 /* PRIVATE FUNCTIONS *********************************************************/ 00030 00031 VOID 00032 NTAPI 00033 KiCheckTimerTable(IN ULARGE_INTEGER CurrentTime) 00034 { 00035 #if DBG 00036 ULONG i = 0; 00037 PLIST_ENTRY ListHead, NextEntry; 00038 KIRQL OldIrql; 00039 PKTIMER Timer; 00040 00041 /* Raise IRQL to high and loop timers */ 00042 KeRaiseIrql(HIGH_LEVEL, &OldIrql); 00043 do 00044 { 00045 /* Loop the current list */ 00046 ListHead = &KiTimerTableListHead[i].Entry; 00047 NextEntry = ListHead->Flink; 00048 while (NextEntry != ListHead) 00049 { 00050 /* Get the timer and move to the next one */ 00051 Timer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry); 00052 NextEntry = NextEntry->Flink; 00053 00054 /* Check if it expired */ 00055 if (Timer->DueTime.QuadPart <= CurrentTime.QuadPart) 00056 { 00057 /* Check if the DPC was queued, but didn't run */ 00058 if (!(KeGetCurrentPrcb()->TimerRequest) && 00059 !(*((volatile PULONG*)(&KiTimerExpireDpc.DpcData)))) 00060 { 00061 /* This is bad, breakpoint! */ 00062 DPRINT1("Invalid timer state!\n"); 00063 DbgBreakPoint(); 00064 } 00065 } 00066 } 00067 00068 /* Move to the next timer */ 00069 i++; 00070 } while(i < TIMER_TABLE_SIZE); 00071 00072 /* Lower IRQL and return */ 00073 KeLowerIrql(OldIrql); 00074 #endif 00075 } 00076 00077 VOID 00078 NTAPI 00079 KiTimerExpiration(IN PKDPC Dpc, 00080 IN PVOID DeferredContext, 00081 IN PVOID SystemArgument1, 00082 IN PVOID SystemArgument2) 00083 { 00084 ULARGE_INTEGER SystemTime, InterruptTime; 00085 LARGE_INTEGER Interval; 00086 LONG Limit, Index, i; 00087 ULONG Timers, ActiveTimers, DpcCalls; 00088 PLIST_ENTRY ListHead, NextEntry; 00089 KIRQL OldIrql; 00090 PKTIMER Timer; 00091 PKDPC TimerDpc; 00092 ULONG Period; 00093 DPC_QUEUE_ENTRY DpcEntry[MAX_TIMER_DPCS]; 00094 PKSPIN_LOCK_QUEUE LockQueue; 00095 #ifdef CONFIG_SMP 00096 PKPRCB Prcb = KeGetCurrentPrcb(); 00097 #endif 00098 00099 /* Disable interrupts */ 00100 _disable(); 00101 00102 /* Query system and interrupt time */ 00103 KeQuerySystemTime((PLARGE_INTEGER)&SystemTime); 00104 InterruptTime.QuadPart = KeQueryInterruptTime(); 00105 Limit = KeTickCount.LowPart; 00106 00107 /* Bring interrupts back */ 00108 _enable(); 00109 00110 /* Get the index of the timer and normalize it */ 00111 Index = PtrToLong(SystemArgument1); 00112 if ((Limit - Index) >= TIMER_TABLE_SIZE) 00113 { 00114 /* Normalize it */ 00115 Limit = Index + TIMER_TABLE_SIZE - 1; 00116 } 00117 00118 /* Setup index and actual limit */ 00119 Index--; 00120 Limit &= (TIMER_TABLE_SIZE - 1); 00121 00122 /* Setup accounting data */ 00123 DpcCalls = 0; 00124 Timers = 24; 00125 ActiveTimers = 4; 00126 00127 /* Lock the Database and Raise IRQL */ 00128 OldIrql = KiAcquireDispatcherLock(); 00129 00130 /* Start expiration loop */ 00131 do 00132 { 00133 /* Get the current index */ 00134 Index = (Index + 1) & (TIMER_TABLE_SIZE - 1); 00135 00136 /* Get list pointers and loop the list */ 00137 ListHead = &KiTimerTableListHead[Index].Entry; 00138 while (ListHead != ListHead->Flink) 00139 { 00140 /* Lock the timer and go to the next entry */ 00141 LockQueue = KiAcquireTimerLock(Index); 00142 NextEntry = ListHead->Flink; 00143 00144 /* Get the current timer and check its due time */ 00145 Timers--; 00146 Timer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry); 00147 if ((NextEntry != ListHead) && 00148 (Timer->DueTime.QuadPart <= InterruptTime.QuadPart)) 00149 { 00150 /* It's expired, remove it */ 00151 ActiveTimers--; 00152 KiRemoveEntryTimer(Timer); 00153 00154 /* Make it non-inserted, unlock it, and signal it */ 00155 Timer->Header.Inserted = FALSE; 00156 KiReleaseTimerLock(LockQueue); 00157 Timer->Header.SignalState = 1; 00158 00159 /* Get the DPC and period */ 00160 TimerDpc = Timer->Dpc; 00161 Period = Timer->Period; 00162 00163 /* Check if there's any waiters */ 00164 if (!IsListEmpty(&Timer->Header.WaitListHead)) 00165 { 00166 /* Check the type of event */ 00167 if (Timer->Header.Type == TimerNotificationObject) 00168 { 00169 /* Unwait the thread */ 00170 KxUnwaitThread(&Timer->Header, IO_NO_INCREMENT); 00171 } 00172 else 00173 { 00174 /* Otherwise unwait the thread and signal the timer */ 00175 KxUnwaitThreadForEvent((PKEVENT)Timer, IO_NO_INCREMENT); 00176 } 00177 } 00178 00179 /* Check if we have a period */ 00180 if (Period) 00181 { 00182 /* Calculate the interval and insert the timer */ 00183 Interval.QuadPart = Int32x32To64(Period, -10000); 00184 while (!KiInsertTreeTimer(Timer, Interval)); 00185 } 00186 00187 /* Check if we have a DPC */ 00188 if (TimerDpc) 00189 { 00190 #ifdef CONFIG_SMP 00191 /* 00192 * If the DPC is targeted to another processor, 00193 * then insert it into that processor's DPC queue 00194 * instead of delivering it now. 00195 * If the DPC is a threaded DPC, and the current CPU 00196 * has threaded DPCs enabled (KiExecuteDpc is actively parsing DPCs), 00197 * then also insert it into the DPC queue for threaded delivery, 00198 * instead of doing it here. 00199 */ 00200 if (((TimerDpc->Number >= MAXIMUM_PROCESSORS) && 00201 ((TimerDpc->Number - MAXIMUM_PROCESSORS) != Prcb->Number)) || 00202 ((TimerDpc->Type == ThreadedDpcObject) && (Prcb->ThreadDpcEnable))) 00203 { 00204 /* Queue it */ 00205 KeInsertQueueDpc(TimerDpc, 00206 UlongToPtr(SystemTime.LowPart), 00207 UlongToPtr(SystemTime.HighPart)); 00208 } 00209 else 00210 #endif 00211 { 00212 /* Setup the DPC Entry */ 00213 DpcEntry[DpcCalls].Dpc = TimerDpc; 00214 DpcEntry[DpcCalls].Routine = TimerDpc->DeferredRoutine; 00215 DpcEntry[DpcCalls].Context = TimerDpc->DeferredContext; 00216 DpcCalls++; 00217 ASSERT(DpcCalls < MAX_TIMER_DPCS); 00218 } 00219 } 00220 00221 /* Check if we're done processing */ 00222 if (!(ActiveTimers) || !(Timers)) 00223 { 00224 /* Release the dispatcher while doing DPCs */ 00225 KiReleaseDispatcherLock(DISPATCH_LEVEL); 00226 00227 /* Start looping all DPC Entries */ 00228 for (i = 0; DpcCalls; DpcCalls--, i++) 00229 { 00230 /* Call the DPC */ 00231 DpcEntry[i].Routine(DpcEntry[i].Dpc, 00232 DpcEntry[i].Context, 00233 UlongToPtr(SystemTime.LowPart), 00234 UlongToPtr(SystemTime.HighPart)); 00235 } 00236 00237 /* Reset accounting */ 00238 Timers = 24; 00239 ActiveTimers = 4; 00240 00241 /* Lock the dispatcher database */ 00242 KiAcquireDispatcherLock(); 00243 } 00244 } 00245 else 00246 { 00247 /* Check if the timer list is empty */ 00248 if (NextEntry != ListHead) 00249 { 00250 /* Sanity check */ 00251 ASSERT(KiTimerTableListHead[Index].Time.QuadPart <= 00252 Timer->DueTime.QuadPart); 00253 00254 /* Update the time */ 00255 _disable(); 00256 KiTimerTableListHead[Index].Time.QuadPart = 00257 Timer->DueTime.QuadPart; 00258 _enable(); 00259 } 00260 00261 /* Release the lock */ 00262 KiReleaseTimerLock(LockQueue); 00263 00264 /* Check if we've scanned all the timers we could */ 00265 if (!Timers) 00266 { 00267 /* Release the dispatcher while doing DPCs */ 00268 KiReleaseDispatcherLock(DISPATCH_LEVEL); 00269 00270 /* Start looping all DPC Entries */ 00271 for (i = 0; DpcCalls; DpcCalls--, i++) 00272 { 00273 /* Call the DPC */ 00274 DpcEntry[i].Routine(DpcEntry[i].Dpc, 00275 DpcEntry[i].Context, 00276 UlongToPtr(SystemTime.LowPart), 00277 UlongToPtr(SystemTime.HighPart)); 00278 } 00279 00280 /* Reset accounting */ 00281 Timers = 24; 00282 ActiveTimers = 4; 00283 00284 /* Lock the dispatcher database */ 00285 KiAcquireDispatcherLock(); 00286 } 00287 00288 /* Done looping */ 00289 break; 00290 } 00291 } 00292 } while (Index != Limit); 00293 00294 /* Verify the timer table, on debug builds */ 00295 if (KeNumberProcessors == 1) KiCheckTimerTable(InterruptTime); 00296 00297 /* Check if we still have DPC entries */ 00298 if (DpcCalls) 00299 { 00300 /* Release the dispatcher while doing DPCs */ 00301 KiReleaseDispatcherLock(DISPATCH_LEVEL); 00302 00303 /* Start looping all DPC Entries */ 00304 for (i = 0; DpcCalls; DpcCalls--, i++) 00305 { 00306 /* Call the DPC */ 00307 DpcEntry[i].Routine(DpcEntry[i].Dpc, 00308 DpcEntry[i].Context, 00309 UlongToPtr(SystemTime.LowPart), 00310 UlongToPtr(SystemTime.HighPart)); 00311 } 00312 00313 /* Lower IRQL if we need to */ 00314 if (OldIrql != DISPATCH_LEVEL) KeLowerIrql(OldIrql); 00315 } 00316 else 00317 { 00318 /* Unlock the dispatcher */ 00319 KiReleaseDispatcherLock(OldIrql); 00320 } 00321 } 00322 00323 VOID 00324 FASTCALL 00325 KiTimerListExpire(IN PLIST_ENTRY ExpiredListHead, 00326 IN KIRQL OldIrql) 00327 { 00328 ULARGE_INTEGER SystemTime; 00329 LARGE_INTEGER Interval; 00330 LONG i; 00331 ULONG DpcCalls = 0; 00332 PKTIMER Timer; 00333 PKDPC TimerDpc; 00334 ULONG Period; 00335 DPC_QUEUE_ENTRY DpcEntry[MAX_TIMER_DPCS]; 00336 #ifdef CONFIG_SMP 00337 PKPRCB Prcb = KeGetCurrentPrcb(); 00338 #endif 00339 00340 /* Query system */ 00341 KeQuerySystemTime((PLARGE_INTEGER)&SystemTime); 00342 00343 /* Loop expired list */ 00344 while (ExpiredListHead->Flink != ExpiredListHead) 00345 { 00346 /* Get the current timer */ 00347 Timer = CONTAINING_RECORD(ExpiredListHead->Flink, KTIMER, TimerListEntry); 00348 00349 /* Remove it */ 00350 RemoveEntryList(&Timer->TimerListEntry); 00351 00352 /* Not inserted */ 00353 Timer->Header.Inserted = FALSE; 00354 00355 /* Signal it */ 00356 Timer->Header.SignalState = 1; 00357 00358 /* Get the DPC and period */ 00359 TimerDpc = Timer->Dpc; 00360 Period = Timer->Period; 00361 00362 /* Check if there's any waiters */ 00363 if (!IsListEmpty(&Timer->Header.WaitListHead)) 00364 { 00365 /* Check the type of event */ 00366 if (Timer->Header.Type == TimerNotificationObject) 00367 { 00368 /* Unwait the thread */ 00369 KxUnwaitThread(&Timer->Header, IO_NO_INCREMENT); 00370 } 00371 else 00372 { 00373 /* Otherwise unwait the thread and signal the timer */ 00374 KxUnwaitThreadForEvent((PKEVENT)Timer, IO_NO_INCREMENT); 00375 } 00376 } 00377 00378 /* Check if we have a period */ 00379 if (Period) 00380 { 00381 /* Calculate the interval and insert the timer */ 00382 Interval.QuadPart = Int32x32To64(Period, -10000); 00383 while (!KiInsertTreeTimer(Timer, Interval)); 00384 } 00385 00386 /* Check if we have a DPC */ 00387 if (TimerDpc) 00388 { 00389 #ifdef CONFIG_SMP 00390 /* 00391 * If the DPC is targeted to another processor, 00392 * then insert it into that processor's DPC queue 00393 * instead of delivering it now. 00394 * If the DPC is a threaded DPC, and the current CPU 00395 * has threaded DPCs enabled (KiExecuteDpc is actively parsing DPCs), 00396 * then also insert it into the DPC queue for threaded delivery, 00397 * instead of doing it here. 00398 */ 00399 if (((TimerDpc->Number >= MAXIMUM_PROCESSORS) && 00400 ((TimerDpc->Number - MAXIMUM_PROCESSORS) != Prcb->Number)) || 00401 ((TimerDpc->Type == ThreadedDpcObject) && (Prcb->ThreadDpcEnable))) 00402 { 00403 /* Queue it */ 00404 KeInsertQueueDpc(TimerDpc, 00405 UlongToPtr(SystemTime.LowPart), 00406 UlongToPtr(SystemTime.HighPart)); 00407 } 00408 else 00409 #endif 00410 { 00411 /* Setup the DPC Entry */ 00412 DpcEntry[DpcCalls].Dpc = TimerDpc; 00413 DpcEntry[DpcCalls].Routine = TimerDpc->DeferredRoutine; 00414 DpcEntry[DpcCalls].Context = TimerDpc->DeferredContext; 00415 DpcCalls++; 00416 ASSERT(DpcCalls < MAX_TIMER_DPCS); 00417 } 00418 } 00419 } 00420 00421 /* Check if we still have DPC entries */ 00422 if (DpcCalls) 00423 { 00424 /* Release the dispatcher while doing DPCs */ 00425 KiReleaseDispatcherLock(DISPATCH_LEVEL); 00426 00427 /* Start looping all DPC Entries */ 00428 for (i = 0; DpcCalls; DpcCalls--, i++) 00429 { 00430 /* Call the DPC */ 00431 DpcEntry[i].Routine(DpcEntry[i].Dpc, 00432 DpcEntry[i].Context, 00433 UlongToPtr(SystemTime.LowPart), 00434 UlongToPtr(SystemTime.HighPart)); 00435 } 00436 00437 /* Lower IRQL */ 00438 KeLowerIrql(OldIrql); 00439 } 00440 else 00441 { 00442 /* Unlock the dispatcher */ 00443 KiReleaseDispatcherLock(OldIrql); 00444 } 00445 } 00446 00447 VOID 00448 NTAPI 00449 KiQuantumEnd(VOID) 00450 { 00451 PKPRCB Prcb = KeGetCurrentPrcb(); 00452 PKTHREAD NextThread, Thread = Prcb->CurrentThread; 00453 00454 /* Check if a DPC Event was requested to be signaled */ 00455 if (InterlockedExchange(&Prcb->DpcSetEventRequest, 0)) 00456 { 00457 /* Signal it */ 00458 KeSetEvent(&Prcb->DpcEvent, 0, 0); 00459 } 00460 00461 /* Raise to synchronization level and lock the PRCB and thread */ 00462 KeRaiseIrqlToSynchLevel(); 00463 KiAcquireThreadLock(Thread); 00464 KiAcquirePrcbLock(Prcb); 00465 00466 /* Check if Quantum expired */ 00467 if (Thread->Quantum <= 0) 00468 { 00469 /* Check if we're real-time and with quantums disabled */ 00470 if ((Thread->Priority >= LOW_REALTIME_PRIORITY) && 00471 (Thread->ApcState.Process->DisableQuantum)) 00472 { 00473 /* Otherwise, set maximum quantum */ 00474 Thread->Quantum = MAX_QUANTUM; 00475 } 00476 else 00477 { 00478 /* Reset the new Quantum */ 00479 Thread->Quantum = Thread->QuantumReset; 00480 00481 /* Calculate new priority */ 00482 Thread->Priority = KiComputeNewPriority(Thread, 1); 00483 00484 /* Check if a new thread is scheduled */ 00485 if (!Prcb->NextThread) 00486 { 00487 /* Get a new ready thread */ 00488 NextThread = KiSelectReadyThread(Thread->Priority, Prcb); 00489 if (NextThread) 00490 { 00491 /* Found one, set it on standby */ 00492 NextThread->State = Standby; 00493 Prcb->NextThread = NextThread; 00494 } 00495 } 00496 else 00497 { 00498 /* Otherwise, make sure that this thread doesn't get preempted */ 00499 Thread->Preempted = FALSE; 00500 } 00501 } 00502 } 00503 00504 /* Release the thread lock */ 00505 KiReleaseThreadLock(Thread); 00506 00507 /* Check if there's no thread scheduled */ 00508 if (!Prcb->NextThread) 00509 { 00510 /* Just leave now */ 00511 KiReleasePrcbLock(Prcb); 00512 KeLowerIrql(DISPATCH_LEVEL); 00513 return; 00514 } 00515 00516 /* Get the next thread now */ 00517 NextThread = Prcb->NextThread; 00518 00519 /* Set current thread's swap busy to true */ 00520 KiSetThreadSwapBusy(Thread); 00521 00522 /* Switch threads in PRCB */ 00523 Prcb->NextThread = NULL; 00524 Prcb->CurrentThread = NextThread; 00525 00526 /* Set thread to running and the switch reason to Quantum End */ 00527 NextThread->State = Running; 00528 Thread->WaitReason = WrQuantumEnd; 00529 00530 /* Queue it on the ready lists */ 00531 KxQueueReadyThread(Thread, Prcb); 00532 00533 /* Set wait IRQL to APC_LEVEL */ 00534 Thread->WaitIrql = APC_LEVEL; 00535 00536 /* Swap threads */ 00537 KiSwapContext(APC_LEVEL, Thread); 00538 00539 /* Lower IRQL back to DISPATCH_LEVEL */ 00540 KeLowerIrql(DISPATCH_LEVEL); 00541 } 00542 00543 VOID 00544 FASTCALL 00545 KiRetireDpcList(IN PKPRCB Prcb) 00546 { 00547 PKDPC_DATA DpcData; 00548 PLIST_ENTRY ListHead, DpcEntry; 00549 PKDPC Dpc; 00550 PKDEFERRED_ROUTINE DeferredRoutine; 00551 PVOID DeferredContext, SystemArgument1, SystemArgument2; 00552 ULONG_PTR TimerHand; 00553 #ifdef CONFIG_SMP 00554 KIRQL OldIrql; 00555 #endif 00556 00557 /* Get data and list variables before starting anything else */ 00558 DpcData = &Prcb->DpcData[DPC_NORMAL]; 00559 ListHead = &DpcData->DpcListHead; 00560 00561 /* Main outer loop */ 00562 do 00563 { 00564 /* Set us as active */ 00565 Prcb->DpcRoutineActive = TRUE; 00566 00567 /* Check if this is a timer expiration request */ 00568 if (Prcb->TimerRequest) 00569 { 00570 /* It is, get the timer hand and disable timer request */ 00571 TimerHand = Prcb->TimerHand; 00572 Prcb->TimerRequest = 0; 00573 00574 /* Expire timers with interrups enabled */ 00575 _enable(); 00576 KiTimerExpiration(NULL, NULL, (PVOID)TimerHand, NULL); 00577 _disable(); 00578 } 00579 00580 /* Loop while we have entries in the queue */ 00581 while (DpcData->DpcQueueDepth != 0) 00582 { 00583 /* Lock the DPC data and get the DPC entry*/ 00584 KeAcquireSpinLockAtDpcLevel(&DpcData->DpcLock); 00585 DpcEntry = ListHead->Flink; 00586 00587 /* Make sure we have an entry */ 00588 if (DpcEntry != ListHead) 00589 { 00590 /* Remove the DPC from the list */ 00591 RemoveEntryList(DpcEntry); 00592 Dpc = CONTAINING_RECORD(DpcEntry, KDPC, DpcListEntry); 00593 00594 /* Clear its DPC data and save its parameters */ 00595 Dpc->DpcData = NULL; 00596 DeferredRoutine = Dpc->DeferredRoutine; 00597 DeferredContext = Dpc->DeferredContext; 00598 SystemArgument1 = Dpc->SystemArgument1; 00599 SystemArgument2 = Dpc->SystemArgument2; 00600 00601 /* Decrease the queue depth */ 00602 DpcData->DpcQueueDepth--; 00603 00604 /* Clear DPC Time */ 00605 Prcb->DebugDpcTime = 0; 00606 00607 /* Release the lock */ 00608 KeReleaseSpinLockFromDpcLevel(&DpcData->DpcLock); 00609 00610 /* Re-enable interrupts */ 00611 _enable(); 00612 00613 /* Call the DPC */ 00614 DeferredRoutine(Dpc, 00615 DeferredContext, 00616 SystemArgument1, 00617 SystemArgument2); 00618 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); 00619 00620 /* Disable interrupts and keep looping */ 00621 _disable(); 00622 } 00623 else 00624 { 00625 /* The queue should be flushed now */ 00626 ASSERT(DpcData->DpcQueueDepth == 0); 00627 00628 /* Release DPC Lock */ 00629 KeReleaseSpinLockFromDpcLevel(&DpcData->DpcLock); 00630 } 00631 } 00632 00633 /* Clear DPC Flags */ 00634 Prcb->DpcRoutineActive = FALSE; 00635 Prcb->DpcInterruptRequested = FALSE; 00636 00637 #ifdef CONFIG_SMP 00638 /* Check if we have deferred threads */ 00639 if (Prcb->DeferredReadyListHead.Next) 00640 { 00641 00642 /* Re-enable interrupts and raise to synch */ 00643 _enable(); 00644 OldIrql = KeRaiseIrqlToSynchLevel(); 00645 00646 /* Process deferred threads */ 00647 KiProcessDeferredReadyList(Prcb); 00648 00649 /* Lower IRQL back and disable interrupts */ 00650 KeLowerIrql(OldIrql); 00651 _disable(); 00652 } 00653 #endif 00654 } while (DpcData->DpcQueueDepth != 0); 00655 } 00656 00657 VOID 00658 NTAPI 00659 KiInitializeDpc(IN PKDPC Dpc, 00660 IN PKDEFERRED_ROUTINE DeferredRoutine, 00661 IN PVOID DeferredContext, 00662 IN KOBJECTS Type) 00663 { 00664 /* Setup the DPC Object */ 00665 Dpc->Type = Type; 00666 Dpc->Number = 0; 00667 Dpc->Importance= MediumImportance; 00668 Dpc->DeferredRoutine = DeferredRoutine; 00669 Dpc->DeferredContext = DeferredContext; 00670 Dpc->DpcData = NULL; 00671 } 00672 00673 /* PUBLIC FUNCTIONS **********************************************************/ 00674 00675 /* 00676 * @implemented 00677 */ 00678 VOID 00679 NTAPI 00680 KeInitializeThreadedDpc(IN PKDPC Dpc, 00681 IN PKDEFERRED_ROUTINE DeferredRoutine, 00682 IN PVOID DeferredContext) 00683 { 00684 /* Call the internal routine */ 00685 KiInitializeDpc(Dpc, DeferredRoutine, DeferredContext, ThreadedDpcObject); 00686 } 00687 00688 /* 00689 * @implemented 00690 */ 00691 VOID 00692 NTAPI 00693 KeInitializeDpc(IN PKDPC Dpc, 00694 IN PKDEFERRED_ROUTINE DeferredRoutine, 00695 IN PVOID DeferredContext) 00696 { 00697 /* Call the internal routine */ 00698 KiInitializeDpc(Dpc, DeferredRoutine, DeferredContext, DpcObject); 00699 } 00700 00701 /* 00702 * @implemented 00703 */ 00704 BOOLEAN 00705 NTAPI 00706 KeInsertQueueDpc(IN PKDPC Dpc, 00707 IN PVOID SystemArgument1, 00708 IN PVOID SystemArgument2) 00709 { 00710 KIRQL OldIrql; 00711 PKPRCB Prcb, CurrentPrcb; 00712 ULONG Cpu; 00713 PKDPC_DATA DpcData; 00714 BOOLEAN DpcConfigured = FALSE, DpcInserted = FALSE; 00715 ASSERT_DPC(Dpc); 00716 00717 /* Check IRQL and Raise it to HIGH_LEVEL */ 00718 KeRaiseIrql(HIGH_LEVEL, &OldIrql); 00719 CurrentPrcb = KeGetCurrentPrcb(); 00720 00721 /* Check if the DPC has more then the maximum number of CPUs */ 00722 if (Dpc->Number >= MAXIMUM_PROCESSORS) 00723 { 00724 /* Then substract the maximum and get that PRCB. */ 00725 Cpu = Dpc->Number - MAXIMUM_PROCESSORS; 00726 Prcb = KiProcessorBlock[Cpu]; 00727 } 00728 else 00729 { 00730 /* Use the current one */ 00731 Prcb = CurrentPrcb; 00732 Cpu = Prcb->Number; 00733 } 00734 00735 /* ROS Sanity Check */ 00736 ASSERT(Prcb == CurrentPrcb); 00737 00738 /* Check if this is a threaded DPC and threaded DPCs are enabled */ 00739 if ((Dpc->Type == ThreadedDpcObject) && (Prcb->ThreadDpcEnable)) 00740 { 00741 /* Then use the threaded data */ 00742 DpcData = &Prcb->DpcData[DPC_THREADED]; 00743 } 00744 else 00745 { 00746 /* Otherwise, use the regular data */ 00747 DpcData = &Prcb->DpcData[DPC_NORMAL]; 00748 } 00749 00750 /* Acquire the DPC lock */ 00751 KiAcquireSpinLock(&DpcData->DpcLock); 00752 00753 /* Get the DPC Data */ 00754 if (!InterlockedCompareExchangePointer(&Dpc->DpcData, DpcData, NULL)) 00755 { 00756 /* Now we can play with the DPC safely */ 00757 Dpc->SystemArgument1 = SystemArgument1; 00758 Dpc->SystemArgument2 = SystemArgument2; 00759 DpcData->DpcQueueDepth++; 00760 DpcData->DpcCount++; 00761 DpcConfigured = TRUE; 00762 00763 /* Check if this is a high importance DPC */ 00764 if (Dpc->Importance == HighImportance) 00765 { 00766 /* Pre-empty other DPCs */ 00767 InsertHeadList(&DpcData->DpcListHead, &Dpc->DpcListEntry); 00768 } 00769 else 00770 { 00771 /* Add it at the end */ 00772 InsertTailList(&DpcData->DpcListHead, &Dpc->DpcListEntry); 00773 } 00774 00775 /* Check if this is the DPC on the threaded list */ 00776 if (&Prcb->DpcData[DPC_THREADED] == DpcData) 00777 { 00778 /* Make sure a threaded DPC isn't already active */ 00779 if (!(Prcb->DpcThreadActive) && !(Prcb->DpcThreadRequested)) 00780 { 00781 /* FIXME: Setup Threaded DPC */ 00782 DPRINT1("Threaded DPC not supported\n"); 00783 while (TRUE); 00784 } 00785 } 00786 else 00787 { 00788 /* Make sure a DPC isn't executing already */ 00789 if (!(Prcb->DpcRoutineActive) && !(Prcb->DpcInterruptRequested)) 00790 { 00791 /* Check if this is the same CPU */ 00792 if (Prcb != CurrentPrcb) 00793 { 00794 /* 00795 * Check if the DPC is of high importance or above the 00796 * maximum depth. If it is, then make sure that the CPU 00797 * isn't idle, or that it's sleeping. 00798 */ 00799 if (((Dpc->Importance == HighImportance) || 00800 (DpcData->DpcQueueDepth >= 00801 Prcb->MaximumDpcQueueDepth)) && 00802 (!(AFFINITY_MASK(Cpu) & KiIdleSummary) || 00803 (Prcb->Sleeping))) 00804 { 00805 /* Set interrupt requested */ 00806 Prcb->DpcInterruptRequested = TRUE; 00807 00808 /* Set DPC inserted */ 00809 DpcInserted = TRUE; 00810 } 00811 } 00812 else 00813 { 00814 /* Check if the DPC is of anything but low importance */ 00815 if ((Dpc->Importance != LowImportance) || 00816 (DpcData->DpcQueueDepth >= 00817 Prcb->MaximumDpcQueueDepth) || 00818 (Prcb->DpcRequestRate < Prcb->MinimumDpcRate)) 00819 { 00820 /* Set interrupt requested */ 00821 Prcb->DpcInterruptRequested = TRUE; 00822 00823 /* Set DPC inserted */ 00824 DpcInserted = TRUE; 00825 } 00826 } 00827 } 00828 } 00829 } 00830 00831 /* Release the lock */ 00832 KiReleaseSpinLock(&DpcData->DpcLock); 00833 00834 /* Check if the DPC was inserted */ 00835 if (DpcInserted) 00836 { 00837 /* Check if this was SMP */ 00838 if (Prcb != CurrentPrcb) 00839 { 00840 /* It was, request and IPI */ 00841 KiIpiSend(AFFINITY_MASK(Cpu), IPI_DPC); 00842 } 00843 else 00844 { 00845 /* It wasn't, request an interrupt from HAL */ 00846 HalRequestSoftwareInterrupt(DISPATCH_LEVEL); 00847 } 00848 } 00849 00850 /* Lower IRQL */ 00851 KeLowerIrql(OldIrql); 00852 return DpcConfigured; 00853 } 00854 00855 /* 00856 * @implemented 00857 */ 00858 BOOLEAN 00859 NTAPI 00860 KeRemoveQueueDpc(IN PKDPC Dpc) 00861 { 00862 PKDPC_DATA DpcData; 00863 BOOLEAN Enable; 00864 ASSERT_DPC(Dpc); 00865 00866 /* Disable interrupts */ 00867 Enable = KeDisableInterrupts(); 00868 00869 /* Get DPC data */ 00870 DpcData = Dpc->DpcData; 00871 if (DpcData) 00872 { 00873 /* Acquire the DPC lock */ 00874 KiAcquireSpinLock(&DpcData->DpcLock); 00875 00876 /* Make sure that the data didn't change */ 00877 if (DpcData == Dpc->DpcData) 00878 { 00879 /* Remove the DPC */ 00880 DpcData->DpcQueueDepth--; 00881 RemoveEntryList(&Dpc->DpcListEntry); 00882 Dpc->DpcData = NULL; 00883 } 00884 00885 /* Release the lock */ 00886 KiReleaseSpinLock(&DpcData->DpcLock); 00887 } 00888 00889 /* Re-enable interrupts */ 00890 if (Enable) _enable(); 00891 00892 /* Return if the DPC was in the queue or not */ 00893 return DpcData ? TRUE : FALSE; 00894 } 00895 00896 /* 00897 * @implemented 00898 */ 00899 VOID 00900 NTAPI 00901 KeFlushQueuedDpcs(VOID) 00902 { 00903 PKPRCB CurrentPrcb = KeGetCurrentPrcb(); 00904 PAGED_CODE(); 00905 00906 /* Check if this is an UP machine */ 00907 if (KeActiveProcessors == 1) 00908 { 00909 /* Check if there are DPCs on either queues */ 00910 if ((CurrentPrcb->DpcData[DPC_NORMAL].DpcQueueDepth > 0) || 00911 (CurrentPrcb->DpcData[DPC_THREADED].DpcQueueDepth > 0)) 00912 { 00913 /* Request an interrupt */ 00914 HalRequestSoftwareInterrupt(DISPATCH_LEVEL); 00915 } 00916 } 00917 else 00918 { 00919 /* FIXME: SMP support required */ 00920 ASSERT(FALSE); 00921 } 00922 } 00923 00924 /* 00925 * @implemented 00926 */ 00927 BOOLEAN 00928 NTAPI 00929 KeIsExecutingDpc(VOID) 00930 { 00931 /* Return if the Dpc Routine is active */ 00932 return KeGetCurrentPrcb()->DpcRoutineActive; 00933 } 00934 00935 /* 00936 * @implemented 00937 */ 00938 VOID 00939 NTAPI 00940 KeSetImportanceDpc (IN PKDPC Dpc, 00941 IN KDPC_IMPORTANCE Importance) 00942 { 00943 /* Set the DPC Importance */ 00944 ASSERT_DPC(Dpc); 00945 Dpc->Importance = Importance; 00946 } 00947 00948 /* 00949 * @implemented 00950 */ 00951 VOID 00952 NTAPI 00953 KeSetTargetProcessorDpc(IN PKDPC Dpc, 00954 IN CCHAR Number) 00955 { 00956 /* Set a target CPU */ 00957 ASSERT_DPC(Dpc); 00958 Dpc->Number = Number + MAXIMUM_PROCESSORS; 00959 } 00960 00961 /* 00962 * @implemented 00963 */ 00964 VOID 00965 NTAPI 00966 KeGenericCallDpc(IN PKDEFERRED_ROUTINE Routine, 00967 IN PVOID Context) 00968 { 00969 ULONG Barrier = KeNumberProcessors; 00970 KIRQL OldIrql; 00971 DEFERRED_REVERSE_BARRIER ReverseBarrier; 00972 ASSERT(KeGetCurrentIrql () < DISPATCH_LEVEL); 00973 00974 // 00975 // The barrier is the number of processors, each processor will decrement it 00976 // by one, so when all processors have run the DPC, the barrier reaches zero 00977 // 00978 ReverseBarrier.Barrier = Barrier; 00979 ReverseBarrier.TotalProcessors = Barrier; 00980 00981 // 00982 // But we don't need the barrier on UP, since we can simply call the routine 00983 // directly while at DISPATCH_LEVEL and not worry about anything else 00984 // 00985 KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); 00986 Routine(&KeGetCurrentPrcb()->CallDpc, Context, &Barrier, &ReverseBarrier); 00987 KeLowerIrql(OldIrql); 00988 } 00989 00990 /* 00991 * @implemented 00992 */ 00993 VOID 00994 NTAPI 00995 KeSignalCallDpcDone(IN PVOID SystemArgument1) 00996 { 00997 // 00998 // Decrement the barrier, which is actually the processor count 00999 // 01000 InterlockedDecrement((PLONG)SystemArgument1); 01001 } 01002 01003 /* 01004 * @implemented 01005 */ 01006 BOOLEAN 01007 NTAPI 01008 KeSignalCallDpcSynchronize(IN PVOID SystemArgument2) 01009 { 01010 // 01011 // There is nothing to do on UP systems -- the processor calling this wins 01012 // 01013 UNREFERENCED_PARAMETER(SystemArgument2); 01014 return TRUE; 01015 } 01016 01017 /* EOF */ Generated on Fri May 25 2012 04:35:55 for ReactOS by
1.7.6.1
|