500 lines
13 KiB
C
500 lines
13 KiB
C
/*++
|
||
|
||
Module Name:
|
||
|
||
clock.c
|
||
|
||
Abstract:
|
||
|
||
This module implements the platform specific clock interrupt processing
|
||
routines in for the kernel.
|
||
|
||
Author:
|
||
|
||
Edward G. Chron (echron) 10-Apr-1996
|
||
|
||
Environment:
|
||
|
||
Kernel mode only.
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "ki.h"
|
||
#include <ia64.h>
|
||
#include <ntia64.h>
|
||
#include <ntexapi.h>
|
||
|
||
VOID
|
||
KiProcessProfileList (
|
||
IN PKTRAP_FRAME TrFrame,
|
||
IN KPROFILE_SOURCE Source,
|
||
IN PLIST_ENTRY ListHead
|
||
);
|
||
|
||
|
||
BOOLEAN
|
||
KiChkTimerExpireSysDpc (
|
||
IN ULONGLONG TickCount
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine determines if it should attempt to place a timer expiration DPC
|
||
in the system DPC list and to drive the DPC by initiating a dispatch level interrupt
|
||
on the current processor.
|
||
|
||
N.B. If DPC is already inserted on the DPC list, we're done.
|
||
|
||
Arguments:
|
||
|
||
TickCount - The lower tick count, timer table hand value
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN - Set to true if Queued DPC or if DPC already Queued.
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOLEAN ret = FALSE; // No DPC queued, default return value.
|
||
|
||
PLIST_ENTRY ListHead = &KiTimerTableListHead[(ULONG)TickCount%TIMER_TABLE_SIZE];
|
||
PLIST_ENTRY NextEntry = ListHead->Flink;
|
||
|
||
//
|
||
// Check to see if the list is empty.
|
||
//
|
||
if (NextEntry != ListHead) {
|
||
PKTIMER Timer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry);
|
||
ULONGLONG TimeValue = Timer->DueTime.QuadPart;
|
||
ULARGE_INTEGER CurrentTime;
|
||
|
||
//
|
||
// See if timer expired.
|
||
//
|
||
|
||
CurrentTime.LowPart = SharedUserData->InterruptTime.LowPart;
|
||
CurrentTime.HighPart = SharedUserData->InterruptTime.High1Time;
|
||
if (TimeValue <= CurrentTime.QuadPart) {
|
||
|
||
PKPRCB Prcb = KeGetCurrentPrcb();
|
||
PKDPC Dpc = &KiTimerExpireDpc;
|
||
|
||
_disable();
|
||
#if !defined(NT_UP)
|
||
KiAcquireSpinLock(&Prcb->DpcLock);
|
||
#endif
|
||
//
|
||
// Insert DPC only if not already inserted.
|
||
//
|
||
if (Dpc->Lock == NULL) {
|
||
|
||
//
|
||
// Put timer expiration DPC in the system DPC list and initiate
|
||
// a dispatch interrupt on the current processor.
|
||
//
|
||
|
||
Prcb->DpcCount += 1;
|
||
Prcb->DpcQueueDepth += 1;
|
||
Dpc->Lock = &Prcb->DpcLock;
|
||
Dpc->SystemArgument1 = (PVOID)TickCount;
|
||
Dpc->SystemArgument2 = 0;
|
||
InsertTailList(&Prcb->DpcListHead, &Dpc->DpcListEntry);
|
||
KiRequestSoftwareInterrupt(DISPATCH_LEVEL);
|
||
}
|
||
|
||
ret = TRUE;
|
||
|
||
#if !defined(NT_UP)
|
||
KiReleaseSpinLock(&Prcb->DpcLock);
|
||
#endif
|
||
|
||
_enable();
|
||
}
|
||
}
|
||
|
||
return(ret);
|
||
}
|
||
|
||
|
||
VOID
|
||
KeUpdateSystemTime (
|
||
IN PKTRAP_FRAME TrFrame,
|
||
IN ULONG Increment
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is executed on a single processor in the processor complex.
|
||
It's function is to update the system time and to check to determine if a
|
||
timer has expired.
|
||
|
||
N.B. This routine is executed on a single processor in a multiprocess system.
|
||
The remainder of the processors in the complex execute the quantum end and
|
||
runtime update code.
|
||
|
||
Arguments:
|
||
|
||
TrFrame - Supplies a pointer to a trap frame.
|
||
|
||
Increment - The time increment to be used to adjust the time slice for the next
|
||
tick. The value is supplied in 100ns units.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG LowTime;
|
||
LONG HighTime;
|
||
LONG SaveTickOffset;
|
||
|
||
//
|
||
// Update the interrupt time in the shared region.
|
||
//
|
||
|
||
LowTime = SharedUserData->InterruptTime.LowPart + Increment;
|
||
HighTime = SharedUserData->InterruptTime.High1Time + (LowTime < Increment);
|
||
SharedUserData->InterruptTime.High2Time = HighTime;
|
||
SharedUserData->InterruptTime.LowPart = LowTime;
|
||
SharedUserData->InterruptTime.High1Time = HighTime;
|
||
|
||
KiTickOffset -= Increment;
|
||
|
||
SaveTickOffset = KiTickOffset;
|
||
|
||
if ((LONG)KiTickOffset > 0)
|
||
{
|
||
//
|
||
// Tick has not completed (100ns time units remain).
|
||
//
|
||
// Determine if a timer has expired at the current hand value.
|
||
//
|
||
|
||
KiChkTimerExpireSysDpc(KeTickCount.QuadPart);
|
||
|
||
} else {
|
||
|
||
//
|
||
// Tick has completed, tick count set to maximum increase plus any
|
||
// residue and system time is updated.
|
||
//
|
||
// Compute next tick offset.
|
||
//
|
||
|
||
KiTickOffset += KeMaximumIncrement;
|
||
LowTime = SharedUserData->SystemTime.LowPart + KeTimeAdjustment;
|
||
HighTime = SharedUserData->SystemTime.High1Time + (LowTime < KeTimeAdjustment);
|
||
SharedUserData->SystemTime.High2Time = HighTime;
|
||
SharedUserData->SystemTime.LowPart = LowTime;
|
||
SharedUserData->SystemTime.High1Time = HighTime;
|
||
++KeTickCount.QuadPart;
|
||
SharedUserData->TickCountLow = (ULONG)KeTickCount.QuadPart;
|
||
|
||
//
|
||
// Determine if a timer has expired at either the current hand value or
|
||
// the next hand value.
|
||
//
|
||
|
||
if (!KiChkTimerExpireSysDpc(KeTickCount.QuadPart - 1))
|
||
KiChkTimerExpireSysDpc(KeTickCount.QuadPart);
|
||
|
||
}
|
||
|
||
if (SaveTickOffset <= 0) {
|
||
KeUpdateRunTime(TrFrame);
|
||
}
|
||
}
|
||
|
||
|
||
VOID
|
||
KeUpdateRunTime (
|
||
IN PKTRAP_FRAME TrFrame
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is executed on all processors in the processor complex.
|
||
It's function is to update the run time of the current thread, udpate the run
|
||
time for the thread's process, and decrement the current thread's quantum.
|
||
|
||
Arguments:
|
||
|
||
TrFrame - Supplies a pointer to a trap frame.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
KSPIN_LOCK Lock;
|
||
PKPRCB Prcb = KeGetCurrentPrcb();
|
||
PKTHREAD Thread = KeGetCurrentThread();
|
||
PKPROCESS Process = Thread->ApcState.Process;
|
||
|
||
//
|
||
// If thread was executing in user mode:
|
||
// increment the thread user time.
|
||
// atomically increment the process user time.
|
||
// else If the old IRQL is greater than the DPC level:
|
||
// increment the time executing interrupt service routines.
|
||
// else If the old IRQL is less than the DPC level or If a DPC is not active:
|
||
// increment the thread kernel time.
|
||
// atomically increment the process kernel time.
|
||
// else
|
||
// increment time executing DPC routines.
|
||
//
|
||
|
||
if (TrFrame->PreviousMode != KernelMode) {
|
||
++Thread->UserTime;
|
||
|
||
// Atomic Update of Process User Time required.
|
||
ExInterlockedIncrementLong(&Process->UserTime, &Lock);
|
||
|
||
// Update the time spent in user mode for the current processor.
|
||
++Prcb->UserTime;
|
||
} else {
|
||
|
||
if (TrFrame->OldIrql > DISPATCH_LEVEL) {
|
||
++Prcb->InterruptTime;
|
||
} else if ((TrFrame->OldIrql < DISPATCH_LEVEL) ||
|
||
(Prcb->DpcRoutineActive == 0)) {
|
||
++Thread->KernelTime;
|
||
ExInterlockedIncrementLong(&Process->KernelTime, &Lock);
|
||
} else {
|
||
++Prcb->DpcTime;
|
||
}
|
||
|
||
//
|
||
// Update the time spent in kernel mode for the current processor.
|
||
//
|
||
|
||
++Prcb->KernelTime;
|
||
}
|
||
|
||
//
|
||
// Update the DPC request rate which is computed as the average between the
|
||
// previous rate and the current rate.
|
||
// Update the DPC last count with the current DPC count.
|
||
//
|
||
Prcb->DpcRequestRate = ((Prcb->DpcCount - Prcb->DpcLastCount) + Prcb->DpcRequestRate) >> 1;
|
||
Prcb->DpcLastCount = Prcb->DpcCount;
|
||
|
||
//
|
||
// If the DPC queue depth is not zero and a DPC routine is not active.
|
||
// Request a dispatch interrupt.
|
||
// Decrement the maximum DPC queue depth.
|
||
// Reset the threshold counter if appropriate.
|
||
//
|
||
if (Prcb->DpcQueueDepth != 0 && Prcb->DpcRoutineActive == 0) {
|
||
|
||
Prcb->AdjustDpcThreshold = KiAdjustDpcThreshold;
|
||
|
||
// Need to request a DPC interrupt.
|
||
KiRequestSoftwareInterrupt(DISPATCH_LEVEL);
|
||
|
||
if (Prcb->DpcRequestRate < KiIdealDpcRate && Prcb->MaximumDpcQueueDepth > 1)
|
||
--Prcb->MaximumDpcQueueDepth;
|
||
} else {
|
||
//
|
||
// The DPC queue is empty or a DPC routine is active or a DPC interrupt
|
||
// has been requested. Count down the adjustment threshold and if the count
|
||
// reaches zero, then increment the maximum DPC queue depth but not above
|
||
// the initial value. Also, reset the adjustment threshold value.
|
||
//
|
||
--Prcb->AdjustDpcThreshold;
|
||
if (Prcb->AdjustDpcThreshold == 0) {
|
||
Prcb->AdjustDpcThreshold = KiAdjustDpcThreshold;
|
||
if (KiMaximumDpcQueueDepth != Prcb->MaximumDpcQueueDepth)
|
||
++Prcb->MaximumDpcQueueDepth;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Decrement current thread quantum and determine if quantum end has occurred.
|
||
//
|
||
Thread->Quantum -= CLOCK_QUANTUM_DECREMENT;
|
||
|
||
// Set quantum end if time expired, for any thread except idle thread.
|
||
if (Thread->Quantum <= 0 && Thread != Prcb->IdleThread) {
|
||
|
||
Prcb->QuantumEnd = 1;
|
||
|
||
// Need to request a DPC interrupt.
|
||
KiRequestSoftwareInterrupt(DISPATCH_LEVEL);
|
||
}
|
||
|
||
#ifdef _MERCED_A0_
|
||
//
|
||
// if SignalDone of the processor prcb is set, an IPI is to be serviced
|
||
// but the corresponding IPI may have been lost on pre-B3 processors;
|
||
// therefore, send another IPI to workaround this problem
|
||
//
|
||
if (KeGetCurrentPrcb()->SignalDone != 0) {
|
||
HalRequestIpi(PCR->SetMember);
|
||
}
|
||
#endif // _MERCED_A0_
|
||
|
||
}
|
||
|
||
|
||
VOID
|
||
KeProfileInterrupt (
|
||
IN PKTRAP_FRAME TrFrame
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is executed on all processors in the processor complex.
|
||
The routine is entered as the result of an interrupt generated by the profile
|
||
timer. Its function is to update the profile information for the currently
|
||
active profile objects.
|
||
|
||
N.B. KeProfileInterrupt is an alternate entry for backwards compatability that
|
||
sets the source to zero (ProfileTime).
|
||
|
||
Arguments:
|
||
|
||
TrFrame - Supplies a pointer to a trap frame.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
KPROFILE_SOURCE Source = 0;
|
||
|
||
KeProfileInterruptWithSource(TrFrame, Source);
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
KeProfileInterruptWithSource (
|
||
IN PKTRAP_FRAME TrFrame,
|
||
IN KPROFILE_SOURCE Source
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is executed on all processors in the processor complex.
|
||
The routine is entered as the result of an interrupt generated by the profile
|
||
timer. Its function is to update the profile information for the currently
|
||
active profile objects.
|
||
|
||
N.B. KeProfileInterruptWithSource is not currently fully implemented by any of
|
||
the architectures.
|
||
|
||
Arguments:
|
||
|
||
TrFrame - Supplies a pointer to a trap frame.
|
||
|
||
Source - Supplies the source of the profile interrupt.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PKTHREAD Thread = KeGetCurrentThread();
|
||
PKPROCESS Process = Thread->ApcState.Process;
|
||
|
||
PERFINFO_PROFILE(TrFrame, Source);
|
||
|
||
#if !defined(NT_UP)
|
||
KiAcquireSpinLock(&KiProfileLock);
|
||
#endif
|
||
|
||
KiProcessProfileList(TrFrame, Source, &Process->ProfileListHead);
|
||
|
||
KiProcessProfileList(TrFrame, Source, &KiProfileListHead);
|
||
|
||
#if !defined(NT_UP)
|
||
KiReleaseSpinLock(&KiProfileLock);
|
||
#endif
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
KiProcessProfileList (
|
||
IN PKTRAP_FRAME TrFrame,
|
||
IN KPROFILE_SOURCE Source,
|
||
IN PLIST_ENTRY ListHead
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is executed on all processors in the processor complex.
|
||
The routine is entered as the result of an interrupt generated by the profile
|
||
timer. Its function is to update the profile information for the currently
|
||
active profile objects.
|
||
|
||
N.B. KeProfileInterruptWithSource is not currently fully implemented by any of
|
||
the architectures.
|
||
|
||
Arguments:
|
||
|
||
TrFrame - Supplies a pointer to a trap frame.
|
||
|
||
Source - Supplies the source of the profile interrupt.
|
||
|
||
ListHead - Supplies a pointer to a profile list.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PLIST_ENTRY NextEntry = ListHead->Flink;
|
||
PKPRCB Prcb = KeGetCurrentPrcb();
|
||
|
||
//
|
||
// Scan profile list and increment profile buckets as appropriate.
|
||
//
|
||
for (; NextEntry != ListHead; NextEntry = NextEntry->Flink) {
|
||
PCHAR BucketPter;
|
||
PULONG BucketValue;
|
||
|
||
PKPROFILE Profile = CONTAINING_RECORD(NextEntry, KPROFILE, ProfileListEntry);
|
||
|
||
if ( (Profile->Source != Source) || ((Profile->Affinity & Prcb->SetMember) == 0) ) {
|
||
continue;
|
||
}
|
||
|
||
if ( ((PVOID)TrFrame->StIIP < Profile->RangeBase) || ((PVOID)TrFrame->StIIP > Profile->RangeLimit) ) {
|
||
continue;
|
||
}
|
||
|
||
BucketPter = (PCHAR)Profile->Buffer +
|
||
((((PCHAR)TrFrame->StIIP - (PCHAR)Profile->RangeBase)
|
||
>> Profile->BucketShift) & 0xFFFFFFFC);
|
||
BucketValue = (PULONG) BucketPter;
|
||
(*BucketValue)++;
|
||
}
|
||
|
||
return;
|
||
}
|