test: dpcsup.c Source File

Main Page | Class Hierarchy | Class List | File List | Class Members | File Members

# dpcsup.c

Go to the documentation of this file.

    00001 /*++
    00002
    00003 Copyright (c) 1989  Microsoft Corporation
    00004
    00005 Module Name:
    00006
    00007     dpcsup.c
    00008
    00009 Abstract:
    00010
    00011     This module contains the support routines for the system DPC objects.
    00012     Functions are provided to process quantum end, the power notification
    00013     queue, and timer expiration.
    00014
    00015 Author:
    00016
    00017     David N. Cutler (davec) 22-Apr-1989
    00018
    00019 Environment:
    00020
    00021     Kernel mode only, IRQL DISPATCH_LEVEL.
    00022
    00023 Revision History:
    00024
    00025 --*/
    00026
    00027 #include "ki.h"
    00028
    00029 //
    00030 // Define DPC entry structure and maximum DPC List size.
    00031 //
    00032
    00033 #define MAXIMUM_DPC_LIST_SIZE 16
    00034
    00035 typedef struct _DPC_ENTRY {
    00036     PRKDPC Dpc;
    00037     PKDEFERRED_ROUTINE Routine;
    00038     PVOID Context;
    00039 } DPC_ENTRY, *PDPC_ENTRY;
    00040
    00041
    00042 PRKTHREAD
    00043 KiQuantumEnd (
    00044     VOID
    00045     )
    00046
    00047 /*++
    00048
    00049 Routine Description:
    00050
    00051     This function is called when a quantum end event occurs on the current
    00052     processor. Its function is to determine whether the thread priority should
    00053     be decremented and whether a redispatch of the processor should occur.
    00054
    00055 Arguments:
    00056
    00057     None.
    00058
    00059 Return Value:
    00060
    00061     The next thread to be schedule on the current processor is returned as
    00062     the function value. If this value is not NULL, then the return is with
    00063     the dispatcher database locked. Otherwise, the dispatcher database is
    00064     unlocked.
    00065
    00066 --*/
    00067
    00068 {
    00069
    00070     KPRIORITY NewPriority;
    00071     KIRQL OldIrql;
    00072     PKPRCB Prcb;
    00073     KPRIORITY Priority;
    00074     PKPROCESS Process;
    00075     PRKTHREAD Thread;
    00076     PRKTHREAD NextThread;
    00077
    00078     //
    00079     // Acquire the dispatcher database lock.
    00080     //
    00081
    00082     Prcb = KeGetCurrentPrcb();
    00083     Thread = KeGetCurrentThread();
    00084     KiLockDispatcherDatabase(&OldIrql);
    00085
    00086     //
    00087     // If the quantum has expired for the current thread, then update its
    00088     // quantum and priority.
    00089     //
    00090
    00091     if (Thread->Quantum <= 0) {
    00092
    00093         //
    00094         // If quantum runout is disabled for the thread's process and
    00095         // the thread is running at a realtime priority, then set the
    00096         // thread quantum to the highest value and do not round robin
    00097         // at the thread's priority level. Otherwise, reset the thread
    00098         // quantum and decay the thread's priority as appropriate.
    00099         //
    00100
    00101         Process = Thread->ApcState.Process;
    00102         if ((Process->DisableQuantum != FALSE) &&
    00103             (Thread->Priority >= LOW_REALTIME_PRIORITY)) {
    00104             Thread->Quantum = MAXCHAR;
    00105
    00106         } else {
    00107             Thread->Quantum = Process->ThreadQuantum;
    00108
    00109             //
    00110             // Decrement the thread's current priority if the thread is not
    00111             // running in a realtime priority class and check to determine
    00112             // if the processor should be redispatched.
    00113             //
    00114
    00115             Priority = Thread->Priority;
    00116             if (Priority < LOW_REALTIME_PRIORITY) {
    00117                 NewPriority = Priority - Thread->PriorityDecrement - 1;
    00118                 if (NewPriority < Thread->BasePriority) {
    00119                     NewPriority = Thread->BasePriority;
    00120                 }
    00121
    00122                 Thread->PriorityDecrement = 0;
    00123
    00124             } else {
    00125                 NewPriority = Priority;
    00126             }
    00127
    00128             //
    00129             // If the new thread priority is different that the current thread
    00130             // priority, then the thread does not run at a realtime level and
    00131             // its priority should be set. Otherwise, attempt to round robin
    00132             // at the current level.
    00133             //
    00134
    00135             if (Priority != NewPriority) {
    00136                 KiSetPriorityThread(Thread, NewPriority);
    00137
    00138             } else {
    00139                 if (Prcb->NextThread == NULL) {
    00140                     NextThread = KiFindReadyThread(Thread->NextProcessor, Priority);
    00141
    00142                     if (NextThread != NULL) {
    00143                         NextThread->State = Standby;
    00144                         Prcb->NextThread = NextThread;
    00145                     }
    00146
    00147                 } else {
    00148                     Thread->Preempted = FALSE;
    00149                 }
    00150             }
    00151         }
    00152     }
    00153
    00154     //
    00155     // If a thread was scheduled for execution on the current processor,
    00156     // then return the address of the thread with the dispatcher database
    00157     // locked. Otherwise, return NULL with the dispatcher data unlocked.
    00158     //
    00159
    00160     NextThread = Prcb->NextThread;
    00161     if (NextThread == NULL) {
    00162         KiUnlockDispatcherDatabase(OldIrql);
    00163     }
    00164
    00165     return NextThread;
    00166 }
    00167
    00168 #if DBG
    00169
    00170
    00171 VOID
    00172 KiCheckTimerTable (
    00173     IN ULARGE_INTEGER CurrentTime
    00174     )
    00175
    00176 {
    00177
    00178     ULONG Index;
    00179     PLIST_ENTRY ListHead;
    00180     PLIST_ENTRY NextEntry;
    00181     KIRQL OldIrql;
    00182     PKTIMER Timer;
    00183
    00184     //
    00185     // Raise IRQL to highest level and scan timer table for timers that
    00186     // have expired.
    00187     //
    00188
    00189     KeRaiseIrql(HIGH_LEVEL, &OldIrql);
    00190     Index = 0;
    00191     do {
    00192         ListHead = &KiTimerTableListHead[Index];
    00193         NextEntry = ListHead->Flink;
    00194         while (NextEntry != ListHead) {
    00195             Timer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry);
    00196             NextEntry = NextEntry->Flink;
    00197             if (Timer->DueTime.QuadPart <= CurrentTime.QuadPart) {
    00198                 DbgBreakPoint();
    00199             }
    00200         }
    00201
    00202         Index += 1;
    00203     } while(Index < TIMER_TABLE_SIZE);
    00204
    00205     //
    00206     // Lower IRQL to the previous level.
    00207     //
    00208
    00209     KeLowerIrql(OldIrql);
    00210     return;
    00211 }
    00212
    00213 #endif
    00214
    00215
    00216 VOID
    00217 KiTimerExpiration (
    00218     IN PKDPC TimerDpc,
    00219     IN PVOID DeferredContext,
    00220     IN PVOID SystemArgument1,
    00221     IN PVOID SystemArgument2
    00222     )
    00223
    00224 /*++
    00225
    00226 Routine Description:
    00227
    00228     This function is called when the clock interupt routine discovers that
    00229     a timer has expired.
    00230
    00231 Arguments:
    00232
    00233     TimerDpc - Supplies a pointer to a control object of type DPC.
    00234
    00235     DeferredContext - Not used.
    00236
    00237     SystemArgument1 - Supplies the starting timer table index value to
    00238         use for the timer table scan.
    00239
    00240     SystemArgument2 - Not used.
    00241
    00242 Return Value:
    00243
    00244     None.
    00245
    00246 --*/
    00247
    00248 {
    00249     ULARGE_INTEGER CurrentTime;
    00250     LIST_ENTRY ExpiredListHead;
    00251     LONG HandLimit;
    00252     LONG Index;
    00253     PLIST_ENTRY ListHead;
    00254     PLIST_ENTRY NextEntry;
    00255     KIRQL OldIrql;
    00256     PKTIMER Timer;
    00257
    00258     //
    00259     // Acquire the dispatcher database lock and read the current interrupt
    00260     // time to determine which timers have expired.
    00261     //
    00262
    00263     KiLockDispatcherDatabase(&OldIrql);
    00264     KiQueryInterruptTime((PLARGE_INTEGER)&CurrentTime);
    00265
    00266     //
    00267     // If the timer table has not wrapped, then start with the specified
    00268     // timer table index value, and scan for timer entries that have expired.
    00269     // Otherwise, start with the first entry in the timer table and scan the
    00270     // entire table for timer entries that have expired.
    00271     //
    00272     // N.B. This later condition exists when DPC processing is blocked for a
    00273     //      period longer than one round trip throught the timer table.
    00274     //
    00275
    00276     HandLimit = (LONG)KiQueryLowTickCount();
    00277     if (((ULONG)(HandLimit - PtrToLong(SystemArgument1))) >= TIMER_TABLE_SIZE) {
    00278         Index = - 1;
    00279         HandLimit = TIMER_TABLE_SIZE - 1;
    00280
    00281     } else {
    00282         Index = (PtrToLong(SystemArgument1) - 1) & (TIMER_TABLE_SIZE - 1);
    00283         HandLimit &= (TIMER_TABLE_SIZE - 1);
    00284     }
    00285
    00286     InitializeListHead(&ExpiredListHead);
    00287     do {
    00288         Index = (Index + 1) & (TIMER_TABLE_SIZE - 1);
    00289         ListHead = &KiTimerTableListHead[Index];
    00290         NextEntry = ListHead->Flink;
    00291         while (NextEntry != ListHead) {
    00292             Timer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry);
    00293             if (Timer->DueTime.QuadPart <= CurrentTime.QuadPart) {
    00294
    00295                 //
    00296                 // The next timer in the current timer list has expired.
    00297                 // Remove the entry from the timer list and insert the
    00298                 // timer in the expired list.
    00299                 //
    00300
    00301                 RemoveEntryList(&Timer->TimerListEntry);
    00302                 InsertTailList(&ExpiredListHead, &Timer->TimerListEntry);
    00303                 NextEntry = ListHead->Flink;
    00304
    00305             } else {
    00306                 break;
    00307             }
    00308         }
    00309
    00310     } while(Index != HandLimit);
    00311
    00312 #if DBG
    00313
    00314     if ((PtrToUlong(SystemArgument2) == 0) && (KeNumberProcessors == 1)) {
    00315         KiCheckTimerTable(CurrentTime);
    00316     }
    00317
    00318 #endif
    00319
    00320     //
    00321     // Process the expired timer list.
    00322     //
    00323     // N.B. The following function returns with the dispatcher database
    00324     //      unlocked.
    00325     //
    00326
    00327     KiTimerListExpire(&ExpiredListHead, OldIrql);
    00328     return;
    00329 }
    00330
    00331 VOID
    00332 FASTCALL
    00333 KiTimerListExpire (
    00334     IN PLIST_ENTRY ExpiredListHead,
    00335     IN KIRQL OldIrql
    00336     )
    00337
    00338 /*++
    00339
    00340 Routine Description:
    00341
    00342     This function is called to process a list of timers that have expired.
    00343
    00344     N.B. This function is called with the dispatcher database locked and
    00345         returns with the dispatcher database unlocked.
    00346
    00347 Arguments:
    00348
    00349     ExpiredListHead - Supplies a pointer to a list of timers that have
    00350         expired.
    00351
    00352     OldIrql - Supplies the previous IRQL.
    00353
    00354 Return Value:
    00355
    00356     None.
    00357
    00358 --*/
    00359
    00360 {
    00361
    00362     LONG Count;
    00363     PKDPC Dpc;
    00364     DPC_ENTRY DpcList[MAXIMUM_DPC_LIST_SIZE];
    00365     LONG Index;
    00366     LARGE_INTEGER Interval;
    00367     KIRQL OldIrql1;
    00368     LARGE_INTEGER SystemTime;
    00369     PKTIMER Timer;
    00370
    00371     //
    00372     // Capture the timer expiration time.
    00373     //
    00374
    00375     KiQuerySystemTime(&SystemTime);
    00376
    00377     //
    00378     // Remove the next timer from the expired timer list, set the state of
    00379     // the timer to signaled, reinsert the timer in the timer tree if it is
    00380     // periodic, and optionally call the DPC routine if one is specified.
    00381     //
    00382
    00383 RestartScan:
    00384     Count = 0;
    00385     while (ExpiredListHead->Flink != ExpiredListHead) {
    00386         Timer = CONTAINING_RECORD(ExpiredListHead->Flink, KTIMER, TimerListEntry);
    00387         KiRemoveTreeTimer(Timer);
    00388         Timer->Header.SignalState = 1;
    00389         if (IsListEmpty(&Timer->Header.WaitListHead) == FALSE) {
    00390             KiWaitTest(Timer, TIMER_EXPIRE_INCREMENT);
    00391         }
    00392
    00393         //
    00394         // If the timer is periodic, then compute the next interval time
    00395         // and reinsert the timer in the timer tree.
    00396         //
    00397         // N.B. Even though the timer insertion is relative, it can still
    00398         //      fail if the period of the timer elapses in between computing
    00399         //      the time and inserting the timer in the table. If this happens,
    00400         //      try again.
    00401         //
    00402         if (Timer->Period != 0) {
    00403             Interval.QuadPart = Int32x32To64(Timer->Period, - 10 * 1000);
    00404             while (!KiInsertTreeTimer(Timer, Interval)) {
    00405                 ;
    00406             }
    00407         }
    00408
    00409         if (Timer->Dpc != NULL) {
    00410             Dpc = Timer->Dpc;
    00411
    00412             //
    00413             // If the DPC is explicitly targeted to another processor, then
    00414             // queue the DPC to the target processor. Otherwise, capture the
    00415             // DPC parameters for execution on the current processor.
    00416             //
    00417
    00418 #if defined(NT_UP)
    00419
    00420             DpcList[Count].Dpc = Dpc;
    00421             DpcList[Count].Routine = Dpc->DeferredRoutine;
    00422             DpcList[Count].Context = Dpc->DeferredContext;
    00423             Count += 1;
    00424             if (Count == MAXIMUM_DPC_LIST_SIZE) {
    00425                 break;
    00426             }
    00427
    00428 #else
    00429
    00430             if ((Dpc->Number >= MAXIMUM_PROCESSORS) &&
    00431                 (((ULONG)Dpc->Number - MAXIMUM_PROCESSORS) != (ULONG)KeGetCurrentProcessorNumber())) {
    00432                 KeInsertQueueDpc(Dpc,
    00433                                  ULongToPtr(SystemTime.LowPart),
    00434                                  ULongToPtr(SystemTime.HighPart));
    00435
    00436             } else {
    00437                 DpcList[Count].Dpc = Dpc;
    00438                 DpcList[Count].Routine = Dpc->DeferredRoutine;
    00439                 DpcList[Count].Context = Dpc->DeferredContext;
    00440                 Count += 1;
    00441                 if (Count == MAXIMUM_DPC_LIST_SIZE) {
    00442                     break;
    00443                 }
    00444             }
    00445
    00446 #endif
    00447
    00448         }
    00449     }
    00450
    00451     //
    00452     // Unlock the dispacher database and process DPC list entries.
    00453     //
    00454
    00455     if (Count != 0) {
    00456         KiUnlockDispatcherDatabase(DISPATCH_LEVEL);
    00457         Index = 0;
    00458         do {
    00459
    00460 #if DBG && (defined(i386) || defined(ALPHA))
    00461
    00462             //
    00463             // Reset the dpc tick count. If the tick count handler,
    00464             // which increments this value, detects that it has crossed
    00465             // a certain threshold, a breakpoint will be generated.
    00466             //
    00467
    00468             KeGetCurrentPrcb()->DebugDpcTime = 0;
    00469 #endif
    00470
    00471             (DpcList[Index].Routine)(DpcList[Index].Dpc,
    00472                                      DpcList[Index].Context,
    00473                                      ULongToPtr(SystemTime.LowPart),
    00474                                      ULongToPtr(SystemTime.HighPart));
    00475
    00476
    00477             Index += 1;
    00478         } while (Index < Count);
    00479
    00480         //
    00481         // If processing of the expired timer list was terminated because
    00482         // the DPC List was full, then process any remaining entries.
    00483         //
    00484
    00485         if (Count == MAXIMUM_DPC_LIST_SIZE) {
    00486             KiLockDispatcherDatabase(&OldIrql1);
    00487             goto RestartScan;
    00488         }
    00489
    00490         KeLowerIrql(OldIrql);
    00491
    00492     } else {
    00493         KiUnlockDispatcherDatabase(OldIrql);
    00494     }
    00495
    00496     return;
    00497 }