Synchronize up to trunk's revision r57689.
[reactos.git] / ntoskrnl / ke / time.c
1 /*
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
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 /* GLOBALS ********************************************************************/
16
17 LONG KiTickOffset;
18 ULONG KeTimeAdjustment;
19 BOOLEAN KiTimeAdjustmentEnabled = FALSE;
20
21 /* FUNCTIONS ******************************************************************/
22
23 VOID
24 FORCEINLINE
25 KiWriteSystemTime(volatile KSYSTEM_TIME *SystemTime, ULARGE_INTEGER NewTime)
26 {
27 #ifdef _WIN64
28 /* Do a single atomic write */
29 *(ULONGLONG*)SystemTime = NewTime.QuadPart;
30 #else
31 /* Update in 3 steps, so that a reader can recognize partial updates */
32 SystemTime->High1Time = NewTime.HighPart;
33 SystemTime->LowPart = NewTime.LowPart;
34 SystemTime->High2Time = NewTime.HighPart;
35 #endif
36 }
37
38 VOID
39 FORCEINLINE
40 KiCheckForTimerExpiration(
41 PKPRCB Prcb,
42 PKTRAP_FRAME TrapFrame,
43 ULARGE_INTEGER InterruptTime)
44 {
45 ULONG Hand;
46
47 /* Check for timer expiration */
48 Hand = KeTickCount.LowPart & (TIMER_TABLE_SIZE - 1);
49 if (KiTimerTableListHead[Hand].Time.QuadPart <= InterruptTime.QuadPart)
50 {
51 /* Check if we are already doing expiration */
52 if (!Prcb->TimerRequest)
53 {
54 /* Request a DPC to handle this */
55 Prcb->TimerRequest = (ULONG_PTR)TrapFrame;
56 Prcb->TimerHand = Hand;
57 HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
58 }
59 }
60 }
61
62 VOID
63 FASTCALL
64 KeUpdateSystemTime(IN PKTRAP_FRAME TrapFrame,
65 IN ULONG Increment,
66 IN KIRQL Irql)
67 {
68 PKPRCB Prcb = KeGetCurrentPrcb();
69 ULARGE_INTEGER CurrentTime, InterruptTime;
70 LONG OldTickOffset;
71
72 /* Check if this tick is being skipped */
73 if (Prcb->SkipTick)
74 {
75 /* Handle it next time */
76 Prcb->SkipTick = FALSE;
77
78 /* Increase interrupt count and end the interrupt */
79 Prcb->InterruptCount++;
80 KiEndInterrupt(Irql, TrapFrame);
81 }
82
83 /* Add the increment time to the shared data */
84 InterruptTime.QuadPart = *(ULONGLONG*)&SharedUserData->InterruptTime;
85 InterruptTime.QuadPart += Increment;
86 KiWriteSystemTime(&SharedUserData->InterruptTime, InterruptTime);
87
88 /* Check for timer expiration */
89 KiCheckForTimerExpiration(Prcb, TrapFrame, InterruptTime);
90
91 /* Update the tick offset */
92 OldTickOffset = InterlockedExchangeAdd(&KiTickOffset, -(LONG)Increment);
93
94 /* If the debugger is enabled, check for break-in request */
95 if (KdDebuggerEnabled && KdPollBreakIn())
96 {
97 /* Break-in requested! */
98 DbgBreakPointWithStatus(DBG_STATUS_CONTROL_C);
99 }
100
101 /* Check for full tick */
102 if (OldTickOffset <= (LONG)Increment)
103 {
104 /* Update the system time */
105 CurrentTime.QuadPart = *(ULONGLONG*)&SharedUserData->SystemTime;
106 CurrentTime.QuadPart += KeTimeAdjustment;
107 KiWriteSystemTime(&SharedUserData->SystemTime, CurrentTime);
108
109 /* Update the tick count */
110 CurrentTime.QuadPart = (*(ULONGLONG*)&KeTickCount) + 1;
111 KiWriteSystemTime(&KeTickCount, CurrentTime);
112
113 /* Update it in the shared user data */
114 KiWriteSystemTime(&SharedUserData->TickCount, CurrentTime);
115
116 /* Check for expiration with the new tick count as well */
117 KiCheckForTimerExpiration(Prcb, TrapFrame, InterruptTime);
118
119 /* Reset the tick offset */
120 KiTickOffset += KeMaximumIncrement;
121
122 /* Update processor/thread runtime */
123 KeUpdateRunTime(TrapFrame, Irql);
124 }
125 else
126 {
127 /* Increase interrupt count only */
128 Prcb->InterruptCount++;
129 }
130
131 /* Disable interrupts and end the interrupt */
132 KiEndInterrupt(Irql, TrapFrame);
133 }
134
135 VOID
136 NTAPI
137 KeUpdateRunTime(IN PKTRAP_FRAME TrapFrame,
138 IN KIRQL Irql)
139 {
140 PKTHREAD Thread = KeGetCurrentThread();
141 PKPRCB Prcb = KeGetCurrentPrcb();
142
143 /* Check if this tick is being skipped */
144 if (Prcb->SkipTick)
145 {
146 /* Handle it next time */
147 Prcb->SkipTick = FALSE;
148 return;
149 }
150
151 /* Increase interrupt count */
152 Prcb->InterruptCount++;
153
154 /* Check if we came from user mode */
155 #ifndef _M_ARM
156 if ((TrapFrame->SegCs & MODE_MASK) || (TrapFrame->EFlags & EFLAGS_V86_MASK))
157 #else
158 if (TrapFrame->PreviousMode == UserMode)
159 #endif
160 {
161 /* Increase thread user time */
162 Prcb->UserTime++;
163 Thread->UserTime++;
164 }
165 else
166 {
167 /* See if we were in an ISR */
168 Prcb->KernelTime++;
169 if (Irql > DISPATCH_LEVEL)
170 {
171 /* Handle that */
172 Prcb->InterruptTime++;
173 }
174 else if ((Irql < DISPATCH_LEVEL) || !(Prcb->DpcRoutineActive))
175 {
176 /* Handle being in kernel mode */
177 Thread->KernelTime++;
178 }
179 else
180 {
181 /* Handle being in a DPC */
182 Prcb->DpcTime++;
183
184 #if 0 //DBG
185 /* Update the DPC time */
186 Prcb->DebugDpcTime++;
187
188 /* Check if we have timed out */
189 if (Prcb->DebugDpcTime == KiDPCTimeout);
190 {
191 /* We did! */
192 DbgPrint("*** DPC routine > 1 sec --- This is not a break in KeUpdateSystemTime\n");
193
194 /* Break if debugger is enabled */
195 if (KdDebuggerEnabled) DbgBreakPoint();
196
197 /* Clear state */
198 Prcb->DebugDpcTime = 0;
199 }
200 #endif
201 }
202 }
203
204 /* Update DPC rates */
205 Prcb->DpcRequestRate = ((Prcb->DpcData[0].DpcCount - Prcb->DpcLastCount) +
206 Prcb->DpcRequestRate) >> 1;
207 Prcb->DpcLastCount = Prcb->DpcData[0].DpcCount;
208
209 /* Check if the queue is large enough */
210 if ((Prcb->DpcData[0].DpcQueueDepth) && !(Prcb->DpcRoutineActive))
211 {
212 /* Request a DPC */
213 Prcb->AdjustDpcThreshold = KiAdjustDpcThreshold;
214 HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
215
216 /* Fix the maximum queue depth */
217 if ((Prcb->DpcRequestRate < KiIdealDpcRate) &&
218 (Prcb->MaximumDpcQueueDepth > 1))
219 {
220 /* Make it smaller */
221 Prcb->MaximumDpcQueueDepth--;
222 }
223 }
224 else
225 {
226 /* Check if we've reached the adjustment limit */
227 if (!(--Prcb->AdjustDpcThreshold))
228 {
229 /* Reset it, and check the queue maximum */
230 Prcb->AdjustDpcThreshold = KiAdjustDpcThreshold;
231 if (KiMaximumDpcQueueDepth != Prcb->MaximumDpcQueueDepth)
232 {
233 /* Increase it */
234 Prcb->MaximumDpcQueueDepth++;
235 }
236 }
237 }
238
239 /* Decrement the thread quantum */
240 Thread->Quantum -= CLOCK_QUANTUM_DECREMENT;
241
242 /* Check if the time expired */
243 if ((Thread->Quantum <= 0) && (Thread != Prcb->IdleThread))
244 {
245 /* Schedule a quantum end */
246 Prcb->QuantumEnd = 1;
247 HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
248 }
249 }