xref: /reactos/ntoskrnl/ke/time.c (revision 5ebd4783)
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 FORCEINLINE
24 VOID
KiWriteSystemTime(volatile KSYSTEM_TIME * SystemTime,ULARGE_INTEGER NewTime)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 #endif
35     SystemTime->High2Time = NewTime.HighPart;
36 }
37 
38 FORCEINLINE
39 VOID
KiCheckForTimerExpiration(PKPRCB Prcb,PKTRAP_FRAME TrapFrame,ULARGE_INTEGER InterruptTime)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
KeUpdateSystemTime(IN PKTRAP_FRAME TrapFrame,IN ULONG Increment,IN KIRQL Irql)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 
81 #ifdef _M_IX86 // x86 optimization
82         KiEndInterrupt(Irql, TrapFrame);
83 #endif
84 
85         /* Note: non-x86 return back to the caller! */
86         return;
87     }
88 
89     /* Add the increment time to the shared data */
90     InterruptTime.QuadPart = *(ULONGLONG*)&SharedUserData->InterruptTime;
91     InterruptTime.QuadPart += Increment;
92     KiWriteSystemTime(&SharedUserData->InterruptTime, InterruptTime);
93 
94     /* Check for timer expiration */
95     KiCheckForTimerExpiration(Prcb, TrapFrame, InterruptTime);
96 
97     /* Update the tick offset */
98     OldTickOffset = InterlockedExchangeAdd(&KiTickOffset, -(LONG)Increment);
99 
100     /* If the debugger is enabled, check for break-in request */
101     if (KdDebuggerEnabled && KdPollBreakIn())
102     {
103         /* Break-in requested! */
104         DbgBreakPointWithStatus(DBG_STATUS_CONTROL_C);
105     }
106 
107     /* Check for full tick */
108     if (OldTickOffset <= (LONG)Increment)
109     {
110         /* Update the system time */
111         CurrentTime.QuadPart = *(ULONGLONG*)&SharedUserData->SystemTime;
112         CurrentTime.QuadPart += KeTimeAdjustment;
113         KiWriteSystemTime(&SharedUserData->SystemTime, CurrentTime);
114 
115         /* Update the tick count */
116         CurrentTime.QuadPart = (*(ULONGLONG*)&KeTickCount) + 1;
117         KiWriteSystemTime(&KeTickCount, CurrentTime);
118 
119         /* Update it in the shared user data */
120         KiWriteSystemTime(&SharedUserData->TickCount, CurrentTime);
121 
122         /* Check for expiration with the new tick count as well */
123         KiCheckForTimerExpiration(Prcb, TrapFrame, InterruptTime);
124 
125         /* Reset the tick offset */
126         KiTickOffset += KeMaximumIncrement;
127 
128         /* Update processor/thread runtime */
129         KeUpdateRunTime(TrapFrame, Irql);
130     }
131     else
132     {
133         /* Increase interrupt count only */
134         Prcb->InterruptCount++;
135     }
136 
137 #ifdef _M_IX86 // x86 optimization
138     /* Disable interrupts and end the interrupt */
139     KiEndInterrupt(Irql, TrapFrame);
140 #endif
141 }
142 
143 VOID
144 NTAPI
KeUpdateRunTime(IN PKTRAP_FRAME TrapFrame,IN KIRQL Irql)145 KeUpdateRunTime(IN PKTRAP_FRAME TrapFrame,
146                 IN KIRQL Irql)
147 {
148     PKTHREAD Thread = KeGetCurrentThread();
149     PKPRCB Prcb = KeGetCurrentPrcb();
150 
151     /* Check if this tick is being skipped */
152     if (Prcb->SkipTick)
153     {
154         /* Handle it next time */
155         Prcb->SkipTick = FALSE;
156         return;
157     }
158 
159     /* Increase interrupt count */
160     Prcb->InterruptCount++;
161 
162     /* Check if we came from user mode */
163 #ifndef _M_ARM
164     if (KiUserTrap(TrapFrame) || (TrapFrame->EFlags & EFLAGS_V86_MASK))
165 #else
166     if (TrapFrame->PreviousMode == UserMode)
167 #endif
168     {
169         /* Increase thread user time */
170         Prcb->UserTime++;
171         Thread->UserTime++;
172     }
173     else
174     {
175         /* See if we were in an ISR */
176         Prcb->KernelTime++;
177         if (Irql > DISPATCH_LEVEL)
178         {
179             /* Handle that */
180             Prcb->InterruptTime++;
181         }
182         else if ((Irql < DISPATCH_LEVEL) || !(Prcb->DpcRoutineActive))
183         {
184             /* Handle being in kernel mode */
185             Thread->KernelTime++;
186         }
187         else
188         {
189             /* Handle being in a DPC */
190             Prcb->DpcTime++;
191 
192 #if DBG
193             /* Update the DPC time */
194             Prcb->DebugDpcTime++;
195 
196             /* Check if we have timed out */
197             if (Prcb->DebugDpcTime == KiDPCTimeout)
198             {
199                 /* We did! */
200                 DbgPrint("*** DPC routine > 1 sec --- This is not a break in KeUpdateSystemTime\n");
201 
202                 /* Break if debugger is enabled */
203                 if (KdDebuggerEnabled) DbgBreakPoint();
204 
205                 /* Clear state */
206                 Prcb->DebugDpcTime = 0;
207             }
208 #endif
209         }
210     }
211 
212     /* Update DPC rates */
213     Prcb->DpcRequestRate = ((Prcb->DpcData[0].DpcCount - Prcb->DpcLastCount) +
214                             Prcb->DpcRequestRate) >> 1;
215     Prcb->DpcLastCount = Prcb->DpcData[0].DpcCount;
216 
217     /* Check if the queue is large enough */
218     if ((Prcb->DpcData[0].DpcQueueDepth) && !(Prcb->DpcRoutineActive))
219     {
220         /* Request a DPC */
221         Prcb->AdjustDpcThreshold = KiAdjustDpcThreshold;
222         HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
223 
224         /* Fix the maximum queue depth */
225         if ((Prcb->DpcRequestRate < KiIdealDpcRate) &&
226             (Prcb->MaximumDpcQueueDepth > 1))
227         {
228             /* Make it smaller */
229             Prcb->MaximumDpcQueueDepth--;
230         }
231     }
232     else
233     {
234         /* Check if we've reached the adjustment limit */
235         if (!(--Prcb->AdjustDpcThreshold))
236         {
237             /* Reset it, and check the queue maximum */
238             Prcb->AdjustDpcThreshold = KiAdjustDpcThreshold;
239             if (KiMaximumDpcQueueDepth != Prcb->MaximumDpcQueueDepth)
240             {
241                 /* Increase it */
242                 Prcb->MaximumDpcQueueDepth++;
243             }
244         }
245     }
246 
247     /* Decrement the thread quantum */
248     Thread->Quantum -= CLOCK_QUANTUM_DECREMENT;
249 
250     /* Check if the time expired */
251     if ((Thread->Quantum <= 0) && (Thread != Prcb->IdleThread))
252     {
253         /* Schedule a quantum end */
254         Prcb->QuantumEnd = 1;
255         HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
256     }
257 }
258