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 }

Generated on Sat May 15 19:39:46 2004 for test by doxygen 1.3.7