/* * PROJECT: ReactOS Kernel * LICENSE: BSD - See COPYING.ARM in the top level directory * FILE: ntoskrnl/ke/time.c * PURPOSE: Implements timebase functionality * PROGRAMMERS: ReactOS Portable Systems Group */ /* INCLUDES *******************************************************************/ #include #define NDEBUG #include /* GLOBALS ********************************************************************/ LONG KiTickOffset; ULONG KeTimeAdjustment; BOOLEAN KiTimeAdjustmentEnabled = FALSE; /* FUNCTIONS ******************************************************************/ FORCEINLINE VOID KiWriteSystemTime(volatile KSYSTEM_TIME *SystemTime, ULARGE_INTEGER NewTime) { #ifdef _WIN64 /* Do a single atomic write */ *(ULONGLONG*)SystemTime = NewTime.QuadPart; #else /* Update in 3 steps, so that a reader can recognize partial updates */ SystemTime->High1Time = NewTime.HighPart; SystemTime->LowPart = NewTime.LowPart; SystemTime->High2Time = NewTime.HighPart; #endif } FORCEINLINE VOID KiCheckForTimerExpiration( PKPRCB Prcb, PKTRAP_FRAME TrapFrame, ULARGE_INTEGER InterruptTime) { ULONG Hand; /* Check for timer expiration */ Hand = KeTickCount.LowPart & (TIMER_TABLE_SIZE - 1); if (KiTimerTableListHead[Hand].Time.QuadPart <= InterruptTime.QuadPart) { /* Check if we are already doing expiration */ if (!Prcb->TimerRequest) { /* Request a DPC to handle this */ Prcb->TimerRequest = (ULONG_PTR)TrapFrame; Prcb->TimerHand = Hand; HalRequestSoftwareInterrupt(DISPATCH_LEVEL); } } } VOID FASTCALL KeUpdateSystemTime(IN PKTRAP_FRAME TrapFrame, IN ULONG Increment, IN KIRQL Irql) { PKPRCB Prcb = KeGetCurrentPrcb(); ULARGE_INTEGER CurrentTime, InterruptTime; LONG OldTickOffset; /* Check if this tick is being skipped */ if (Prcb->SkipTick) { /* Handle it next time */ Prcb->SkipTick = FALSE; /* Increase interrupt count and end the interrupt */ Prcb->InterruptCount++; KiEndInterrupt(Irql, TrapFrame); /* Note: non-x86 return back to the caller! */ return; } /* Add the increment time to the shared data */ InterruptTime.QuadPart = *(ULONGLONG*)&SharedUserData->InterruptTime; InterruptTime.QuadPart += Increment; KiWriteSystemTime(&SharedUserData->InterruptTime, InterruptTime); /* Check for timer expiration */ KiCheckForTimerExpiration(Prcb, TrapFrame, InterruptTime); /* Update the tick offset */ OldTickOffset = InterlockedExchangeAdd(&KiTickOffset, -(LONG)Increment); /* If the debugger is enabled, check for break-in request */ if (KdDebuggerEnabled && KdPollBreakIn()) { /* Break-in requested! */ DbgBreakPointWithStatus(DBG_STATUS_CONTROL_C); } /* Check for full tick */ if (OldTickOffset <= (LONG)Increment) { /* Update the system time */ CurrentTime.QuadPart = *(ULONGLONG*)&SharedUserData->SystemTime; CurrentTime.QuadPart += KeTimeAdjustment; KiWriteSystemTime(&SharedUserData->SystemTime, CurrentTime); /* Update the tick count */ CurrentTime.QuadPart = (*(ULONGLONG*)&KeTickCount) + 1; KiWriteSystemTime(&KeTickCount, CurrentTime); /* Update it in the shared user data */ KiWriteSystemTime(&SharedUserData->TickCount, CurrentTime); /* Check for expiration with the new tick count as well */ KiCheckForTimerExpiration(Prcb, TrapFrame, InterruptTime); /* Reset the tick offset */ KiTickOffset += KeMaximumIncrement; /* Update processor/thread runtime */ KeUpdateRunTime(TrapFrame, Irql); } else { /* Increase interrupt count only */ Prcb->InterruptCount++; } /* Disable interrupts and end the interrupt */ KiEndInterrupt(Irql, TrapFrame); } VOID NTAPI KeUpdateRunTime(IN PKTRAP_FRAME TrapFrame, IN KIRQL Irql) { PKTHREAD Thread = KeGetCurrentThread(); PKPRCB Prcb = KeGetCurrentPrcb(); /* Check if this tick is being skipped */ if (Prcb->SkipTick) { /* Handle it next time */ Prcb->SkipTick = FALSE; return; } /* Increase interrupt count */ Prcb->InterruptCount++; /* Check if we came from user mode */ #ifndef _M_ARM if (KiUserTrap(TrapFrame) || (TrapFrame->EFlags & EFLAGS_V86_MASK)) #else if (TrapFrame->PreviousMode == UserMode) #endif { /* Increase thread user time */ Prcb->UserTime++; Thread->UserTime++; } else { /* See if we were in an ISR */ Prcb->KernelTime++; if (Irql > DISPATCH_LEVEL) { /* Handle that */ Prcb->InterruptTime++; } else if ((Irql < DISPATCH_LEVEL) || !(Prcb->DpcRoutineActive)) { /* Handle being in kernel mode */ Thread->KernelTime++; } else { /* Handle being in a DPC */ Prcb->DpcTime++; #if DBG /* Update the DPC time */ Prcb->DebugDpcTime++; /* Check if we have timed out */ if (Prcb->DebugDpcTime == KiDPCTimeout) { /* We did! */ DbgPrint("*** DPC routine > 1 sec --- This is not a break in KeUpdateSystemTime\n"); /* Break if debugger is enabled */ if (KdDebuggerEnabled) DbgBreakPoint(); /* Clear state */ Prcb->DebugDpcTime = 0; } #endif } } /* Update DPC rates */ Prcb->DpcRequestRate = ((Prcb->DpcData[0].DpcCount - Prcb->DpcLastCount) + Prcb->DpcRequestRate) >> 1; Prcb->DpcLastCount = Prcb->DpcData[0].DpcCount; /* Check if the queue is large enough */ if ((Prcb->DpcData[0].DpcQueueDepth) && !(Prcb->DpcRoutineActive)) { /* Request a DPC */ Prcb->AdjustDpcThreshold = KiAdjustDpcThreshold; HalRequestSoftwareInterrupt(DISPATCH_LEVEL); /* Fix the maximum queue depth */ if ((Prcb->DpcRequestRate < KiIdealDpcRate) && (Prcb->MaximumDpcQueueDepth > 1)) { /* Make it smaller */ Prcb->MaximumDpcQueueDepth--; } } else { /* Check if we've reached the adjustment limit */ if (!(--Prcb->AdjustDpcThreshold)) { /* Reset it, and check the queue maximum */ Prcb->AdjustDpcThreshold = KiAdjustDpcThreshold; if (KiMaximumDpcQueueDepth != Prcb->MaximumDpcQueueDepth) { /* Increase it */ Prcb->MaximumDpcQueueDepth++; } } } /* Decrement the thread quantum */ Thread->Quantum -= CLOCK_QUANTUM_DECREMENT; /* Check if the time expired */ if ((Thread->Quantum <= 0) && (Thread != Prcb->IdleThread)) { /* Schedule a quantum end */ Prcb->QuantumEnd = 1; HalRequestSoftwareInterrupt(DISPATCH_LEVEL); } }