Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1128 lines
33 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. dpcsup.c
  5. Abstract:
  6. This module contains the support routines for the system DPC objects.
  7. Functions are provided to process quantum end, the power notification
  8. queue, and timer expiration.
  9. Author:
  10. David N. Cutler (davec) 22-Apr-1989
  11. Environment:
  12. Kernel mode only, IRQL DISPATCH_LEVEL.
  13. Revision History:
  14. --*/
  15. #include "ki.h"
  16. //
  17. // Define DPC entry structure and maximum DPC List size.
  18. //
  19. #define MAXIMUM_DPC_TABLE_SIZE 16
  20. typedef struct _DPC_ENTRY {
  21. PRKDPC Dpc;
  22. PKDEFERRED_ROUTINE Routine;
  23. PVOID Context;
  24. } DPC_ENTRY, *PDPC_ENTRY;
  25. //
  26. // Define maximum number of timers that can be examined or processed before
  27. // dropping the dispatcher database lock.
  28. //
  29. #define MAXIMUM_TIMERS_EXAMINED 24
  30. #define MAXIMUM_TIMERS_PROCESSED 4
  31. VOID
  32. KiExecuteDpc (
  33. IN PVOID Context
  34. )
  35. /*++
  36. Routine Description:
  37. This function is executed by the DPC thread for each processor. DPC
  38. threads are started during kernel initialization after having started
  39. all processors and it is determined that the host configuation should
  40. execute threaded DPCs in a DPC thread.
  41. Arguments:
  42. Context - Supplies a pointer to the processor control block for the
  43. processor on which the DPC thread is to run.
  44. Return Value:
  45. None.
  46. --*/
  47. {
  48. PKDPC Dpc;
  49. PVOID DeferredContext;
  50. PKDEFERRED_ROUTINE DeferredRoutine;
  51. PERFINFO_DPC_INFORMATION DpcInformation;
  52. PLIST_ENTRY Entry;
  53. PLIST_ENTRY ListHead;
  54. LOGICAL Logging;
  55. KIRQL OldIrql;
  56. PKPRCB Prcb;
  57. PVOID SystemArgument1;
  58. PVOID SystemArgument2;
  59. PKTHREAD Thread;
  60. LARGE_INTEGER TimeStamp = {0};
  61. //
  62. // Get PRCB and set the DPC thread address.
  63. //
  64. Prcb = Context;
  65. Thread = KeGetCurrentThread();
  66. Prcb->DpcThread = Thread;
  67. //
  68. // Set the DPC thread priority to the highest level, set the thread
  69. // affinity, and enable threaded DPCs on this processor.
  70. //
  71. KeSetPriorityThread(Thread, HIGH_PRIORITY);
  72. KeSetSystemAffinityThread(Prcb->SetMember);
  73. Prcb->ThreadDpcEnable = TRUE;
  74. //
  75. // Loop processing DPC list entries until the specified DPC list is empty.
  76. //
  77. // N.B. This following code appears to have a redundant loop, but it does
  78. // not. The point of this code is to avoid as many dispatch interrupts
  79. // as possible.
  80. //
  81. ListHead = &Prcb->DpcData[DPC_THREADED].DpcListHead;
  82. do {
  83. Prcb->DpcThreadActive = TRUE;
  84. //
  85. // If the DPC list is not empty, then process the DPC list.
  86. //
  87. if (Prcb->DpcData[DPC_THREADED].DpcQueueDepth != 0) {
  88. Logging = PERFINFO_IS_GROUP_ON(PERF_DPC);
  89. //
  90. // Acquire the DPC lock for the current processor and check if
  91. // the DPC list is empty. If the DPC list is not empty, then
  92. // remove the first entry from the DPC list, capture the DPC
  93. // parameters, set the DPC inserted state false, decrement the
  94. // DPC queue depth, release the DPC lock, enable interrupts, and
  95. // call the specified DPC routine. Otherwise, release the DPC
  96. // lock and enable interrupts.
  97. //
  98. do {
  99. KeRaiseIrql(HIGH_LEVEL, &OldIrql);
  100. KeAcquireSpinLockAtDpcLevel(&Prcb->DpcData[DPC_THREADED].DpcLock);
  101. Entry = ListHead->Flink;
  102. if (Entry != ListHead) {
  103. RemoveEntryList(Entry);
  104. Dpc = CONTAINING_RECORD(Entry, KDPC, DpcListEntry);
  105. DeferredRoutine = Dpc->DeferredRoutine;
  106. DeferredContext = Dpc->DeferredContext;
  107. SystemArgument1 = Dpc->SystemArgument1;
  108. SystemArgument2 = Dpc->SystemArgument2;
  109. Dpc->DpcData = NULL;
  110. Prcb->DpcData[DPC_THREADED].DpcQueueDepth -= 1;
  111. KeReleaseSpinLockFromDpcLevel(&Prcb->DpcData[DPC_THREADED].DpcLock);
  112. KeLowerIrql(OldIrql);
  113. //
  114. // If event tracing is enabled, capture the start time.
  115. //
  116. if (Logging != FALSE) {
  117. PerfTimeStamp(TimeStamp);
  118. }
  119. //
  120. // Call the DPC routine.
  121. //
  122. (DeferredRoutine)(Dpc,
  123. DeferredContext,
  124. SystemArgument1,
  125. SystemArgument2);
  126. ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
  127. ASSERT(Thread->Affinity == Prcb->SetMember);
  128. ASSERT(Thread->Priority == HIGH_PRIORITY);
  129. //
  130. // If event tracing is enabled, then log the start time
  131. // and routine address.
  132. //
  133. if (Logging != FALSE) {
  134. DpcInformation.InitialTime = TimeStamp.QuadPart;
  135. DpcInformation.DpcRoutine = (PVOID)(ULONG_PTR)DeferredRoutine;
  136. PerfInfoLogBytes(PERFINFO_LOG_TYPE_DPC,
  137. &DpcInformation,
  138. sizeof(DpcInformation));
  139. }
  140. } else {
  141. ASSERT(Prcb->DpcData[DPC_THREADED].DpcQueueDepth == 0);
  142. KeReleaseSpinLockFromDpcLevel(&Prcb->DpcData[DPC_THREADED].DpcLock);
  143. KeLowerIrql(OldIrql);
  144. }
  145. } while (Prcb->DpcData[DPC_THREADED].DpcQueueDepth != 0);
  146. }
  147. Prcb->DpcThreadActive = FALSE;
  148. Prcb->DpcThreadRequested = FALSE;
  149. KeMemoryBarrier();
  150. //
  151. // If the thread DPC list is empty, then wait until the DPC event
  152. // for the current processor is set.
  153. //
  154. if (Prcb->DpcData[DPC_THREADED].DpcQueueDepth == 0) {
  155. KeWaitForSingleObject(&Prcb->DpcEvent,
  156. Suspended,
  157. KernelMode,
  158. FALSE,
  159. NULL);
  160. }
  161. } while (TRUE);
  162. return;
  163. }
  164. VOID
  165. KiQuantumEnd (
  166. VOID
  167. )
  168. /*++
  169. Routine Description:
  170. This function is called when a quantum end event occurs on the current
  171. processor. Its function is to determine whether the thread priority should
  172. be decremented and whether a redispatch of the processor should occur.
  173. N.B. This function is called at DISPATCH level and returns at DISPATCH
  174. level.
  175. Arguments:
  176. None.
  177. Return Value:
  178. None.
  179. --*/
  180. {
  181. PKPRCB Prcb;
  182. PKPROCESS Process;
  183. PRKTHREAD Thread;
  184. PRKTHREAD NewThread;
  185. //
  186. // If DPC thread activation is requested, then set the DPC event.
  187. //
  188. Prcb = KeGetCurrentPrcb();
  189. Thread = KeGetCurrentThread();
  190. if (InterlockedExchange(&Prcb->DpcSetEventRequest, FALSE) == TRUE) {
  191. KeSetEvent(&Prcb->DpcEvent, 0, FALSE);
  192. }
  193. //
  194. // Raise IRQL to SYNCH level, acquire the thread lock, and acquire the
  195. // PRCB lock.
  196. //
  197. // If the quantum has expired for the current thread, then update its
  198. // quantum and priority.
  199. //
  200. KeRaiseIrqlToSynchLevel();
  201. KiAcquireThreadLock(Thread);
  202. KiAcquirePrcbLock(Prcb);
  203. if (Thread->Quantum <= 0) {
  204. //
  205. // If quantum runout is disabled for the thread's process and
  206. // the thread is running at a realtime priority, then set the
  207. // thread quantum to the highest value and do not round robin
  208. // at the thread's priority level. Otherwise, reset the thread
  209. // quantum and decay the thread's priority as appropriate.
  210. //
  211. Process = Thread->ApcState.Process;
  212. if ((Process->DisableQuantum != FALSE) &&
  213. (Thread->Priority >= LOW_REALTIME_PRIORITY)) {
  214. Thread->Quantum = MAXCHAR;
  215. } else {
  216. Thread->Quantum = Process->ThreadQuantum;
  217. //
  218. // Compute the new thread priority and attempt to reschedule the
  219. // current processor.
  220. //
  221. // N.B. The new priority will never be greater than the previous
  222. // priority.
  223. //
  224. Thread->Priority = KiComputeNewPriority(Thread, 1);
  225. if (Prcb->NextThread == NULL) {
  226. if ((NewThread = KiSelectReadyThread(Thread->Priority, Prcb)) != NULL) {
  227. NewThread->State = Standby;
  228. Prcb->NextThread = NewThread;
  229. }
  230. } else {
  231. Thread->Preempted = FALSE;
  232. }
  233. }
  234. }
  235. //
  236. // Release the thread lock.
  237. //
  238. // If a thread was scheduled for execution on the current processor, then
  239. // acquire the PRCB lock, set the current thread to the new thread, set
  240. // next thread to NULL, set the thread state to running, release the PRCB
  241. // lock, set the wait reason, ready the old thread, and swap context to
  242. // the new thread.
  243. //
  244. KiReleaseThreadLock(Thread);
  245. if (Prcb->NextThread != NULL) {
  246. KiSetContextSwapBusy(Thread);
  247. NewThread = Prcb->NextThread;
  248. Prcb->NextThread = NULL;
  249. Prcb->CurrentThread = NewThread;
  250. NewThread->State = Running;
  251. Thread->WaitReason = WrQuantumEnd;
  252. KxQueueReadyThread(Thread, Prcb);
  253. Thread->WaitIrql = APC_LEVEL;
  254. KiSwapContext(Thread, NewThread);
  255. } else {
  256. KiReleasePrcbLock(Prcb);
  257. }
  258. //
  259. // Lower IRQL to DISPATCH level and return.
  260. //
  261. KeLowerIrql(DISPATCH_LEVEL);
  262. return;
  263. }
  264. #if DBG
  265. VOID
  266. KiCheckTimerTable (
  267. IN ULARGE_INTEGER CurrentTime
  268. )
  269. {
  270. ULONG Index;
  271. PLIST_ENTRY ListHead;
  272. PLIST_ENTRY NextEntry;
  273. KIRQL OldIrql;
  274. PKTIMER Timer;
  275. //
  276. // Raise IRQL to highest level and scan timer table for timers that
  277. // have expired.
  278. //
  279. KeRaiseIrql(HIGH_LEVEL, &OldIrql);
  280. Index = 0;
  281. do {
  282. ListHead = &KiTimerTableListHead[Index];
  283. NextEntry = ListHead->Flink;
  284. while (NextEntry != ListHead) {
  285. Timer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry);
  286. NextEntry = NextEntry->Flink;
  287. if (Timer->DueTime.QuadPart <= CurrentTime.QuadPart) {
  288. //
  289. // If the timer expiration DPC is queued, then the time has
  290. // been change and the DPC has not yet had the chance to run
  291. // and clear out the expired timers.
  292. //
  293. if (*((volatile PKSPIN_LOCK *)(&KiTimerExpireDpc.DpcData)) == NULL) {
  294. DbgBreakPoint();
  295. }
  296. }
  297. }
  298. Index += 1;
  299. } while(Index < TIMER_TABLE_SIZE);
  300. //
  301. // Lower IRQL to the previous level.
  302. //
  303. KeLowerIrql(OldIrql);
  304. return;
  305. }
  306. #endif
  307. FORCEINLINE
  308. VOID
  309. KiProcessTimerDpcTable (
  310. IN PULARGE_INTEGER SystemTime,
  311. IN PDPC_ENTRY DpcTable,
  312. IN ULONG Count
  313. )
  314. /**++
  315. Routine Description:
  316. This function processes the time DPC table which is a array of DPCs that
  317. are to be called on the current processor.
  318. N.B. This routine is entered with the dispatcher database locked.
  319. N.B. This routine returns with the dispatcher database unlocked.
  320. Arguments:
  321. SystemTime - Supplies a pointer to the timer expiration time.
  322. DpcTable - Supplies a pointer to an array of DPC entries.
  323. Count - Supplies a count of the number of entries in the DPC table.
  324. Return Value:
  325. None.
  326. --*/
  327. {
  328. PERFINFO_DPC_INFORMATION DpcInformation;
  329. LOGICAL Logging;
  330. LARGE_INTEGER TimeStamp = {0};
  331. //
  332. // Unlock the dispacher database and lower IRQL to dispatch level.
  333. //
  334. KiUnlockDispatcherDatabase(DISPATCH_LEVEL);
  335. //
  336. // Process DPC table entries.
  337. //
  338. Logging = PERFINFO_IS_GROUP_ON(PERF_DPC);
  339. while (Count != 0) {
  340. //
  341. // Reset the debug DPC count to avoid a timeout and breakpoint.
  342. //
  343. #if DBG
  344. KeGetCurrentPrcb()->DebugDpcTime = 0;
  345. #endif
  346. //
  347. // If event tracing is enabled, capture the start time.
  348. //
  349. if (Logging != FALSE) {
  350. PerfTimeStamp(TimeStamp);
  351. }
  352. //
  353. // Call the DPC routine.
  354. //
  355. (DpcTable->Routine)(DpcTable->Dpc,
  356. DpcTable->Context,
  357. ULongToPtr(SystemTime->LowPart),
  358. ULongToPtr(SystemTime->HighPart));
  359. //
  360. // If event tracing is enabled, then log the start time and
  361. // routine address.
  362. //
  363. if (Logging != FALSE) {
  364. DpcInformation.InitialTime = TimeStamp.QuadPart;
  365. DpcInformation.DpcRoutine = (PVOID)(ULONG_PTR)DpcTable->Routine;
  366. PerfInfoLogBytes(PERFINFO_LOG_TYPE_TIMERDPC,
  367. &DpcInformation,
  368. sizeof(DpcInformation));
  369. }
  370. DpcTable += 1;
  371. Count -= 1;
  372. }
  373. return;
  374. }
  375. VOID
  376. KiTimerExpiration (
  377. IN PKDPC TimerDpc,
  378. IN PVOID DeferredContext,
  379. IN PVOID SystemArgument1,
  380. IN PVOID SystemArgument2
  381. )
  382. /*++
  383. Routine Description:
  384. This function is called when the clock interupt routine discovers that
  385. a timer has expired.
  386. Arguments:
  387. TimerDpc - Not used.
  388. DeferredContext - Not used.
  389. SystemArgument1 - Supplies the starting timer table index value to
  390. use for the timer table scan.
  391. SystemArgument2 - Not used.
  392. Return Value:
  393. None.
  394. --*/
  395. {
  396. ULARGE_INTEGER CurrentTime;
  397. ULONG DpcCount;
  398. PKDPC Dpc;
  399. DPC_ENTRY DpcTable[MAXIMUM_TIMERS_PROCESSED];
  400. KIRQL DummyIrql;
  401. LONG HandLimit;
  402. LONG Index;
  403. LARGE_INTEGER Interval;
  404. PLIST_ENTRY ListHead;
  405. PLIST_ENTRY NextEntry;
  406. KIRQL OldIrql;
  407. LONG Period;
  408. ULARGE_INTEGER SystemTime;
  409. PKTIMER Timer;
  410. ULONG TimersExamined;
  411. ULONG TimersProcessed;
  412. UNREFERENCED_PARAMETER(TimerDpc);
  413. UNREFERENCED_PARAMETER(DeferredContext);
  414. UNREFERENCED_PARAMETER(SystemArgument2);
  415. //
  416. // Capture the timer expiration time, the current interrupt time, and
  417. // the low tick count.
  418. //
  419. // N.B. Interrupts are disabled to ensure that interrupt activity on the
  420. // current processor does not cause the values read to be skewed.
  421. //
  422. _disable();
  423. KiQuerySystemTime((PLARGE_INTEGER)&SystemTime);
  424. KiQueryInterruptTime((PLARGE_INTEGER)&CurrentTime);
  425. HandLimit = (LONG)KiQueryLowTickCount();
  426. _enable();
  427. //
  428. // If the timer table has not wrapped, then start with the specified
  429. // timer table index value, and scan for timer entries that have expired.
  430. // Otherwise, start with the specified timer table index value and scan
  431. // the entire table for timer entries that have expired.
  432. //
  433. // N.B. This later condition exists when DPC processing is blocked for a
  434. // period longer than one round trip throught the timer table.
  435. //
  436. // N.B. The current instance of the timer expiration execution will only
  437. // process the timer queue entries specified by the computed index
  438. // and hand limit. If another timer expires while the current scan
  439. // is in progress, then another scan will occur when the current one
  440. // is finished.
  441. //
  442. Index = PtrToLong(SystemArgument1);
  443. if ((ULONG)(HandLimit - Index) >= TIMER_TABLE_SIZE) {
  444. HandLimit = Index + TIMER_TABLE_SIZE - 1;
  445. }
  446. Index -= 1;
  447. HandLimit &= (TIMER_TABLE_SIZE - 1);
  448. //
  449. // Acquire the dispatcher database lock and read the current interrupt
  450. // time to determine which timers have expired.
  451. //
  452. DpcCount = 0;
  453. TimersExamined = MAXIMUM_TIMERS_EXAMINED;
  454. TimersProcessed = MAXIMUM_TIMERS_PROCESSED;
  455. KiLockDispatcherDatabase(&OldIrql);
  456. do {
  457. Index = (Index + 1) & (TIMER_TABLE_SIZE - 1);
  458. ListHead = &KiTimerTableListHead[Index];
  459. NextEntry = ListHead->Flink;
  460. while (NextEntry != ListHead) {
  461. Timer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry);
  462. TimersExamined -= 1;
  463. if (Timer->DueTime.QuadPart <= CurrentTime.QuadPart) {
  464. //
  465. // The next timer in the current timer list has expired.
  466. // Remove the entry from the timer tree and set the signal
  467. // state of the timer.
  468. //
  469. TimersProcessed -= 1;
  470. KiRemoveTreeTimer(Timer);
  471. Timer->Header.SignalState = 1;
  472. //
  473. // Capture the DPC and period fields from the timer object.
  474. // Once wait test is called, the timer must not be touched
  475. // again unless it is periodic. The reason for this is that
  476. // a thread may allocate a timer on its local stack and wait
  477. // on it. Wait test can cause that thread to immediately
  478. // start running on another processor on an MP system. If
  479. // the thread returns, then the timer will be corrupted.
  480. //
  481. Dpc = Timer->Dpc;
  482. Period = Timer->Period;
  483. if (IsListEmpty(&Timer->Header.WaitListHead) == FALSE) {
  484. if (Timer->Header.Type == TimerNotificationObject) {
  485. KiWaitTestWithoutSideEffects(Timer, TIMER_EXPIRE_INCREMENT);
  486. } else {
  487. KiWaitTestSynchronizationObject(Timer, TIMER_EXPIRE_INCREMENT);
  488. }
  489. }
  490. //
  491. // If the timer is periodic, then compute the next interval
  492. // time and reinsert the timer in the timer tree.
  493. //
  494. // N.B. Even though the timer insertion is relative, it can
  495. // still fail if the period of the timer elapses in
  496. // between computing the time and inserting the timer.
  497. // If this happens, then the insertion is retried.
  498. //
  499. if (Period != 0) {
  500. Interval.QuadPart = Int32x32To64(Period, - 10 * 1000);
  501. do {
  502. } while (KiInsertTreeTimer(Timer, Interval) == FALSE);
  503. }
  504. //
  505. // If a DPC is specified, then insert it in the target
  506. // processor's DPC queue or capture the parameters in
  507. // the DPC table for subsequent execution on the current
  508. // processor.
  509. //
  510. if (Dpc != NULL) {
  511. #if defined(NT_UP)
  512. DpcTable[DpcCount].Dpc = Dpc;
  513. DpcTable[DpcCount].Routine = Dpc->DeferredRoutine;
  514. DpcTable[DpcCount].Context = Dpc->DeferredContext;
  515. DpcCount += 1;
  516. #else
  517. if (((Dpc->Number >= MAXIMUM_PROCESSORS) &&
  518. (((ULONG)Dpc->Number - MAXIMUM_PROCESSORS) != KeGetCurrentProcessorNumber())) ||
  519. ((Dpc->Type == (UCHAR)ThreadedDpcObject) &&
  520. (KeGetCurrentPrcb()->ThreadDpcEnable != FALSE))) {
  521. KeInsertQueueDpc(Dpc,
  522. ULongToPtr(SystemTime.LowPart),
  523. ULongToPtr(SystemTime.HighPart));
  524. } else {
  525. DpcTable[DpcCount].Dpc = Dpc;
  526. DpcTable[DpcCount].Routine = Dpc->DeferredRoutine;
  527. DpcTable[DpcCount].Context = Dpc->DeferredContext;
  528. DpcCount += 1;
  529. }
  530. #endif
  531. }
  532. //
  533. // If the maximum number of timers have been processed or
  534. // the maximum number of timers have been examined, then
  535. // drop the dispatcher lock and process the DPC table.
  536. //
  537. if ((TimersProcessed == 0) || (TimersExamined == 0)) {
  538. KiProcessTimerDpcTable(&SystemTime, &DpcTable[0], DpcCount);
  539. //
  540. // Initialize the DPC count, the scan counters, and
  541. // acquire the dispatcher database lock.
  542. //
  543. // N.B. Control is returned with the dispatcher database
  544. // unlocked.
  545. //
  546. DpcCount = 0;
  547. TimersExamined = MAXIMUM_TIMERS_EXAMINED;
  548. TimersProcessed = MAXIMUM_TIMERS_PROCESSED;
  549. KiLockDispatcherDatabase(&DummyIrql);
  550. }
  551. NextEntry = ListHead->Flink;
  552. } else {
  553. //
  554. // If the maximum number of timers have been scanned, then
  555. // drop the dispatcher lock and process the DPC table.
  556. //
  557. if (TimersExamined == 0) {
  558. KiProcessTimerDpcTable(&SystemTime, &DpcTable[0], DpcCount);
  559. //
  560. // Initialize the DPC count, the scan counters, and
  561. // acquire the dispatcher database lock.
  562. //
  563. // N.B. Control is returned with the dispatcher database
  564. // unlocked.
  565. //
  566. DpcCount = 0;
  567. TimersExamined = MAXIMUM_TIMERS_EXAMINED;
  568. TimersProcessed = MAXIMUM_TIMERS_PROCESSED;
  569. KiLockDispatcherDatabase(&DummyIrql);
  570. }
  571. break;
  572. }
  573. }
  574. } while(Index != HandLimit);
  575. #if DBG
  576. if (KeNumberProcessors == 1) {
  577. KiCheckTimerTable(CurrentTime);
  578. }
  579. #endif
  580. //
  581. // If the DPC table is not empty, then process the remaining DPC table
  582. // entries and lower IRQL. Otherwise, unlock the dispatcher database.
  583. //
  584. // N.B. Control is returned from the DPC processing routine with the
  585. // dispatcher database unlocked.
  586. //
  587. if (DpcCount != 0) {
  588. KiProcessTimerDpcTable(&SystemTime, &DpcTable[0], DpcCount);
  589. if (OldIrql != DISPATCH_LEVEL) {
  590. KeLowerIrql(OldIrql);
  591. }
  592. } else {
  593. KiUnlockDispatcherDatabase(OldIrql);
  594. }
  595. return;
  596. }
  597. VOID
  598. FASTCALL
  599. KiTimerListExpire (
  600. IN PLIST_ENTRY ExpiredListHead,
  601. IN KIRQL OldIrql
  602. )
  603. /*++
  604. Routine Description:
  605. This function is called to process a list of timers that have expired.
  606. N.B. This function is called with the dispatcher database locked and
  607. returns with the dispatcher database unlocked.
  608. Arguments:
  609. ExpiredListHead - Supplies a pointer to a list of timers that have
  610. expired.
  611. OldIrql - Supplies the previous IRQL.
  612. Return Value:
  613. None.
  614. --*/
  615. {
  616. LONG Count;
  617. PKDPC Dpc;
  618. DPC_ENTRY DpcTable[MAXIMUM_DPC_TABLE_SIZE];
  619. LARGE_INTEGER Interval;
  620. KIRQL OldIrql1;
  621. ULARGE_INTEGER SystemTime;
  622. PKTIMER Timer;
  623. LONG Period;
  624. //
  625. // Capture the timer expiration time.
  626. //
  627. KiQuerySystemTime((PLARGE_INTEGER)&SystemTime);
  628. //
  629. // Remove the next timer from the expired timer list, set the state of
  630. // the timer to signaled, reinsert the timer in the timer tree if it is
  631. // periodic, and optionally call the DPC routine if one is specified.
  632. //
  633. RestartScan:
  634. Count = 0;
  635. while (ExpiredListHead->Flink != ExpiredListHead) {
  636. Timer = CONTAINING_RECORD(ExpiredListHead->Flink, KTIMER, TimerListEntry);
  637. KiRemoveTreeTimer(Timer);
  638. Timer->Header.SignalState = 1;
  639. //
  640. // Capture the DPC and period fields from the timer object. Once wait
  641. // test is called, the timer must not be touched again unless it is
  642. // periodic. The reason for this is that a thread may allocate a timer
  643. // on its local stack and wait on it. Wait test can cause that thread
  644. // to immediately start running on another processor on an MP system.
  645. // If the thread returns, then the timer will be corrupted.
  646. //
  647. Dpc = Timer->Dpc;
  648. Period = Timer->Period;
  649. if (IsListEmpty(&Timer->Header.WaitListHead) == FALSE) {
  650. if (Timer->Header.Type == TimerNotificationObject) {
  651. KiWaitTestWithoutSideEffects(Timer, TIMER_EXPIRE_INCREMENT);
  652. } else {
  653. KiWaitTestSynchronizationObject(Timer, TIMER_EXPIRE_INCREMENT);
  654. }
  655. }
  656. //
  657. // If the timer is periodic, then compute the next interval time
  658. // and reinsert the timer in the timer tree.
  659. //
  660. // N.B. Even though the timer insertion is relative, it can still
  661. // fail if the period of the timer elapses in between computing
  662. // the time and inserting the timer. If this happens, then the
  663. // insertion is retried.
  664. //
  665. if (Period != 0) {
  666. Interval.QuadPart = Int32x32To64(Period, - 10 * 1000);
  667. do {
  668. } while (KiInsertTreeTimer(Timer, Interval) == FALSE);
  669. }
  670. //
  671. // If a DPC is specified, then insert it in the target processor's
  672. // DPC queue or capture the parameters in the DPC table for subsequent
  673. // execution on the current processor.
  674. //
  675. if (Dpc != NULL) {
  676. //
  677. // If the DPC is explicitly targeted to another processor, then
  678. // queue the DPC to the target processor. Otherwise, capture the
  679. // DPC parameters for execution on the current processor.
  680. //
  681. #if defined(NT_UP)
  682. DpcTable[Count].Dpc = Dpc;
  683. DpcTable[Count].Routine = Dpc->DeferredRoutine;
  684. DpcTable[Count].Context = Dpc->DeferredContext;
  685. Count += 1;
  686. if (Count == MAXIMUM_DPC_TABLE_SIZE) {
  687. break;
  688. }
  689. #else
  690. if (((Dpc->Number >= MAXIMUM_PROCESSORS) &&
  691. (((ULONG)Dpc->Number - MAXIMUM_PROCESSORS) != KeGetCurrentProcessorNumber())) ||
  692. ((Dpc->Type == (UCHAR)ThreadedDpcObject) &&
  693. (KeGetCurrentPrcb()->ThreadDpcEnable != FALSE))) {
  694. KeInsertQueueDpc(Dpc,
  695. ULongToPtr(SystemTime.LowPart),
  696. ULongToPtr(SystemTime.HighPart));
  697. } else {
  698. DpcTable[Count].Dpc = Dpc;
  699. DpcTable[Count].Routine = Dpc->DeferredRoutine;
  700. DpcTable[Count].Context = Dpc->DeferredContext;
  701. Count += 1;
  702. if (Count == MAXIMUM_DPC_TABLE_SIZE) {
  703. break;
  704. }
  705. }
  706. #endif
  707. }
  708. }
  709. //
  710. // Unlock the dispatcher database and process DPC list entries.
  711. //
  712. if (Count != 0) {
  713. KiProcessTimerDpcTable(&SystemTime, &DpcTable[0], Count);
  714. //
  715. // If processing of the expired timer list was terminated because
  716. // the DPC List was full, then process any remaining entries.
  717. //
  718. if (Count == MAXIMUM_DPC_TABLE_SIZE) {
  719. KiLockDispatcherDatabase(&OldIrql1);
  720. goto RestartScan;
  721. }
  722. KeLowerIrql(OldIrql);
  723. } else {
  724. KiUnlockDispatcherDatabase(OldIrql);
  725. }
  726. return;
  727. }
  728. VOID
  729. FASTCALL
  730. KiRetireDpcList (
  731. PKPRCB Prcb
  732. )
  733. /*++
  734. Routine Description:
  735. This function processes the DPC list for the specified processor,
  736. processes timer expiration, and processes the deferred ready list.
  737. N.B. This function is entered with interrupts disabled and exits with
  738. interrupts disabled.
  739. Arguments:
  740. Prcb - Supplies the address of the processor block.
  741. Return Value:
  742. None.
  743. --*/
  744. {
  745. PKDPC Dpc;
  746. PKDPC_DATA DpcData;
  747. PVOID DeferredContext;
  748. PKDEFERRED_ROUTINE DeferredRoutine;
  749. PERFINFO_DPC_INFORMATION DpcInformation;
  750. PLIST_ENTRY Entry;
  751. PLIST_ENTRY ListHead;
  752. LOGICAL Logging;
  753. PVOID SystemArgument1;
  754. PVOID SystemArgument2;
  755. ULONG_PTR TimerHand;
  756. LARGE_INTEGER TimeStamp = {0};
  757. //
  758. // Loop processing DPC list entries until the specified DPC list is empty.
  759. //
  760. // N.B. This following code appears to have a redundant loop, but it does
  761. // not. The point of this code is to avoid as many dispatch interrupts
  762. // as possible.
  763. //
  764. DpcData = &Prcb->DpcData[DPC_NORMAL];
  765. ListHead = &DpcData->DpcListHead;
  766. Logging = PERFINFO_IS_GROUP_ON(PERF_DPC);
  767. do {
  768. Prcb->DpcRoutineActive = TRUE;
  769. //
  770. // If the timer hand value is nonzero, then process expired timers.
  771. //
  772. if (Prcb->TimerRequest != 0) {
  773. TimerHand = Prcb->TimerHand;
  774. Prcb->TimerRequest = 0;
  775. _enable();
  776. KiTimerExpiration(NULL, NULL, (PVOID) TimerHand, NULL);
  777. _disable();
  778. }
  779. //
  780. // If the DPC list is not empty, then process the DPC list.
  781. //
  782. if (DpcData->DpcQueueDepth != 0) {
  783. //
  784. // Acquire the DPC lock for the current processor and check if
  785. // the DPC list is empty. If the DPC list is not empty, then
  786. // remove the first entry from the DPC list, capture the DPC
  787. // parameters, set the DPC inserted state false, decrement the
  788. // DPC queue depth, release the DPC lock, enable interrupts, and
  789. // call the specified DPC routine. Otherwise, release the DPC
  790. // lock and enable interrupts.
  791. //
  792. do {
  793. KeAcquireSpinLockAtDpcLevel(&DpcData->DpcLock);
  794. Entry = ListHead->Flink;
  795. if (Entry != ListHead) {
  796. RemoveEntryList(Entry);
  797. Dpc = CONTAINING_RECORD(Entry, KDPC, DpcListEntry);
  798. DeferredRoutine = Dpc->DeferredRoutine;
  799. DeferredContext = Dpc->DeferredContext;
  800. SystemArgument1 = Dpc->SystemArgument1;
  801. SystemArgument2 = Dpc->SystemArgument2;
  802. Dpc->DpcData = NULL;
  803. DpcData->DpcQueueDepth -= 1;
  804. #if DBG
  805. Prcb->DebugDpcTime = 0;
  806. #endif
  807. KeReleaseSpinLockFromDpcLevel(&DpcData->DpcLock);
  808. _enable();
  809. //
  810. // If event tracing is enabled, capture the start time.
  811. //
  812. if (Logging != FALSE) {
  813. PerfTimeStamp(TimeStamp);
  814. }
  815. //
  816. // Call the DPC routine.
  817. //
  818. (DeferredRoutine)(Dpc,
  819. DeferredContext,
  820. SystemArgument1,
  821. SystemArgument2);
  822. ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
  823. //
  824. // If event tracing is enabled, then log the start time
  825. // and routine address.
  826. //
  827. if (Logging != FALSE) {
  828. DpcInformation.InitialTime = TimeStamp.QuadPart;
  829. DpcInformation.DpcRoutine = (PVOID) (ULONG_PTR) DeferredRoutine;
  830. PerfInfoLogBytes(PERFINFO_LOG_TYPE_DPC,
  831. &DpcInformation,
  832. sizeof(DpcInformation));
  833. }
  834. _disable();
  835. } else {
  836. ASSERT(DpcData->DpcQueueDepth == 0);
  837. KeReleaseSpinLockFromDpcLevel(&DpcData->DpcLock);
  838. }
  839. } while (DpcData->DpcQueueDepth != 0);
  840. }
  841. Prcb->DpcRoutineActive = FALSE;
  842. Prcb->DpcInterruptRequested = FALSE;
  843. KeMemoryBarrier();
  844. //
  845. // Process the deferred ready list if the list is not empty.
  846. //
  847. #if !defined(NT_UP)
  848. if (Prcb->DeferredReadyListHead.Next != NULL) {
  849. KIRQL OldIrql;
  850. _enable();
  851. OldIrql = KeRaiseIrqlToSynchLevel();
  852. KiProcessDeferredReadyList(Prcb);
  853. KeLowerIrql(OldIrql);
  854. _disable();
  855. }
  856. #endif
  857. } while (DpcData->DpcQueueDepth != 0);
  858. return;
  859. }