.TITLE LAT$RATING_DPT - LAT Driver Supplemental Rating Image .IDENT 'V1.0-004' ; ;+----------------------------------------------------------------------------+ ;! ! ;! Copyright (c) 1994-1995 by ! ;! Digital Equipment Corporation, ! ;! Maynard, Massachusetts. ! ;! All rights reserved. ! ;! ! ;! This software is furnished under a license and may be used and copied ! ;! only in accordance with the terms of such license and with the inclusion ! ;! of the above copyright notice. This software or any other copies ! ;! thereof may not be provided or otherwise made available to any other ! ;! person. No title to and ownership of the software is hereby transferred. ! ;! ! ;! The information in this software is subject to change without notice and ! ;! should not be construed as a commitment by Digital Equipment Corporation. ! ;! ! ;! Digital assumes no responsibility for the use or reliability of its ! ;! software on equipment that is not supplied by Digital. ! ;! ! ;+----------------------------------------------------------------------------+ ; ;++ ; Facility: ; ; OpenVMS LAT Terminal Driver Software ; ; Abstract: ; ; LAT Rating Algorithm ; ; This module is part of the LAT rating algorithm. It handles ; initialization when the rating image is loaded by SYSGEN and ; contains call entry points from LTDRIVER. Although global data ; storage is maintained in this module, the actual rating and ; load average calculations are contained in LAT$RATING_CALC.C. ; ; Authors: ; ; Michael D. Raspuzzi ; ; Revision history: ; ; V1.0-004 Guy Peleg 04-Aug-2002 ; Fix conditionalization for IPF port. ; ; V1.0-003 Michael D. Raspuzzi 20-Mar-1995 ; Add code for calculating CPU load average that is ; done on Alpha since this routine is in SCHED_ROUTINES. ; This is provided for example only in case the default ; routine needs to be customized by site. ; ; V1.0-002 Michael D. Raspuzzi 28-Feb-1995 ; Use correct register to clear nounload bit in DPT. ; ; V1.0-001 Michael D. Raspuzzi 13-May-1994 ; Initial module creation. Seperate rating calculation ; logic out of LTDRIVER into a seperate (SYSGEN loadable) ; image. This image is loaded by LAT$CONFIG.COM at LAT ; startup time. ;-- .SBTTL Description ;++ ; The LAT rating algorithm is no longer part of the LTDRIVER ; image. Instead, a new image (SYS$LOADABLE_IMAGES:LAT$RATING.EXE) ; houses the code that calculates the LAT rating. This image is ; loaded by the LAT startup command procedures (specifically, by ; SYS$STARTUP:LAT$CONFIG.COM). LAT$RATING.EXE is loaded before ; LTDRIVER and before LATACP is started. ; ; The purpose of this external image is to provide sites with ; an easier method for modifying the default LAT rating algorithm. ; The files necessary to produce the LAT rating image that ships ; with OpenVMS by default are in SYS$EXAMPLES. There are 3 files :- ; ; LAT$RATING_DPT.MAR - Image setup and macro routines ; LAT$RATING_CALC.C - Rating and load average calculation ; LAT$RATING_BUILD.COM - Used to produce LAT$RATING.EXE ; ; These files may be modified to suit different environments as ; necessary. Caution: modification of the rating image should ; be done by someone familiar with OpenVMS internals and device ; drivers. Programming errors in the rating image can cause ; undesired system crashes. ; ; The LAT rating image has a vector of callable routines that ; are used by LTDRIVER. This vector currently consists of the ; following: ; ; +-----------------------------------------------------+ ; | Address of controller init routine | 0 ; +-----------------------------------------------------+ ; | Address of unit init routine (not currently used) | 4 ; +-----------------------------------------------------+ ; | Address of rating init routine | 8 ; +-----------------------------------------------------+ ; | Address of 1 second timer routine | 12 ; +-----------------------------------------------------+ ; | Address of rating calculation routine | 16 ; +-----------------------------------------------------+ ; | Address of stop routine | 20 ; +-----------------------------------------------------+ ; ; The controller init routine is not called by SYSGEN because ; the LAT rating image has no associated UCB (therefore, no ; SYSGEN CONNECT command is done). Instead, the controller init ; routine is located by LTDRIVER and is called when LTDRIVER's ; controller init routine is called. Note, since no UCB is ; associated with this image, the arguments to the controller ; init routine are all 0. It relocates the vector table properly ; and sets the "not unloadable bit" in the DPT. ; ; The unit init routine is present but does nothing as there is ; no UCB associated with this image. It is never called. ; ; The rating init routine is called by LTDRIVER when the LAT protocol ; is started. It performs the same load average initialization ; that LTDRIVER used to perform when the LAT protocol was started. ; ; The timer tick routine is called every second by LTDRIVER. It ; is responsible for calculating a new load average. ; ; The rating calculation routine is called by LTDRIVER when it ; must know the current dynamic LAT rating for insertion into ; a LAT service announcement message. This typically occurs ; when the LAT multicast timer expires (default 60 seconds). ; ; The stop routine or shutdown routine is called when the LAT ; protocol is stopped. LTDRIVER calls the shutdown entry routine ; and the LAT rating image's unloadable status bit is cleared ; in its DPT. ; ; The LAT rating relies heavily on the system load average that ; this image calculates. LAT$RATING_CALC.C has the code that ; performs most of this. If once per second is not a good enough ; sample, the rating init entry point in this module can be modified ; to create a seperate OpenVMS timer queue entry (TQE) for this ; image. This allows one to modify this image such that it has its ; own independent load average calculation. Just be aware that the ; more frequently the load average is calculated, modifications ; must be done to the load average calculation in order to maintain ; the current decay rate. ; ; The current decay rate will cause a load average of 100 to reach ; 0 on an idle system in about 60 seconds. This decay rate is ; handled by using 97.85% of the current system load and only 2.15% ; of the new system load (and this is done once every second). ; ; The following formula is used to maintain the decay rate: ; ; OLD_AVG_PCT = exp ((12/n) * ln(.9)) ; NEW_AVG_PCT = 1 - OLD_AVG_PCT ; ; Where: ; "exp" is the standard FORTRAN exp() function and ; "ln" is the natural log function. "n" is the ; number of samples per minute to be taken. ; ; So, if one modified this image to calculate the load average ; every 500ms (120 times per minute), to maintain the same ; decay rate :- ; ; OLD_AVG_PCT = exp ((12/120) * ln(.9)) ; = exp (.1 * ln(.9)) ; = .9895 ; NEW_AVG_PCT = .0105 ; ; Caution: do not use floating point arithmetic when calculating ; the LAT rating. This will crash OpenVMS on AXP systems and ; will be very inefficient on OpenVMS VAX (and may produce ; unpredictable results). Instead, it is safer to use integer ; arithmetic using a multiplication factor that is high enough ; (but not too high to cause arithmetic overflows). The default ; rating calculation code in the LAT$RATING_CALC.C module currently ; uses a multiplication factor of 1024. This is sufficient to ; produce desired results in the rating calculation. ;-- .SBTTL Global Definitions .ENABLE SUPPRESSION ; ; Global definitions ; $CRBDEF ; Define crb $DCDEF ; Define device adapter types $DDBDEF ; Define ddb $DDTDEF ; Define ddt $DPTDEF ; Define dpt $DYNDEF ; Define dynamic structure mnemonics $IPLDEF ; IPL levels $LATDEF ; Public LAT definitions $PCBDEF ; PCB data structure definitions $SSDEF ; Return condition codes $VECDEF ; Define vector for crb $WQHDEF ; Wait queue header definitions ; ; Macro for routine return ; .MACRO $LAT_RETURN .IF DF,VAX RSB ; For VAX JSB calls .IFF RET ; For AXP calls .ENDC .ENDM .SBTTL Driver Prologue Table .IF NDF,VAX ; Verified for IA64 port - Guy Peleg DRIVER_DATA $$$105_PROLOGUE ; ; Driver prologue table: ; LAT$RATING_DPT:: ; Driver start DPTAB - ; Driver prologue table STEP=2,- ; Driver step version END=LAT$RATING_END,- ; Address of end of module FLAGS=,- ; SMP ok ADAPTER=NULL,- ; Adapter type NAME=LAT$RATING,- ; Name of driver VECTOR=LAT$RATING_VECTOR,- ; Port driver vector table MAXUNITS=1 ; Maximum LTA unit number. This ; number will be changed in the ; controller init routine but can ; be changed by a user with the ; proper SETMODE function. .ENDC ; End EVAX .IF DF,VAX .PSECT $$$105_PROLOGUE ; ; Driver prologue table: ; LAT$RATING_DPT:: ; Driver start DPTAB - ; Driver prologue table END=LAT$RATING_END,- ; Address of end of module FLAGS=,- ; Version OK and SMP driver ADAPTER=NULL,- ; No adapter NAME=LAT$RATING,- ; Name of driver VECTOR=LAT$RATING_VECTOR,- ; LTDRIVER/rating image vector MAXUNITS=1 ; Maximum unit number - a don't ; care as this "driver" does not ; reference UCBs. .ENDC ; End VAX DPT_STORE INIT DPT_STORE REINIT DPT_STORE DDB,DDB$L_DDT,D,LTR$DDT .IF DF,VAX DPT_STORE CRB,CRB$L_INTD+VEC$L_INITIAL,D,LAT$RATING_CINIT ; Controller init DPT_STORE CRB,CRB$L_INTD+VEC$L_UNITINIT,D,LAT$RATING_UINIT ; Unit init .ENDC DPT_STORE END .IF DF,VAX DDTAB DEVNAM = LTR,- ; Dummy port driver dispatch table START = 0,- FUNCTB = 0 .ENDC .IF NDF,VAX ; Verified for IA64 port - Guy Peleg DDTAB DEVNAM = LTR,- ; Port driver dispatch table FUNCTB = 0,- CTRLINIT=LAT$RATING_CINIT,- UNITINIT=LAT$RATING_UINIT .ENDC .GLOBAL LAT$RATING_DPT ; Make this symbol global ;++ ; Global storage starts here. Important safety tip - don't ; define or use any global/static storage in the C source. ; Place all globally referenced storage here. ;-- .IF DF,VAX .PSECT $$$115_DRIVER,LONG .ENDC .IF NDF,VAX ; Verified for IA64 port - Guy Peleg DRIVER_DATA .ENDC ASSUME LAT$A_CONTROL_INIT_RTN EQ 0 ASSUME LAT$A_UNIT_INIT_RTN EQ 4 ASSUME LAT$A_RATING_INIT_RTN EQ 8 ASSUME LAT$A_TIMER_RTN EQ 12 ASSUME LAT$A_CALC_RATING_RTN EQ 16 ASSUME LAT$A_STOP_RTN EQ 20 ASSUME LAT$A_SPARE1_RTN EQ 24 ASSUME LAT$A_SPARE2_RTN EQ 28 ASSUME LAT$A_SPARE3_RTN EQ 32 ASSUME LAT$A_SPARE4_RTN EQ 36 ASSUME LAT$A_SPARE5_RTN EQ 40 ASSUME LAT$A_SPARE6_RTN EQ 44 ASSUME LAT$A_SPARE7_RTN EQ 48 ASSUME LAT$A_SPARE8_RTN EQ 52 .ALIGN LONG LAT$RATING_VECTOR:: ; Vector of routines between .ADDRESS LAT$RATING_CINIT ; Address of controller init .ADDRESS LAT$RATING_UINIT ; Address of unit init .ADDRESS LAT$RATING_INIT ; Rating init routine .ADDRESS LAT$TIMER_TICK ; Timer tick routine .ADDRESS LAT$CALCULATE_RATING ; Rating calculation routine .ADDRESS LAT$SHUTDOWN_RATING ; Shutdown routine when LAT stops .ADDRESS LAT$NULL ; Spare routine 1 .ADDRESS LAT$NULL ; Spare routine 2 .ADDRESS LAT$NULL ; Spare routine 3 .ADDRESS LAT$NULL ; Spare routine 4 .ADDRESS LAT$NULL ; Spare routine 5 .ADDRESS LAT$NULL ; Spare routine 6 .ADDRESS LAT$NULL ; Spare routine 7 .ADDRESS LAT$NULL ; Spare routine 8 .ADDRESS 0 ; End of table LAT$LOAD_AVERAGE:: ; Calculated load average .LONG 0 LAT$RATING_VALUE:: ; Calculated rating value .LONG 0 ; (returned to LTDRIVER) .IF NDF,VAX ; Verified for IA64 port - Guy Peleg DRIVER_CODE .ENDC ;+ ; Null routine to fill unsused vector entries ;- .IF DF,VAX .ENTRY LAT$NULL,^M<> .ENDC .IF NDF,VAX ; Verified for IA64 port - Guy Peleg LAT$NULL:: .CALL_ENTRY .ENDC RET .SBTTL LAT$RATING_CINIT - Controller Initialization Routine ;++ ; FUNCTIONAL DESCRIPTION: ; ; This routine is called at controller initialization time. ; It is responsible for initializing the vector that is used ; between this rating image and LTDRIVER. Because this image ; does not have an associated UCB, this routine is typically ; called from LTDRIVER's controller initialization routine. ; ; FORMAL PARAMETERS: ; ; LTDRIVER passes in the parameters as 0 ; R4 Address of device's CSR ; R5 Address of IDB associated with controller ; R6 Address of DDB associated with controller ; R8 Address of controller's CRB ; ; IMPLICIT INPUTS: ; ; Called at IPL$_POWER ; ; IMPLICIT OUTPUTS: ; ; Completion status returned in R0 (SS$_NORMAL) ;-- .ALIGN LONG LAT$RATING_CINIT:: .IF NDF,VAX ; Verified for IA64 port - Guy Peleg $DRIVER_CTRLINIT_ENTRY .ENDC MOVAB LAT$RATING_DPT,R1 ; Get our DPT address BBSSI #DPT$V_NOUNLOAD,- ; Make this image not unloadable DPT$L_FLAGS(R1),- ; until LAT is shutdown 20$ ; but forget this if already done .IF DF,VAX ;+ ; Vector routine addresses do not have to be relocated for Alpha ; so this code is conditionalized. ;- MOVAB LAT$RATING_VECTOR,R0 ; Get address of our vector ;+ ; Next, initialize each field of the vector that we use between ; the rating image and LTDRIVER. This is done by relocating each ; routine address. Note, relocation is only necessary on VAX. ;- 10$: TSTL (R0) ; Relocated or table end? BLEQ 20$ ; Yes, done ADDL R1,(R0)+ ; Relocate routine BRB 10$ ; And relocate it .ENDC 20$: MOVZBL #SS$_NORMAL,R0 ; And return success $LAT_RETURN .SBTTL LAT$RATING_UINIT - Unit Initialization Routine ;++ ; FUNCTIONAL DESCRIPTION: ; ; This routine is called at unit initialization time. Since ; this image is not associated with any UCBs, this routine ; should never be called. If it is called, simply return ; success and do nothing. ; ; FORMAL PARAMETERS: ; ; R3 Address of primary CSR ; R4 Address of secondary CSR ; R5 Address of unit control block (UCB) ; ; IMPLICIT INPUTS: ; ; Called at IPL$_POWER ; ; IMPLICIT OUTPUTS: ; ; Completion status returned in R0 (SS$_NORMAL) ;-- .ALIGN LONG LAT$RATING_UINIT:: .IF NDF,VAX ; Verified for IA64 port - Guy Peleg $DRIVER_UNITINIT_ENTRY .ENDC MOVZBL #SS$_NORMAL,R0 ; This routine did not do much $LAT_RETURN .SBTTL LAT$RATING_INIT - LAT Initialization Routine ;++ ; FUNCTIONAL DESCRIPTION: ; ; This routine is called when LTDRIVER is initialized and ; activated (essentially when SET NODE /STATE=ON is done). ; It is responsible for calculating an initial value for ; the load average. ; ; FORMAL PARAMETERS: ; ; None ; ; IMPLICIT INPUTS: ; ; Called with IOLOCK8 spinlock held ; ; IMPLICIT OUTPUTS: ; ; Completion status returned in R0 (SS$_NORMAL) ;-- .ALIGN LONG .IF DF,VAX .ENTRY LAT$RATING_INIT,^M<> .ENDC .IF NDF,VAX ; Verfieid for IA64 port - Guy Peleg LAT$RATING_INIT:: .CALL_ENTRY .ENDC ;+ ; Obtain the scheduler spinlock and then call the C routine to ; count up the number of processes in a "computable" wait state. ; This is then multiplied by 100 and is the initial seed value for ; the load average. On OpenVMS AXP, the routine that does this ; calculation is already in the kernel. ;- .IF DF,VAX LOCK LOCKNAME = SCHED,- ; Obtain scheduler spinlock SAVIPL = -(SP),- ; Save IPL on stack PRESERVE = NO CALLS #0,G^LAT$RATING_CALC_LOAD ; Calculate the load MULL3 #100,R0,LAT$LOAD_AVERAGE ; Scale it and store it UNLOCK LOCKNAME = SCHED,- ; Release scheduler spinlock NEWIPL = (SP)+,- ; Get original IPL from stack PRESERVE = NO .IFF ;+ ; Routine SCH$CALC_CPU_LOAD is in SCHED_ROUTINES. However, at ; the bottom of this module, you will find the code (commented out) ; in case its workings is insufficient. ;- CALLS #0,G^SCH$CALC_CPU_LOAD MULL3 #100,R0,LAT$LOAD_AVERAGE ; Scale it and store it .ENDC MOVZBL #SS$_NORMAL,R0 ; This routine did not do much RET .SBTTL LAT$TIMER_TICK - Calculate Moving Load Average ;++ ; FUNCTIONAL DESCRIPTION: ; ; This routine is called by LTDRIVER once every second. It ; is responsible for updating the system load average based on ; the old value and a small percentage of the newly calculated ; load average. ; ; FORMAL PARAMETERS: ; ; None ; ; IMPLICIT INPUTS: ; ; Called with IOLOCK8 spinlock held ; ; IMPLICIT OUTPUTS: ; ; Condition status returned in R0 (SS$_NORMAL) ;-- .ALIGN LONG .IF DF,VAX .ENTRY LAT$TIMER_TICK,^M ; Save destructos .ENDC .IF NDF,VAX ; Verified for IA64 port - Guy Peleg LAT$TIMER_TICK:: .CALL_ENTRY INPUT=,OUTPUT= .ENDC ;+ ; Obtain the scheduler spinlock and then call the C routine to ; count up the number of processes in a "computable" wait state. ; After getting current "computable" process count, call another ; routine to update the load average. On VAX, the routines necessary ; to do this are in the LAT$RATING modules. On OpenVMS AXP systems, ; this calculation is accomplished by SCH$CALC_CPU_LOAD. ;- .IF DF,VAX LOCK LOCKNAME = SCHED,- ; Obtain scheduler spinlock SAVIPL = -(SP),- ; Save IPL on stack PRESERVE = NO CALLS #0,G^LAT$RATING_CALC_LOAD ; Calculate the load MOVL R0,R5 ; Have new load average UNLOCK LOCKNAME = SCHED,- ; Release scheduler spinlock NEWIPL = (SP)+,- ; Get original IPL from stack PRESERVE = NO .IFF CALLS #0,G^SCH$CALC_CPU_LOAD MOVL R0,R5 ; Have new load average .ENDC PUSHL R5 ; Pass new load average on CALLS #1,G^LAT$NEW_LOAD_CALC ; Calculate new load average MOVZBL #SS$_NORMAL,R0 ; Return to LTDRIVER with success RET .SBTTL LAT$SHUTDOWN_RATING - LAT Shutdown Routine ;++ ; FUNCTIONAL DESCRIPTION: ; ; This routine is called when LTDRIVER has completed LAT ; software shutdown. It simply marks the rating algorithm ; image as unloadable so that a new one can replace the current ; one without rebooting the system. ; ; FORMAL PARAMETERS: ; ; None ; ; IMPLICIT INPUTS: ; ; Called at LAT software shutdown ; ; IMPLICIT OUTPUTS: ; ; Completion status returned in R0 (SS$_NORMAL) ;-- .ALIGN LONG .IF DF,VAX .ENTRY LAT$SHUTDOWN_RATING,^M<> .ENDC .IF NDF,VAX ; Verified for IA64 port - Guy Peleg LAT$SHUTDOWN_RATING:: .CALL_ENTRY .ENDC MOVAB LAT$RATING_DPT,R0 ; Get our DPT address BBCCI #DPT$V_NOUNLOAD,- ; Clear so that the image DPT$L_FLAGS(R0),- ; is now unloadable. 20$ 20$: MOVZBL #SS$_NORMAL,R0 ; This routine did not do much RET .SBTTL LAT$COUNT_COM_PCBS - Count PCBs on COM queues ;++ ; FUNCTIONAL DESCRIPTION: ; ; This routine counts the number of processes on the COM ; (compute) queues. It only works for OpenVMS VAX (a different ; routine in SCHED does this for OpenVMS AXP). Only those ; processes whose priority is greater than DEFPRI are counted. ; ; FORMAL PARAMETERS: ; ; None ; ; IMPLICIT INPUTS: ; ; Scheduling COM queues, caller must have SCHED spinlock ; ; IMPLICIT OUTPUTS: ; ; Number of processes in COM state ;-- .IF DF,VAX .ALIGN LONG .ENTRY LAT$COUNT_COM_PCBS,^M MOVL G^SCH$GL_COMQS,R7 ; Get bit mask work list MOVAL G^SCH$AQ_COMH,R6 MOVZBL G^SYS$GB_DEFPRI,R5 ; Get default priority SUBL3 R5,#32,R5 ; Highest Q is 32-DEFPRI CLRL R0 ; Init process count NEXTQ: FFS #0,R5,R7,R2 ; Find next non-empty queue BEQL QEMPTY ; All done? BBSC R2,R7,40$ ; Clear queue state 40$: MOVAQ (R6)[R2],R3 ; Get adr of queue head MOVL R3,R4 ; Copy queue header BRB 60$ ; Start loop at bottom 50$: CMPB #DYN$C_PCB,PCB$B_TYPE(R4) ; Make sure it's a process BNEQ 60$ ; If not, look at next entry in queue ; INCL R0 ; Count up interactive processes 60$: ASSUME PCB$L_SQFL EQ 0 MOVL (R4),R4 ; Get next process PCB CMPL R3,R4 ; End of queue? BNEQ 50$ ; Not yet BRB NEXTQ ; Yes, count PCBs in next queue QEMPTY: RET .SBTTL LAT$COUNT_COMO_PCBS - Count PCBs on COMO queues ;++ ; FUNCTIONAL DESCRIPTION: ; ; This routine counts the number of processes on the outswapped ; COM (compute) queues. It only works for OpenVMS VAX (a different ; routine in SCHED does this for OpenVMS AXP). ; ; FORMAL PARAMETERS: ; ; None ; ; IMPLICIT INPUTS: ; ; Scheduling COMO queues ; ; IMPLICIT OUTPUTS: ; ; Number of processes in COMO state ;-- .ALIGN LONG .ENTRY LAT$COUNT_COMO_PCBS,^M MOVL G^SCH$GL_COMOQS,R7 ; Get COMO queue worklist MOVAL G^SCH$AQ_COMOH,R6 MOVZBL G^SYS$GB_DEFPRI,R5 ; Get default priority SUBL3 R5,#32,R5 ; Highest Q is 32-DEFPRI CLRL R0 ; Init process count NEXT_Q: FFS #0,R5,R7,R2 ; Find next non-empty queue BEQL Q_EMPTY ; All done? BBSC R2,R7,40$ ; Clear queue state 40$: MOVAQ (R6)[R2],R3 ; Get adr of queue head MOVL R3,R4 ; Copy queue header BRB 60$ ; Start loop at bottom 50$: CMPB #DYN$C_PCB,PCB$B_TYPE(R4) ; Make sure it's a process BNEQ 60$ ; If not, look at next PCB ; INCL R0 ; Count up interactive processes 60$: ASSUME PCB$L_SQFL EQ 0 MOVL (R4),R4 ; Get next process PCB CMPL R3,R4 ; End of queue? BNEQ 50$ ; Not yet BRB NEXT_Q ; Yes, count PCBs in next queue Q_EMPTY: RET .ENDC .SBTTL SCH$CALC_CPU_LOAD - calculate CPU load ;++ ; FUNCTIONAL DESCRIPTION: ; ; This routine is used by the LAT driver (LTDRIVER in LAT facility) in ; its load calculations. The routine returns the total number of ; COM and COMO processes/kernel threads above defpri and other resource ; -waited processes/kernel threads. ; ; CALLING SEQUENCE: ; ; CALLS #0,SCH$CALC_CPU_LOAD ; ; INPUT PARAMETERS: ; ; none ; ; OUTPUT PARAMETERS ; ; R0 - Total number of COMO and COM processes/kernel threads above ; defpri plus the number of processes/kernel threads in ; miscellaneous, collided page, free page, and page fault wait. ; ; ENVIRONMENT: ; ; The SCHED lock must be held. ; ;-- ;+ ; NOTA BENE: This routine is commented out as it already exists in the ; OpenVMS AXP EXEC. If this routine is customized, it may be necessary ; to change its name and the call to the SCH$CALC_CPU_LOAD call above. ;- .REPEAT 0 ; Code is not used - here for example SCH$CALC_CPU_LOAD:: .CALL_ENTRY LOCK LOCKNAME=SCHED,- SAVIPL=-(SP),- PRESERVE=NO ; Add together the number of kernel threads in wait states MOVAL SCH$GQ_COLPGWQ,R1 ; Collided page wait queue MOVZWL WQH$L_WQCNT(R1),R2 ; Fetch waiting processes/kernel threads MOVAL SCH$GQ_MWAIT,R1 ; Miscellaneous wait queue MOVZWL WQH$L_WQCNT(R1),R0 ; Fetch waiting processes/kernel threads ADDL2 R0,R2 ; Add to total MOVAL SCH$GQ_PFWQ,R1 ; Page fault wait queue MOVZWL WQH$L_WQCNT(R1),R0 ; Fetch waiting processes/kernel threads ADDL2 R0,R2 ; Add to total MOVAL SCH$GQ_FPGWQ,R1 ; Free page wait queue MOVZWL WQH$L_WQCNT(R1),R0 ; Fetch waiting processes/kernel threads ADDL2 R0,R2 ; Add to total ; Add COM processes EVAX_LDQ R5,SCH$GQ_COMQS ; Fetch summary quadword MOVAL SCH$AQ_COMH,R6 ; COM listhead BSBW CHECK_COM ; Number COM processes/kernel threads ADDL2 R0,R2 ; Add to total ; Add COMO processes EVAX_LDQ R5,SCH$GQ_COMOQS ; Fetch summary quadword MOVAL SCH$AQ_COMOH,R6 ; COMO listhead BSBW CHECK_COM ; Number COMO processes/kernel threads ADDL2 R0,R2 ; Add to total UNLOCK LOCKNAME=SCHED,- NEWIPL=(SP)+,- PRESERVE=NO MOVL R2,R0 ; Return total in R0 RET CHECK_COM: .JSB_ENTRY INPUT= CLRL R2 ; Initialize count SUBL3 #PRI$C_NUM_PRI,R31,R3 ; Initialize priority loop 100$: BLBS R5,120$ ; Found an active priority 110$: EVAX_SRL R5,#1,R5 ; Shift summary for next test ADDL #1,R3 ; Increment index BLSS 100$ ; Test next priority MOVL R2,R0 ; Return process/kernel thread count RSB 120$: ADDL3 #64,R3,R22 ; Fetch internal priority MOVAQ (R6)[R22],R7 ; Fetch address of COM queue BSBW COUNT_PROC ; Check for process ADDL2 R0,R2 ; Add number of processes/kernel threads BRB 110$ ; Keep checking priorities COUNT_PROC: .JSB_ENTRY INPUT=R7 CLRL R0 ; Initialize count MOVL R7,R4 ; Copy queue head CMPL (R4),R7 ; End of queue? BEQL 220$ ; yes, return 200$: MOVL (R4),R4 ; Fetch PCB CMPB #DYN$C_PCB,KTB$B_TYPE(R4) ; Only count processes/kernel threads BNEQ 210$ ; INCL R0 ; Increment counter 210$: CMPL (R4),R7 ; End of queue? BNEQ 200$ ; No 220$: RSB .ENDR .END