Source code of Windows XP (NT5)
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.

499 lines
13 KiB

  1. /*++
  2. Module Name:
  3. clock.c
  4. Abstract:
  5. This module implements the platform specific clock interrupt processing
  6. routines in for the kernel.
  7. Author:
  8. Edward G. Chron (echron) 10-Apr-1996
  9. Environment:
  10. Kernel mode only.
  11. Revision History:
  12. --*/
  13. #include "ki.h"
  14. #include <ia64.h>
  15. #include <ntia64.h>
  16. #include <ntexapi.h>
  17. VOID
  18. KiProcessProfileList (
  19. IN PKTRAP_FRAME TrFrame,
  20. IN KPROFILE_SOURCE Source,
  21. IN PLIST_ENTRY ListHead
  22. );
  23. BOOLEAN
  24. KiChkTimerExpireSysDpc (
  25. IN ULONGLONG TickCount
  26. )
  27. /*++
  28. Routine Description:
  29. This routine determines if it should attempt to place a timer expiration DPC
  30. in the system DPC list and to drive the DPC by initiating a dispatch level interrupt
  31. on the current processor.
  32. N.B. If DPC is already inserted on the DPC list, we're done.
  33. Arguments:
  34. TickCount - The lower tick count, timer table hand value
  35. Return Value:
  36. BOOLEAN - Set to true if Queued DPC or if DPC already Queued.
  37. --*/
  38. {
  39. BOOLEAN ret = FALSE; // No DPC queued, default return value.
  40. PLIST_ENTRY ListHead = &KiTimerTableListHead[(ULONG)TickCount%TIMER_TABLE_SIZE];
  41. PLIST_ENTRY NextEntry = ListHead->Flink;
  42. //
  43. // Check to see if the list is empty.
  44. //
  45. if (NextEntry != ListHead) {
  46. PKTIMER Timer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry);
  47. ULONGLONG TimeValue = Timer->DueTime.QuadPart;
  48. ULARGE_INTEGER CurrentTime;
  49. //
  50. // See if timer expired.
  51. //
  52. CurrentTime.LowPart = SharedUserData->InterruptTime.LowPart;
  53. CurrentTime.HighPart = SharedUserData->InterruptTime.High1Time;
  54. if (TimeValue <= CurrentTime.QuadPart) {
  55. PKPRCB Prcb = KeGetCurrentPrcb();
  56. PKDPC Dpc = &KiTimerExpireDpc;
  57. _disable();
  58. #if !defined(NT_UP)
  59. KiAcquireSpinLock(&Prcb->DpcLock);
  60. #endif
  61. //
  62. // Insert DPC only if not already inserted.
  63. //
  64. if (Dpc->Lock == NULL) {
  65. //
  66. // Put timer expiration DPC in the system DPC list and initiate
  67. // a dispatch interrupt on the current processor.
  68. //
  69. Prcb->DpcCount += 1;
  70. Prcb->DpcQueueDepth += 1;
  71. Dpc->Lock = &Prcb->DpcLock;
  72. Dpc->SystemArgument1 = (PVOID)TickCount;
  73. Dpc->SystemArgument2 = 0;
  74. InsertTailList(&Prcb->DpcListHead, &Dpc->DpcListEntry);
  75. KiRequestSoftwareInterrupt(DISPATCH_LEVEL);
  76. }
  77. ret = TRUE;
  78. #if !defined(NT_UP)
  79. KiReleaseSpinLock(&Prcb->DpcLock);
  80. #endif
  81. _enable();
  82. }
  83. }
  84. return(ret);
  85. }
  86. VOID
  87. KeUpdateSystemTime (
  88. IN PKTRAP_FRAME TrFrame,
  89. IN ULONG Increment
  90. )
  91. /*++
  92. Routine Description:
  93. This routine is executed on a single processor in the processor complex.
  94. It's function is to update the system time and to check to determine if a
  95. timer has expired.
  96. N.B. This routine is executed on a single processor in a multiprocess system.
  97. The remainder of the processors in the complex execute the quantum end and
  98. runtime update code.
  99. Arguments:
  100. TrFrame - Supplies a pointer to a trap frame.
  101. Increment - The time increment to be used to adjust the time slice for the next
  102. tick. The value is supplied in 100ns units.
  103. Return Value:
  104. None.
  105. --*/
  106. {
  107. ULONG LowTime;
  108. LONG HighTime;
  109. LONG SaveTickOffset;
  110. //
  111. // Update the interrupt time in the shared region.
  112. //
  113. LowTime = SharedUserData->InterruptTime.LowPart + Increment;
  114. HighTime = SharedUserData->InterruptTime.High1Time + (LowTime < Increment);
  115. SharedUserData->InterruptTime.High2Time = HighTime;
  116. SharedUserData->InterruptTime.LowPart = LowTime;
  117. SharedUserData->InterruptTime.High1Time = HighTime;
  118. KiTickOffset -= Increment;
  119. SaveTickOffset = KiTickOffset;
  120. if ((LONG)KiTickOffset > 0)
  121. {
  122. //
  123. // Tick has not completed (100ns time units remain).
  124. //
  125. // Determine if a timer has expired at the current hand value.
  126. //
  127. KiChkTimerExpireSysDpc(KeTickCount.QuadPart);
  128. } else {
  129. //
  130. // Tick has completed, tick count set to maximum increase plus any
  131. // residue and system time is updated.
  132. //
  133. // Compute next tick offset.
  134. //
  135. KiTickOffset += KeMaximumIncrement;
  136. LowTime = SharedUserData->SystemTime.LowPart + KeTimeAdjustment;
  137. HighTime = SharedUserData->SystemTime.High1Time + (LowTime < KeTimeAdjustment);
  138. SharedUserData->SystemTime.High2Time = HighTime;
  139. SharedUserData->SystemTime.LowPart = LowTime;
  140. SharedUserData->SystemTime.High1Time = HighTime;
  141. ++KeTickCount.QuadPart;
  142. SharedUserData->TickCountLow = (ULONG)KeTickCount.QuadPart;
  143. //
  144. // Determine if a timer has expired at either the current hand value or
  145. // the next hand value.
  146. //
  147. if (!KiChkTimerExpireSysDpc(KeTickCount.QuadPart - 1))
  148. KiChkTimerExpireSysDpc(KeTickCount.QuadPart);
  149. }
  150. if (SaveTickOffset <= 0) {
  151. KeUpdateRunTime(TrFrame);
  152. }
  153. }
  154. VOID
  155. KeUpdateRunTime (
  156. IN PKTRAP_FRAME TrFrame
  157. )
  158. /*++
  159. Routine Description:
  160. This routine is executed on all processors in the processor complex.
  161. It's function is to update the run time of the current thread, udpate the run
  162. time for the thread's process, and decrement the current thread's quantum.
  163. Arguments:
  164. TrFrame - Supplies a pointer to a trap frame.
  165. Return Value:
  166. None.
  167. --*/
  168. {
  169. KSPIN_LOCK Lock;
  170. PKPRCB Prcb = KeGetCurrentPrcb();
  171. PKTHREAD Thread = KeGetCurrentThread();
  172. PKPROCESS Process = Thread->ApcState.Process;
  173. //
  174. // If thread was executing in user mode:
  175. // increment the thread user time.
  176. // atomically increment the process user time.
  177. // else If the old IRQL is greater than the DPC level:
  178. // increment the time executing interrupt service routines.
  179. // else If the old IRQL is less than the DPC level or If a DPC is not active:
  180. // increment the thread kernel time.
  181. // atomically increment the process kernel time.
  182. // else
  183. // increment time executing DPC routines.
  184. //
  185. if (TrFrame->PreviousMode != KernelMode) {
  186. ++Thread->UserTime;
  187. // Atomic Update of Process User Time required.
  188. ExInterlockedIncrementLong(&Process->UserTime, &Lock);
  189. // Update the time spent in user mode for the current processor.
  190. ++Prcb->UserTime;
  191. } else {
  192. if (TrFrame->OldIrql > DISPATCH_LEVEL) {
  193. ++Prcb->InterruptTime;
  194. } else if ((TrFrame->OldIrql < DISPATCH_LEVEL) ||
  195. (Prcb->DpcRoutineActive == 0)) {
  196. ++Thread->KernelTime;
  197. ExInterlockedIncrementLong(&Process->KernelTime, &Lock);
  198. } else {
  199. ++Prcb->DpcTime;
  200. }
  201. //
  202. // Update the time spent in kernel mode for the current processor.
  203. //
  204. ++Prcb->KernelTime;
  205. }
  206. //
  207. // Update the DPC request rate which is computed as the average between the
  208. // previous rate and the current rate.
  209. // Update the DPC last count with the current DPC count.
  210. //
  211. Prcb->DpcRequestRate = ((Prcb->DpcCount - Prcb->DpcLastCount) + Prcb->DpcRequestRate) >> 1;
  212. Prcb->DpcLastCount = Prcb->DpcCount;
  213. //
  214. // If the DPC queue depth is not zero and a DPC routine is not active.
  215. // Request a dispatch interrupt.
  216. // Decrement the maximum DPC queue depth.
  217. // Reset the threshold counter if appropriate.
  218. //
  219. if (Prcb->DpcQueueDepth != 0 && Prcb->DpcRoutineActive == 0) {
  220. Prcb->AdjustDpcThreshold = KiAdjustDpcThreshold;
  221. // Need to request a DPC interrupt.
  222. KiRequestSoftwareInterrupt(DISPATCH_LEVEL);
  223. if (Prcb->DpcRequestRate < KiIdealDpcRate && Prcb->MaximumDpcQueueDepth > 1)
  224. --Prcb->MaximumDpcQueueDepth;
  225. } else {
  226. //
  227. // The DPC queue is empty or a DPC routine is active or a DPC interrupt
  228. // has been requested. Count down the adjustment threshold and if the count
  229. // reaches zero, then increment the maximum DPC queue depth but not above
  230. // the initial value. Also, reset the adjustment threshold value.
  231. //
  232. --Prcb->AdjustDpcThreshold;
  233. if (Prcb->AdjustDpcThreshold == 0) {
  234. Prcb->AdjustDpcThreshold = KiAdjustDpcThreshold;
  235. if (KiMaximumDpcQueueDepth != Prcb->MaximumDpcQueueDepth)
  236. ++Prcb->MaximumDpcQueueDepth;
  237. }
  238. }
  239. //
  240. // Decrement current thread quantum and determine if quantum end has occurred.
  241. //
  242. Thread->Quantum -= CLOCK_QUANTUM_DECREMENT;
  243. // Set quantum end if time expired, for any thread except idle thread.
  244. if (Thread->Quantum <= 0 && Thread != Prcb->IdleThread) {
  245. Prcb->QuantumEnd = 1;
  246. // Need to request a DPC interrupt.
  247. KiRequestSoftwareInterrupt(DISPATCH_LEVEL);
  248. }
  249. #ifdef _MERCED_A0_
  250. //
  251. // if SignalDone of the processor prcb is set, an IPI is to be serviced
  252. // but the corresponding IPI may have been lost on pre-B3 processors;
  253. // therefore, send another IPI to workaround this problem
  254. //
  255. if (KeGetCurrentPrcb()->SignalDone != 0) {
  256. HalRequestIpi(PCR->SetMember);
  257. }
  258. #endif // _MERCED_A0_
  259. }
  260. VOID
  261. KeProfileInterrupt (
  262. IN PKTRAP_FRAME TrFrame
  263. )
  264. /*++
  265. Routine Description:
  266. This routine is executed on all processors in the processor complex.
  267. The routine is entered as the result of an interrupt generated by the profile
  268. timer. Its function is to update the profile information for the currently
  269. active profile objects.
  270. N.B. KeProfileInterrupt is an alternate entry for backwards compatability that
  271. sets the source to zero (ProfileTime).
  272. Arguments:
  273. TrFrame - Supplies a pointer to a trap frame.
  274. Return Value:
  275. None.
  276. --*/
  277. {
  278. KPROFILE_SOURCE Source = 0;
  279. KeProfileInterruptWithSource(TrFrame, Source);
  280. return;
  281. }
  282. VOID
  283. KeProfileInterruptWithSource (
  284. IN PKTRAP_FRAME TrFrame,
  285. IN KPROFILE_SOURCE Source
  286. )
  287. /*++
  288. Routine Description:
  289. This routine is executed on all processors in the processor complex.
  290. The routine is entered as the result of an interrupt generated by the profile
  291. timer. Its function is to update the profile information for the currently
  292. active profile objects.
  293. N.B. KeProfileInterruptWithSource is not currently fully implemented by any of
  294. the architectures.
  295. Arguments:
  296. TrFrame - Supplies a pointer to a trap frame.
  297. Source - Supplies the source of the profile interrupt.
  298. Return Value:
  299. None.
  300. --*/
  301. {
  302. PKTHREAD Thread = KeGetCurrentThread();
  303. PKPROCESS Process = Thread->ApcState.Process;
  304. PERFINFO_PROFILE(TrFrame, Source);
  305. #if !defined(NT_UP)
  306. KiAcquireSpinLock(&KiProfileLock);
  307. #endif
  308. KiProcessProfileList(TrFrame, Source, &Process->ProfileListHead);
  309. KiProcessProfileList(TrFrame, Source, &KiProfileListHead);
  310. #if !defined(NT_UP)
  311. KiReleaseSpinLock(&KiProfileLock);
  312. #endif
  313. return;
  314. }
  315. VOID
  316. KiProcessProfileList (
  317. IN PKTRAP_FRAME TrFrame,
  318. IN KPROFILE_SOURCE Source,
  319. IN PLIST_ENTRY ListHead
  320. )
  321. /*++
  322. Routine Description:
  323. This routine is executed on all processors in the processor complex.
  324. The routine is entered as the result of an interrupt generated by the profile
  325. timer. Its function is to update the profile information for the currently
  326. active profile objects.
  327. N.B. KeProfileInterruptWithSource is not currently fully implemented by any of
  328. the architectures.
  329. Arguments:
  330. TrFrame - Supplies a pointer to a trap frame.
  331. Source - Supplies the source of the profile interrupt.
  332. ListHead - Supplies a pointer to a profile list.
  333. Return Value:
  334. None.
  335. --*/
  336. {
  337. PLIST_ENTRY NextEntry = ListHead->Flink;
  338. PKPRCB Prcb = KeGetCurrentPrcb();
  339. //
  340. // Scan profile list and increment profile buckets as appropriate.
  341. //
  342. for (; NextEntry != ListHead; NextEntry = NextEntry->Flink) {
  343. PCHAR BucketPter;
  344. PULONG BucketValue;
  345. PKPROFILE Profile = CONTAINING_RECORD(NextEntry, KPROFILE, ProfileListEntry);
  346. if ( (Profile->Source != Source) || ((Profile->Affinity & Prcb->SetMember) == 0) ) {
  347. continue;
  348. }
  349. if ( ((PVOID)TrFrame->StIIP < Profile->RangeBase) || ((PVOID)TrFrame->StIIP > Profile->RangeLimit) ) {
  350. continue;
  351. }
  352. BucketPter = (PCHAR)Profile->Buffer +
  353. ((((PCHAR)TrFrame->StIIP - (PCHAR)Profile->RangeBase)
  354. >> Profile->BucketShift) & 0xFFFFFFFC);
  355. BucketValue = (PULONG) BucketPter;
  356. (*BucketValue)++;
  357. }
  358. return;
  359. }