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.

419 lines
14 KiB

  1. title "Interval Clock Interrupt"
  2. ;++
  3. ;
  4. ; Copyright (c) 2000 Microsoft Corporation
  5. ;
  6. ; Module Name:
  7. ;
  8. ; clockint.asm
  9. ;
  10. ; Abstract:
  11. ;
  12. ; This module implements the architecture dependent code necessary to
  13. ; process the interval clock interrupt.
  14. ;
  15. ; Author:
  16. ;
  17. ; David N. Cutler (davec) 12-Sep-2000
  18. ;
  19. ; Environment:
  20. ;
  21. ; Kernel mode only.
  22. ;
  23. ;--
  24. include ksamd64.inc
  25. extern KdDebuggerEnabled:byte
  26. extern KeMaximumIncrement:dword
  27. extern KeTimeAdjustment:dword
  28. extern KeTickCount:qword
  29. extern KiAdjustDpcThreshold:dword
  30. extern KiCheckBreakInRequest:proc
  31. extern KiIdealDpcRate:dword
  32. extern KiMaximumDpcQueueDepth:dword
  33. extern KiTickOffset:dword
  34. extern KiTimerTableListHead:qword
  35. extern __imp_HalRequestSoftwareInterrupt:qword
  36. subttl "Update System Time"
  37. ;++
  38. ;
  39. ; VOID
  40. ; KeUpdateSystemTime (
  41. ; IN ULONG64 Increment
  42. ; )
  43. ;
  44. ; Routine Description:
  45. ;
  46. ; This routine is called as the result of an interrupt generated by the
  47. ; interval timer. Its function is to update the interrupt time, update the
  48. ; system time, and check to determine if a timer has expired.
  49. ;
  50. ; N.B. This routine is executed on a single processor in a multiprocess
  51. ; system. The remainder of the processors only execute the quantum end
  52. ; and runtime update code.
  53. ;
  54. ; Arguments:
  55. ;
  56. ; TrapFrame (rcx) - Supplies the address of a trap frame.
  57. ;
  58. ; Increment (rcx) - Supplies the time increment value in 100 nanosecond
  59. ; units.
  60. ;
  61. ; Return Value:
  62. ;
  63. ; None.
  64. ;
  65. ;--
  66. UsFrame struct
  67. P1Home dq ? ; request IRQL parameter
  68. Fill dq ? ; fill to 8 mod 16
  69. SavedRbp dq ? ; saved register RBP
  70. UsFrame ends
  71. NESTED_ENTRY KeUpdateSystemTime, _TEXT$00
  72. push_reg rbp ; save nonvolatile register
  73. alloc_stack (sizeof UsFrame- (1 * 8)); allocate stack frame
  74. END_PROLOGUE
  75. lea rbp, 128[rcx] ; set frame pointer address
  76. ;
  77. ; Check if the current clock tick should be skipped.
  78. ;
  79. ; Skip tick is set when the kernel debugger is entered.
  80. ;
  81. if DBG
  82. cmp byte ptr gs:[PcSkipTick], 0 ; check if tick should be skipped
  83. jnz KiUS60 ; if nz, skip clock tick
  84. endif
  85. ;
  86. ; Update interrupt time.
  87. ;
  88. mov rcx, USER_SHARED_DATA ; get user shared data address
  89. lea r11, KiTimerTableListHead ; get timer table address
  90. mov r8, UsInterruptTime[rcx] ; get interrupt time
  91. add r8, rdx ; add time increment
  92. mov rax, r8 ; isolate high part of interrupt time
  93. shr rax, 32 ;
  94. mov UsInterruptTime + 8[rcx], eax ; store high 2 interrupt time
  95. mov UsInterruptTime[rcx], r8 ; store interrupt time
  96. mov r10, KeTickCount ; get tick count value
  97. sub KiTickOffset, edx ; subtract time increment
  98. jg short KiUS20 ; if greater, not complete tick
  99. ;
  100. ; Update system time.
  101. ;
  102. mov r9d, UsSystemTime + 0[rcx] ; get low interrupt time
  103. mov eax, UsSystemTime + 4[rcx] ; get high interrupt time
  104. add r9d, KeTimeAdjustment ; add time increment
  105. adc eax, 0 ; propagate carry
  106. mov UsSystemTime + 8[rcx], eax ; store high 2 interrupt time
  107. mov UsSystemTime + 0[rcx], r9d ; store low interrupt time
  108. mov UsSystemTime + 4[rcx], eax ; store high 1 interrupt time
  109. ;
  110. ; Update tick count.
  111. ;
  112. inc KeTickCount ; update tick count value
  113. inc dword ptr UsTickCountLow[rcx] ; update low tick count value
  114. ;
  115. ; Check to determine if a timer has expired.
  116. ;
  117. mov rcx, r10 ; copy tick count value
  118. and ecx, TIMER_TABLE_SIZE - 1 ; isolate current hand value
  119. shl ecx, 4 ; compute listhead offset
  120. lea rcx, [r11][rcx] ; get listhead address
  121. mov r9, LsFlink[rcx] ; get first entry address
  122. cmp r9, rcx ; check if list is empty
  123. je short KiUS10 ; if e, list is empty
  124. cmp r8, (TiDueTime - TiTimerListEntry)[r9] ; compare due time
  125. jae short KiUS30 ; if ae, timer has expired
  126. KiUS10: inc r10 ; advance tick count value
  127. ;
  128. ; Check to determine if a timer has expired.
  129. ;
  130. KiUS20: mov rcx, r10 ; copy tick count value
  131. and ecx, TIMER_TABLE_SIZE - 1 ; isolate current hand value
  132. shl ecx, 4 ; compute listhead offset
  133. lea rcx, [r11][rcx] ; get listhead addrees
  134. mov r9, LsFlink[rcx] ; get first entry address
  135. cmp r9, rcx ; check if list is empty
  136. je short KiUS40 ; if equal, list is empty
  137. cmp r8, (TiDueTime - TiTimerListEntry)[r9] ; compare due time
  138. jb short KiUS40 ; if b, timer has not expired
  139. ;
  140. ; A timer has expired.
  141. ;
  142. ; Set the timer hand value in the current processor block if it is not already
  143. ; set.
  144. ;
  145. KiUS30: mov rdx, gs:[PcCurrentPrcb] ; get current processor block address
  146. cmp dword ptr PbTimerHand[rdx], 0 ; check if expiration active
  147. jne short KiUS40 ; if ne, expiration already active
  148. and r10d, TIMER_TABLE_SIZE - 1 ; isolate current hand value
  149. inc r10d ; increment timer hand value
  150. mov PbTimerHand[rdx], r10d ; set timer hand value
  151. mov cl, DISPATCH_LEVEL ; request dispatch interrupt
  152. call __imp_HalRequestSoftwareInterrupt ;
  153. ;
  154. ; If the debugger is enabled, check if a break is requested.
  155. ;
  156. KiUS40: cmp KdDebuggerEnabled, 0 ; check if a debugger is enabled
  157. je short KiUS50 ; if e, debugger is not enabled
  158. call KiCheckBreakInRequest ; check for break in request
  159. ;
  160. ; Check to determine if a full tick has expired.
  161. ;
  162. KiUS50: cmp KiTickOffset, 0 ; check if full tick has expired
  163. jg short KiUS70 ; if g, not a full tick
  164. mov eax, KeMaximumIncrement ; get maximum time incrmeent
  165. add KiTickOffset, eax ; add maximum time to residue
  166. mov rcx, rbp ; set trap frame address
  167. call KeUpdateRunTime ; update runtime
  168. if DBG
  169. KiUS60: mov byte ptr gs:[PcSkipTick], 0 ; clear skip tick indicator
  170. endif
  171. KiUS70: add rsp, sizeof UsFrame- (1 * 8) ; deallocate stack frame
  172. pop rbp ; restore nonvolatile register
  173. ret ; return
  174. NESTED_END KeUpdateSystemTime, _TEXT$00
  175. subttl "Update Thread and Process Runtime"
  176. ;++
  177. ;
  178. ; Routine Description:
  179. ;
  180. ; This routine is called as the result of the interval timer interrupt on
  181. ; all processors in the system. Its function is update the runtime of the
  182. ; current thread, update the runtime of the current thread's process, and
  183. ; decrement the current thread's quantum. This routine also implements DPC
  184. ; interrupt moderation.
  185. ;
  186. ; N.B. This routine is executed on all processors in a multiprocessor
  187. ; system.
  188. ;
  189. ; Arguments:
  190. ;
  191. ; rcx - Supplies the address of a trap frame.
  192. ;
  193. ; Return Value:
  194. ;
  195. ; None.
  196. ;
  197. ;--
  198. UrFrame struct
  199. P1Home dq ? ; request IRQL parameter
  200. Fill dq ? ; fill to 8 mod 16
  201. SavedRdi dq ? ; saved register RDI
  202. SavedRsi dq ? ; saved register RSI
  203. savedRbp dq ? ; saved register RBP
  204. UrFrame ends
  205. NESTED_ENTRY KeUpdateRunTime, _TEXT$00
  206. push_reg rbp ; save nonvolatile registers
  207. push_reg rsi ;
  208. push_reg rdi ;
  209. alloc_stack (sizeof UrFrame - (3 * 8)) ; allocate stack frame
  210. END_PROLOGUE
  211. lea rbp, 128[rcx] ; set frame pointer address
  212. ;
  213. ; Check if the current clock tick should be skipped.
  214. ;
  215. ; Skip tick is set when the kernel debugger is entered.
  216. ;
  217. if DBG
  218. cmp byte ptr gs:[PcSkipTick], 0 ; check if tick should be skipped
  219. jnz KiUR70 ; if nz, skip clock tick
  220. endif
  221. ;
  222. ; Update time counter based on previous mode, IRQL level, and whether there
  223. ; is currently a DPC active.
  224. ;
  225. mov rsi, gs:[PcCurrentPrcb] ; get current processor block address
  226. mov rdi, PbCurrentThread[rsi] ; get current thread address
  227. mov rdx, ThApcState + AsProcess[rdi] ; get current process address
  228. test byte ptr TrSegCs[rbp], MODE_MASK ; check if previous mode user
  229. jnz short KiUR30 ; if nz, previous mode user
  230. ;
  231. ; Update the total time spent in kernel mode.
  232. ;
  233. inc dword ptr PbKernelTime[rsi] ; increment kernel time
  234. cmp byte ptr TrPreviousIrql[rbp], DISPATCH_LEVEL ; check IRQL level
  235. jb short KiUR20 ; if b, previous IRQL below DPC level
  236. ja short KiUR10 ; if a, previous IRQL above DPC level
  237. cmp dword ptr PbDpcRoutineActive[rsi], 0 ; check if DPC routine active
  238. je short KiUR20 ; if e, no DPC routine active
  239. inc dword ptr PbDpcTime[rsi] ; increment time at DPC level
  240. jmp short KiUR40 ; finish in common code
  241. ;
  242. ; Update the time spent at interrupt time for this processor
  243. ;
  244. KiUR10: inc dword ptr PbInterruptTime[rsi] ; increment interrupt time
  245. jmp short KiUR40 ; finish in common code
  246. ;
  247. ; Update the time spent in kernel mode for the current thread and the current
  248. ; process.
  249. ;
  250. KiUR20: inc dword ptr ThKernelTime[rdi] ; increment time in kernel mode
  251. ifndef NT_UP
  252. lock inc dword ptr PrKernelTime[rdx] ; increment time in kernel mode
  253. else
  254. inc dword ptr PrKernelTime[rdx] ; increment time in kernel mode
  255. endif
  256. jmp short KiUR40 ; finish in common code
  257. ;
  258. ; Update total time spent in user mode and update the time spent inuser mode
  259. ; for the current thread and the current process.
  260. ;
  261. KiUR30: inc dword ptr PbUserTime[rsi] ; increment time in user mode
  262. inc dword ptr ThUserTime[rdi] ; increment time is user mode
  263. ifndef NT_UP
  264. lock inc dword ptr PrUserTime[rdx] ; increment time in user mode
  265. else
  266. inc dword ptr PrUserTime[rdx] ; increment time in user mode
  267. endif
  268. ;
  269. ; Update the DPC request rate which is computed as the average between the
  270. ; previous rate and the current rate.
  271. ;
  272. KiUR40: mov ecx, PbDpcCount[rsi] ; get current DPC count
  273. mov edx, PbDpcLastCount[rsi] ; get last DPC count
  274. mov PbDpcLastCount[rsi], ecx ; set last DPC count
  275. sub ecx, edx ; compute count during interval
  276. add ecx, PbDpcRequestRate[rsi] ; compute sum
  277. shr ecx, 1 ; average current and last
  278. mov PbDpcRequestRate[rsi], ecx ; set new DPC request rate
  279. ;
  280. ; If the current DPC queue depth is not zero, a DPC routine is not active,
  281. ; and a DPC interrupt has not been requested, then request a dispatch
  282. ; interrupt, decrement the maximum DPC queue depth, and reset the threshold
  283. ; counter if appropriate.
  284. ;
  285. cmp dword ptr PbDpcQueueDepth[rsi], 0 ; check if queue depth zero
  286. je short KiUR50 ; if e, DPC queue depth is zero
  287. cmp dword ptr PbDpcRoutineActive[rsi], 0 ; check if DPC routine active
  288. jne short KiUR50 ; if ne, DPC routine active
  289. cmp dword ptr PbDpcInterruptRequested[rsi], 0 ; check if interrupt
  290. jne short KiUR50 ; if ne, interrupt requested
  291. mov cl, DISPATCH_LEVEL ; request a dispatch interrupt
  292. call __imp_HalRequestSoftwareInterrupt ;
  293. mov ecx, PbDpcRequestRate[rsi] ; get DPC request rate
  294. mov edx, KiAdjustDpcThreshold ; reset initial threshold counter
  295. mov PbAdjustDpcThreshold[rsi], edx ;
  296. cmp ecx, KiIdealDpcRate ; check if current rate less than ideal
  297. jge short KiUR60 ; if ge, rate greater or equal ideal
  298. cmp dword ptr PbMaximumDpcQueueDepth[rsi], 1 ; check if maximum depth one
  299. je short KiUR60 ; if e, maximum depth is one
  300. dec dword ptr PbMaximumDpcQueueDepth[rsi] ; decrement depth
  301. jmp short KiUR60 ;
  302. ;
  303. ; The DPC queue is empty or a DPC routine is active or a DPC interrupt has
  304. ; been requested. Count down the adjustment threshold and if the count reaches
  305. ; zero, then increment the maximum DPC queue depth, but not above the initial
  306. ; value and reset the adjustment threshold value.
  307. ;
  308. KiUR50: dec dword ptr PbAdjustDpcThreshold[rsi] ; decrement threshold
  309. jnz short KiUR60 ; if nz, threshold not zero
  310. mov ecx, KiAdjustDpcThreshold ; reset initial threshold counter
  311. mov PbAdjustDpcThreshold[rsi], ecx ;
  312. mov ecx, KiMaximumDpcQueueDepth ; get maximum DPC queue depth
  313. cmp ecx, PbMaximumDpcQueueDepth[rsi] ; check if depth at maximum level
  314. je short KiUR60 ; if e, aleady a maximum level
  315. inc dword ptr PbMaximumDpcQueueDepth[rsi] ; increment maximum depth
  316. ;
  317. ; Decrement current thread quantum and check to determine if a quantum end
  318. ; has occurred.
  319. ;
  320. KiUR60: sub byte ptr ThQuantum[rdi], CLOCK_QUANTUM_DECREMENT ; decrement quantum
  321. jg short KiUR80 ; if g, time remaining on quantum
  322. ;
  323. ; Set quantum end flag and initiate a dispather interrupt on the current
  324. ; processor.
  325. ;
  326. cmp rdi, PbIdleThread[rsi] ; check if idle thread
  327. je short KiUR80 ; if e, idle thread
  328. inc dword ptr PbQuantumEnd[rsi] ; set quantum end indicator
  329. mov cl, DISPATCH_LEVEL ; request dispatch interrupt
  330. call __imp_HalRequestSoftwareInterrupt ;
  331. if DBG
  332. KiUR70: mov byte ptr gs:[PcSkipTick], 0 ; clear skip tick indicator
  333. endif
  334. KiUR80: add rsp, sizeof UrFrame - (3 * 8) ; deallocate stack frame
  335. pop rdi ; restore nonvolatile registers
  336. pop rsi ;
  337. pop rbp ;
  338. ret ; return
  339. NESTED_END KeUpdateRunTime, _TEXT$00
  340. end