ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

dpc.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 doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.