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