2 * PROJECT: ReactOS Kernel
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * FILE: ntoskrnl/ke/time.c
5 * PURPOSE: Implements timebase functionality
6 * PROGRAMMERS: ReactOS Portable Systems Group
9 /* INCLUDES *******************************************************************/
15 /* GLOBALS ********************************************************************/
18 ULONG KeTimeAdjustment
;
19 BOOLEAN KiTimeAdjustmentEnabled
= FALSE
;
21 /* FUNCTIONS ******************************************************************/
25 KiWriteSystemTime(volatile KSYSTEM_TIME
*SystemTime
, ULARGE_INTEGER NewTime
)
28 /* Do a single atomic write */
29 *(ULONGLONG
*)SystemTime
= NewTime
.QuadPart
;
31 /* Update in 3 steps, so that a reader can recognize partial updates */
32 SystemTime
->High1Time
= NewTime
.HighPart
;
33 SystemTime
->LowPart
= NewTime
.LowPart
;
35 SystemTime
->High2Time
= NewTime
.HighPart
;
40 KiCheckForTimerExpiration(
42 PKTRAP_FRAME TrapFrame
,
43 ULARGE_INTEGER InterruptTime
)
47 /* Check for timer expiration */
48 Hand
= KeTickCount
.LowPart
& (TIMER_TABLE_SIZE
- 1);
49 if (KiTimerTableListHead
[Hand
].Time
.QuadPart
<= InterruptTime
.QuadPart
)
51 /* Check if we are already doing expiration */
52 if (!Prcb
->TimerRequest
)
54 /* Request a DPC to handle this */
55 Prcb
->TimerRequest
= (ULONG_PTR
)TrapFrame
;
56 Prcb
->TimerHand
= Hand
;
57 HalRequestSoftwareInterrupt(DISPATCH_LEVEL
);
64 KeUpdateSystemTime(IN PKTRAP_FRAME TrapFrame
,
68 PKPRCB Prcb
= KeGetCurrentPrcb();
69 ULARGE_INTEGER CurrentTime
, InterruptTime
;
72 /* Check if this tick is being skipped */
75 /* Handle it next time */
76 Prcb
->SkipTick
= FALSE
;
78 /* Increase interrupt count and end the interrupt */
79 Prcb
->InterruptCount
++;
80 KiEndInterrupt(Irql
, TrapFrame
);
82 /* Note: non-x86 return back to the caller! */
86 /* Add the increment time to the shared data */
87 InterruptTime
.QuadPart
= *(ULONGLONG
*)&SharedUserData
->InterruptTime
;
88 InterruptTime
.QuadPart
+= Increment
;
89 KiWriteSystemTime(&SharedUserData
->InterruptTime
, InterruptTime
);
91 /* Check for timer expiration */
92 KiCheckForTimerExpiration(Prcb
, TrapFrame
, InterruptTime
);
94 /* Update the tick offset */
95 OldTickOffset
= InterlockedExchangeAdd(&KiTickOffset
, -(LONG
)Increment
);
97 /* If the debugger is enabled, check for break-in request */
98 if (KdDebuggerEnabled
&& KdPollBreakIn())
100 /* Break-in requested! */
101 DbgBreakPointWithStatus(DBG_STATUS_CONTROL_C
);
104 /* Check for full tick */
105 if (OldTickOffset
<= (LONG
)Increment
)
107 /* Update the system time */
108 CurrentTime
.QuadPart
= *(ULONGLONG
*)&SharedUserData
->SystemTime
;
109 CurrentTime
.QuadPart
+= KeTimeAdjustment
;
110 KiWriteSystemTime(&SharedUserData
->SystemTime
, CurrentTime
);
112 /* Update the tick count */
113 CurrentTime
.QuadPart
= (*(ULONGLONG
*)&KeTickCount
) + 1;
114 KiWriteSystemTime(&KeTickCount
, CurrentTime
);
116 /* Update it in the shared user data */
117 KiWriteSystemTime(&SharedUserData
->TickCount
, CurrentTime
);
119 /* Check for expiration with the new tick count as well */
120 KiCheckForTimerExpiration(Prcb
, TrapFrame
, InterruptTime
);
122 /* Reset the tick offset */
123 KiTickOffset
+= KeMaximumIncrement
;
125 /* Update processor/thread runtime */
126 KeUpdateRunTime(TrapFrame
, Irql
);
130 /* Increase interrupt count only */
131 Prcb
->InterruptCount
++;
134 /* Disable interrupts and end the interrupt */
135 KiEndInterrupt(Irql
, TrapFrame
);
140 KeUpdateRunTime(IN PKTRAP_FRAME TrapFrame
,
143 PKTHREAD Thread
= KeGetCurrentThread();
144 PKPRCB Prcb
= KeGetCurrentPrcb();
146 /* Check if this tick is being skipped */
149 /* Handle it next time */
150 Prcb
->SkipTick
= FALSE
;
154 /* Increase interrupt count */
155 Prcb
->InterruptCount
++;
157 /* Check if we came from user mode */
159 if (KiUserTrap(TrapFrame
) || (TrapFrame
->EFlags
& EFLAGS_V86_MASK
))
161 if (TrapFrame
->PreviousMode
== UserMode
)
164 /* Increase thread user time */
170 /* See if we were in an ISR */
172 if (Irql
> DISPATCH_LEVEL
)
175 Prcb
->InterruptTime
++;
177 else if ((Irql
< DISPATCH_LEVEL
) || !(Prcb
->DpcRoutineActive
))
179 /* Handle being in kernel mode */
180 Thread
->KernelTime
++;
184 /* Handle being in a DPC */
188 /* Update the DPC time */
189 Prcb
->DebugDpcTime
++;
191 /* Check if we have timed out */
192 if (Prcb
->DebugDpcTime
== KiDPCTimeout
)
195 DbgPrint("*** DPC routine > 1 sec --- This is not a break in KeUpdateSystemTime\n");
197 /* Break if debugger is enabled */
198 if (KdDebuggerEnabled
) DbgBreakPoint();
201 Prcb
->DebugDpcTime
= 0;
207 /* Update DPC rates */
208 Prcb
->DpcRequestRate
= ((Prcb
->DpcData
[0].DpcCount
- Prcb
->DpcLastCount
) +
209 Prcb
->DpcRequestRate
) >> 1;
210 Prcb
->DpcLastCount
= Prcb
->DpcData
[0].DpcCount
;
212 /* Check if the queue is large enough */
213 if ((Prcb
->DpcData
[0].DpcQueueDepth
) && !(Prcb
->DpcRoutineActive
))
216 Prcb
->AdjustDpcThreshold
= KiAdjustDpcThreshold
;
217 HalRequestSoftwareInterrupt(DISPATCH_LEVEL
);
219 /* Fix the maximum queue depth */
220 if ((Prcb
->DpcRequestRate
< KiIdealDpcRate
) &&
221 (Prcb
->MaximumDpcQueueDepth
> 1))
223 /* Make it smaller */
224 Prcb
->MaximumDpcQueueDepth
--;
229 /* Check if we've reached the adjustment limit */
230 if (!(--Prcb
->AdjustDpcThreshold
))
232 /* Reset it, and check the queue maximum */
233 Prcb
->AdjustDpcThreshold
= KiAdjustDpcThreshold
;
234 if (KiMaximumDpcQueueDepth
!= Prcb
->MaximumDpcQueueDepth
)
237 Prcb
->MaximumDpcQueueDepth
++;
242 /* Decrement the thread quantum */
243 Thread
->Quantum
-= CLOCK_QUANTUM_DECREMENT
;
245 /* Check if the time expired */
246 if ((Thread
->Quantum
<= 0) && (Thread
!= Prcb
->IdleThread
))
248 /* Schedule a quantum end */
249 Prcb
->QuantumEnd
= 1;
250 HalRequestSoftwareInterrupt(DISPATCH_LEVEL
);