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 SystemTime->High2Time = NewTime.HighPart; 35 #endif 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