593 lines
21 KiB
ArmAsm
593 lines
21 KiB
ArmAsm
// 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
|