Windows2000/private/ntos/ke/mips/xxclock.s

593 lines
21 KiB
ArmAsm
Raw Normal View History

2001-01-01 00:00:00 +01:00
// TITLE("Interval and Profile Clock Interrupts")
//++
//
// Copyright (c) 1990 Microsoft Corporation
//
// Module Name:
//
// xxclock.s
//
// Abstract:
//
// This module implements the code necessary to field and process the
// interval and profile clock interrupts.
//
// Author:
//
// David N. Cutler (davec) 27-Mar-1990
//
// Environment:
//
// Kernel mode only.
//
// Revision History:
//
//--
#include "ksmips.h"
//
// Define external variables that can be addressed using GP.
//
.extern KeMaximumIncrement 4
.extern KeTickCount 3 * 4
.extern KeTimeAdjustment 4
.extern KiAdjustDpcThreshold 4
.extern KiIdealDpcRate 4
.extern KiMaximumDpcQueueDepth 4
.extern KiProfileListHead 2 * 4
.extern KiProfileLock 4
.extern KiTickOffset 4
SBTTL("Update System Time")
//++
//
// VOID
// KeUpdateSystemTime (
// IN PKTRAP_FRAME TrapFrame,
// IN ULONG TimeIncrement
// )
//
// Routine Description:
//
// This routine is entered as the result of an interrupt generated by the
// interval timer. Its function is to update the system time and 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 only execute the quantum end
// and runtime update code.
//
// Arguments:
//
// TrapFrame (a0) - Supplies a pointer to a trap frame.
//
// TimeIncrement (a1) - Supplies the time increment in 100ns units.
//
// Return Value:
//
// None.
//
//--
LEAF_ENTRY(KeUpdateSystemTime)
//
// Update the interrupt time.
//
ld t8,KiPcr2 + Pc2InterruptTime // get interrupt time
daddu t8,t8,a1 // add time increment value
sd t8,KiPcr2 + Pc2InterruptTime // store interrupt time
//
// Update tick offset and check for "system clock" tick.
//
lw a2,KiTickOffset // get tick offset value
sub a2,a2,a1 // subtract time increment
ld v0,KeTickCount // get low and high 1 tick count
la t0,KiTimerTableListHead // get base address of timer table
sw a2,KiTickOffset // store tick offset value
bgtz a2,10f // if gtz, tick not completed
lw a3,KeMaximumIncrement // get maximum increment value
//
// Update system time.
//
lw t1,KeTimeAdjustment // get time adjustment value
ld t2,KiPcr2 + Pc2SystemTime // get low and high 1 system time
daddu t2,t2,t1 // add time increment value
sd t2,KiPcr2 + Pc2SystemTime // store low nad high 1 system time
//
// Update the tick count.
//
// N.B. The tick count is updated in a very strict manner so that an
// interlock does not have to be used in an MP system. This is
// required for backward compatibility with old drivers and file
// systems.
//
daddu t2,v0,1 // increment tick count
dsrl t3,t2,32 // get high half of tick count
sw t2,KiPcr2 + Pc2TickCountLow(zero) // store low tick count
.set noreorder
.set noat
sw t3,KeTickCount + 8 // store high 2 tick count
sd t2,KeTickCount // store low and high 1 tick count
.set at
.set reorder
//
// Compute next tick offset value.
//
addu a3,a3,a2 // add maximum increment to residue
sw a3,KiTickOffset // store tick offset value
//
// Check to determine if a timer has expired at the current hand value.
//
and t1,v0,TIMER_TABLE_SIZE - 1 // reduce to table table index
sll t2,t1,3 // compute timer table listhead address
addu t2,t2,t0 //
lw t3,LsFlink(t2) // get address of first timer in list
beq t2,t3,5f // if eq, no timer active
//
// Get the expiration time from the timer object.
//
// N.B. The offset to the timer list entry must be subtracted out of the
// displacement calculation.
//
ld t4,TiDueTime - TiTimerListEntry(t3) // get timer due time
sltu t9,t8,t4 // check if timer is due
beq zero,t9,20f // if eq, timer has expired
//
// Check to determine if a timer has expired at the next hand value.
//
5: addu v0,v0,1 // advance hand value to next entry
10: and t1,v0,TIMER_TABLE_SIZE - 1 // reduce to table table index
sll t2,t1,3 // compute timer table listhead address
addu t2,t2,t0 //
lw t3,LsFlink(t2) // get address of first timer in list
beq t2,t3,40f // if eq, no timer active
//
// Get the expiration time from the timer object.
//
// N.B. The offset to the timer list entry must be subtracted out of the
// displacement calculation.
//
ld t4,TiDueTime - TiTimerListEntry(t3) // get timer due time
sltu t9,t8,t4 // check if timer is due
bne zero,t9,40f // if ne, timer has not expired
//
// Put timer expiration DPC in the system DPC list and initiate a dispatch
// interrupt on the current processor.
//
20: la t0,KiTimerExpireDpc // get expiration DPC address
lw a1,KiPcr + PcPrcb(zero) // get address of PRCB
DISABLE_INTERRUPTS(t2) // disable interrupts
addu t3,a1,PbDpcListHead // compute DPC listhead address
addu v1,a1,PbDpcLock // compute DPC lock address
#if !defined(NT_UP)
30: ll t4,0(v1) // get current lock value
move t5,t3 // set lock ownership value
bne zero,t4,30b // if ne, spin lock owned
sc t5,0(v1) // set spin lock owned
beq zero,t5,30b // if eq, store conditional failed
#endif
lw t4,DpLock(t0) // get DPC inserted state
bne zero,t4,35f // if ne, DPC entry already inserted
lw t4,LsBlink(t3) // get address of last entry in list
sw v1,DpLock(t0) // set DPC inserted state
sw v0,DpSystemArgument1(t0) // set timer table hand value
addu t0,t0,DpDpcListEntry // compute address of DPC list entry
sw t0,LsBlink(t3) // set address of new last entry
sw t0,LsFlink(t4) // set next link in old last entry
sw t3,LsFlink(t0) // set address of next entry
sw t4,LsBlink(t0) // set address of previous entry
lw t5,PbDpcQueueDepth(a1) // increment DPC queue depth
addu t5,t5,1 //
sw t5,PbDpcQueueDepth(a1) //
.set noreorder
.set noat
mfc0 t3,cause // get exception cause register
or t3,t3,DISPATCH_INTERRUPT // merge dispatch interrut request
mtc0 t3,cause // set exception cause register
.set at
.set reorder
35: //
#if !defined(NT_UP)
sw zero,0(v1) // set spin lock not owned
#endif
ENABLE_INTERRUPTS(t2) // enable interrupts
40: blez a2,50f // if lez, full tick
j ra // return
50: j KeUpdateRunTime
.end KeUpdateSystemTime
SBTTL("Update Thread and Process Runtime")
//++
//
// VOID
// KeUpdateRunTime (
// IN PKTRAP_FRAME TrapFrame
// )
//
// Routine Description:
//
// This routine is entered as the result of an interrupt generated by the
// interval timer. Its function is to update the runtime of the current
// thread, update the runtime of the current thread's process, and decrement
// the current thread's quantum.
//
// N.B. This routine is executed on all processors in a multiprocess system.
//
// Arguments:
//
// TrapFrame (a0) - Supplies a pointer to a trap frame.
//
// Return Value:
//
// None.
//
//--
LEAF_ENTRY(KeUpdateRunTime)
lw t0,KiPcr + PcCurrentThread(zero) // get current thread address
lw t2,ThApcState + AsProcess(t0) // get address of current process
lw t3,TrPsr(a0) // get saved processor status
lw t5,KiPcr + PcPrcb(zero) // get current processor block address
lw t7,PbDpcRoutineActive(t5) // get DPC active flag
and t4,t3,0x1 << PSR_PMODE // isolate previous processor mode
bne zero,t4,30f // if ne, previous mode was user
//
// If a DPC is active, then increment the time spent executing DPC routines.
// Otherwise, if the old IRQL is greater than DPC level, then increment the
// time spent executing interrupt services routines. Otherwise, increment
// the time spent in kernel mode for the current thread.
//
lbu t6,TrOldIrql(a0) // get previous IRQL
subu t6,t6,DISPATCH_LEVEL // compare IRQL with DPC level
bltz t6,20f // if ltz, increment thread kernel time
addu t8,t5,PbInterruptTime // compute interrupt time address
bgtz t6,10f // if gtz, increment interrupt time
addu t8,t5,PbDpcTime // compute DPC time address
beq zero,t7,20f // if eq, increment thread kernel time
//
// Update the time spend in DPC/interrupt processing.
//
10: lw t6,0(t8) // get processor time
addu t6,t6,1 // increment processor time
sw t6,0(t8) // store processor time
addu t9,t5,PbKernelTime // compute processor kernel time address
b 50f //
//
// Update the time spent in kernel mode for the current thread.
//
20: lw t6,ThKernelTime(t0) // get kernel time
addu t6,t6,1 // increment kernel time
sw t6,ThKernelTime(t0) // store kernel time
addu t2,t2,PrKernelTime // compute process kernel time address
addu t9,t5,PbKernelTime // compute processor kernel time address
b 40f //
//
// Update the time spent in user mode for the current thread.
//
30: lw t6,ThUserTime(t0) // get user time
addu t6,t6,1 // increment user time
sw t6,ThUserTime(t0) // store user time
addu t2,t2,PrUserTime // compute process user time address
addu t9,t5,PbUserTime // compute processor user time address
//
// Update the time spent in kernel/user mode for the current thread's process.
//
// N.B. The update of the process time must be synchronized across processors.
//
40: ll t6,0(t2) // get process time
addu t6,t6,1 // increment process time
sc t6,0(t2) // store process time
beq zero,t6,40b // if eq, store conditional failed
//
// Update the time spent in kernel/user mode for the current processor.
//
50: lw t6,0(t9) // get processor time
addu t6,t6,1 // increment processor time
sw t6,0(t9) // store processor time
//
// Update the DPC request rate which is computed as the average between
// the previous rate and the current rate.
//
lw a0,PbDpcCount(t5) // get current DPC count
lw a1,PbDpcLastCount(t5) // get last DPC count
lw a2,PbDpcRequestRate(t5) // get last DPC request rate
lw a3,PbDpcQueueDepth(t5) // get current DPC queue depth
sw a0,PbDpcLastCount(t5) // set last DPC count
subu a0,a0,a1 // compute count during interval
addu a0,a0,a2 // compute sum of current and last
srl a0,a0,1 // average current and last
sw a0,PbDpcRequestRate(t5) // set new DPC request rate
//
// If the current DPC queue depth is not zero, a DPC routine is not active,
// and a DPC interrupt has not been requested, then request a dispatch
// interrupt, decrement the maximum DPC queue depth, and reset the threshold
// counter if appropriate.
//
lw v0,PbDpcInterruptRequested(t5) // get DPC interrupt requested
beq zero,a3,60f // if eq, DPC queue is empty
or v0,v0,t7 // merge DPC interrupt requested and active
bne zero,v0,60f // if ne, DPC active or interrupt requested
DISABLE_INTERRUPTS(a1) // disable interrupt
.set noreorder
.set noat
mfc0 a2,cause // get exception cause register
lw v0,PbMaximumDpcQueueDepth(t5) // get maximum queue depth
lw v1,KiIdealDpcRate // get ideal DPC rate
or a2,a2,DISPATCH_INTERRUPT // merge dispatch interrut request
mtc0 a2,cause // set exception cause register
.set at
.set reorder
ENABLE_INTERRUPTS(a1) // enable interrupts
sltu a0,a0,v1 // test if current rate less than ideal
lw a1,KiAdjustDpcThreshold // reset initial threshold counter
sw a1,PbAdjustDpcThreshold(t5) //
beq zero,a0,KiDecrementQuantum // if eq, rate greater or equal ideal
subu v0,v0,1 // decrement maximum DPC queue depth
beq zero,v0,KiDecrementQuantum // if eq, current value is one
sw v0,PbMaximumDpcQueueDepth(t5) // set new maximum DPC queue depth
b KiDecrementQuantum //
//
// 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
// no above the initial value and reset the adjustment threshold value.
//
60: lw a0,PbAdjustDpcThreshold(t5) // get adjustment threshold counter
lw a1,PbMaximumDpcQueueDepth(t5) // get current maximum queue depth
lw a2,KiMaximumDpcQueueDepth // get initial maximum queue depth
subu a0,a0,1 // decrement adjustment threshold counter
sw a0,PbAdjustDpcThreshold(t5) //
bne zero,a0,KiDecrementQuantum // if ne, adjustment counter not zero
lw a0,KiAdjustDpcThreshold //set new DPC threshold counter
sw a0,PbAdjustDpcThreshold(t5) //
beq a1,a2,KiDecrementQuantum // if eq, currently at maximum depth
addu a1,a1,1 // increment current maximum queue depth
sw a1,PbMaximumDpcQueueDepth(t5) // set new maximum DPC queue depth
//
// Decrement current thread quantum and check to determine if a quantum end
// has occurred.
//
ALTERNATE_ENTRY(KiDecrementQuantum)
lb t6,ThQuantum(t0) // get current thread quantum
sub t6,t6,CLOCK_QUANTUM_DECREMENT // decrement current quantum
sb t6,ThQuantum(t0) // store thread quantum
bgtz t6,60f // if gtz, quantum remaining
//
// Set quantum end flag and initiate a dispatch interrupt on the current
// processor.
//
lw t1,PbIdleThread(t5) // get address of idle
beq t0,t1,60f // if eq, idle thread
sw sp,KiPcr + PcQuantumEnd(zero) // set quantum end indicator
DISABLE_INTERRUPTS(t0) // disable interrupts
.set noreorder
.set noat
mfc0 t1,cause // get exception cause register
or t1,t1,DISPATCH_INTERRUPT // merge dispatch interrupt request
mtc0 t1,cause // set exception cause register
nop // 1 cycle hazzard
.set at
.set reorder
ENABLE_INTERRUPTS(t0) // enable interrupts
60: j ra // return
.end KeUpdateRunTime
SBTTL("Process Profile Interrupt")
//++
//
// VOID
// KeProfileInterruptWithSource (
// IN PKTRAP_FRAME TrapFrame,
// IN KPROFILE_SOURCE ProfileSource
// )
//
// VOID
// KeProfileInterrupt (
// IN PKTRAP_FRAME TrapFrame
// )
//
// Routine Description:
//
// This 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. This routine is executed on all processors in a multiprocess system.
//
// Arguments:
//
// TrapFrame (a0) - Supplies a pointer to a trap frame.
//
// ProfileSource (a1) - Supplies the source of the profile interrupt
// KeProfileInterrupt is an alternate entry for backwards
// compatibility that sets the source to zero (ProfileTime)
//
// Return Value:
//
// None.
//
//--
.struct 0
.space 4 * 4 // argument save area
.space 3 * 4 //
PfRa: .space 4 // return address
ProfileFrameLength: // profile frame length
NESTED_ENTRY(KeProfileInterrupt, ProfileFrameLength, zero)
move a1, zero // set profile source to ProfileTime
ALTERNATE_ENTRY(KeProfileInterruptWithSource)
subu sp,sp,ProfileFrameLength // allocate stack frame
sw ra,PfRa(sp) // save return address
PROLOGUE_END
#if !defined(NT_UP)
10: ll t0,KiProfileLock // get current lock value
move t1,s0 // set ownership value
bne zero,t0,10b // if ne, spin lock owned
sc t1,KiProfileLock // set spin lock owned
beq zero,t1,10b // if eq, store conditional failed
#endif
lw a2,KiPcr + PcCurrentThread(zero) // get current thread address
lw a2,ThApcState + AsProcess(a2) // get address of current process
addu a2,a2,PrProfileListHead // compute profile listhead address
jal KiProcessProfileList // process the process profile list
la a2,KiProfileListHead // get profile listhead address
jal KiProcessProfileList // process the system profile list
#if !defined(NT_UP)
sw zero,KiProfileLock // set spin lock not owned
#endif
lw ra,PfRa(sp) // restore return address
addu sp,sp,ProfileFrameLength // deallocate stack frame
j ra // return
.end KeProfileInterrupt
SBTTL("Process Profile List")
//++
//
// VOID
// KiProcessProfileList (
// IN PKTRAP_FRAME TrapFrame,
// IN KPROFILE_SOURCE Source,
// IN PLIST_ENTRY ListHead
// )
//
// Routine Description:
//
// This routine is called to process a profile list.
//
// Arguments:
//
// TrapFrame (a0) - Supplies a pointer to a trap frame.
//
// Source (a1) - Supplies profile source to match
//
// ListHead (a2) - Supplies a pointer to a profile list.
//
// Return Value:
//
// None.
//
//--
LEAF_ENTRY(KiProcessProfileList)
lw t8,LsFlink(a2) // get address of next entry
li a3,0xfffffffc // set bucket mask value
beq a2,t8,30f // if eq, end of list
lw t0,TrFir(a0) // get interrupt PC address
lw t6,KiPcr + PcSetMember(zero) // get current processor member
//
// Scan profile list and increment profile buckets as appropriate.
//
10: lw t1,PfRangeBase - PfProfileListEntry(t8) // get base of range
lw t2,PfRangeLimit - PfProfileListEntry(t8) // get limit of range
lhu t3,PfSource - PfProfileListEntry(t8) // get source
lhu t4,PfAffinity - PfProfileListEntry(t8) // get affinity
bne t3,a1,20f // if ne, source mismatch
sltu v0,t0,t1 // check against range base
sltu v1,t0,t2 // check against range limit
and t5,t6,t4 // check against processor
bne zero,v0,20f // if ne, less that range base
beq zero,v1,20f // if eq, not less that range limit
beq zero,t5,20f // if eq, affinity mismatch
subu t1,t0,t1 // compute offset in range
lw t2,PfBucketShift - PfProfileListEntry(t8) // get shift count
lw v0,PfBuffer - PfProfileListEntry(t8) // get profile buffer address
srl v1,t1,t2 // compute bucket offset
and v1,v1,a3 // clear low order offset bits
addu v1,v1,v0 // compute bucket address
lw v0,0(v1) // increment profile bucket
addu v0,v0,1 //
sw v0,0(v1) //
20: lw t8,LsFlink(t8) // get address of next entry
bne a2,t8,10b // if ne, more entries in profile list
30: j ra // return
.end KiProcessProfileList