.title PRIMGR V1.1 - Interactive Priority Monitor/Manager .ident /PRIMGR V1.1/ .sbttl Description ;++ ; ; Program: Priority Monitor V1.1 ; Author: Don Stokes, GP Print Limited, Wellington, NZ ; don@gp.co.nz ; Date: 15-Aug-1989 ; ; Description: ; The purpose of the program is to maintain response for interactive ; jobs, while ensuring that heavy processing does not interfere with ; production processing. To achieve this, light processing in local ; event flag wait states are elevated in priority above the normal base. ; After a small amount of processing time has been used, the process is ; reduced to normal interactive priority. This means that a process ; using very little CPU gets the processor for as much time as it needs ; ahead of heavier processing. ; ; Processes which are CPU bound, ie are doing very little I/O while ; remaining computable are reduced to priority 3 to share the cpu with ; batch jobs. ; ; The algorithm used is as follows: (prib = current base priority, ; deltacpu = cpu time used since last pass, state = scheduler state, ; com_passes = no of passes since process was not computable, ; cpulim = cpu threshold, low_passes = passes since change to ; priority 3, low_pass_min = minimum pri. 3 passes) ; ; low_passes >= low_pass_min state = wait ; deltacpu < cpulim34 deltacpu < cpulim45 ; --> --> ; Priority 3 Priority 4 Priority 5 ; <-- <-- ; com_passes >= com_limit deltacpu > cpulim45 ; deltacpu < cpulim34 ; ; Note that priorities only move one step at at time. Processes ; executing at abnormal priorities (<3 or >5) are ignored. ; ; Only interactive processes are affected. ; ;-- .sbttl Modification history ;++ ; ; Modifications: ;_When_____Who__Version_Description_____________________________________________ ; 26/10/89/dcs V1.1 Added low-passes limit to prevent oscillations. ; Previously, if two processes were computable, one ; would tend to execute at priority 4, locking the ; other out at priority 3. On the next pass, because ; the second had no CPU it would be raised to priority ; 4, while the other went to 3. The fix meant that ; a priority has to spend low_pass_min (currently 4) ; passes at priority 3 before being eligible for raising ; to priority 4 (unless it is in a wait state). ; com_limit has been raised to prevent processes from ; going to priority 3 too readily, as this mod will ; result in a longer priority 3 burst for processes ; "accidentally lowered. ; Also fixed minor bug where processes with 0 in the ; MASTER_PID field were processed incorrectly (but ; harmlessly - appeared as funnies when PRIMGR is run ; interactively with debugging turned on) SWAPPER and ; NULL exhibit this trait. ; ;-- .sbttl Assembly time parameters pass_time = 5 ; 5 seconds between passes cpu_limit_34 = 10 ; 2% cpu threshold for priority 3 <-> 4 transition cpu_limit_45 = 50 ; 10% cpu threshold for priority 4 <-> 5 transition com_limit = 6 ; No. consecutive COM states seen to drop priority to 3 low_pass_min = 6 ; No. of passes pri. 3 process must stay at priority 3 log_changes = 0 ; Set to one to log each priority change debug = 0 ; Set to one to display debugging info disable_change = 0 ; Set to one to disable changing of priority .sbttl Macros .library /SYS$LIBRARY:LIB/ ; $STATEDEF lives here. $SSDEF $JPIDEF $DSCDEF $STATEDEF $FSCNDEF $OPCDEF ; ; STATUS macro - do status check on specified condition code, beanch to ; error report/restart routine if error detected. ; eg: STATUS r0 ; .macro status condition, ?L1 blbs condition, L1 movl condition, R7 brw crash L1: .endm status ; ; FATAL macro - do status check on specified condition code, abort if ; error detected. Used in error report routine. ; eg: FATAL r0 ; .macro fatal condition, ?L1 blbs condition, L1 $EXIT_S condition L1: .endm fatal ; ; CALL macro - call a subroutine, pass parameters on stack ; eg: CALL LIB$SET_SYMBOL, sym_filename, filename_d ; .macro call routine, - p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11, - p12,p13,p14,p15,p16,p17,p18,p19,p20 .narg call.argc call.argc = call.argc - 1 call.argn = 20 .irp call.argv, .if less_equal call.argn - call.argc pushal call.argv .endc call.argn = call.argn - 1 .endr calls #call.argc, G^routine .endm ; ; ITEM macro - create itemlist entry. ; eg ITEM item, code, buflen, address ; .macro item code, buflen, address, lenadr .word buflen .word code .long address .if b lenadr .long 0 .endc .if nb lenadr .long lenadr .endc .endm ; ; DESC macro - create string descriptor ; eg DESCRIPTOR string length ; .macro descriptor addr, len .word len .byte DSC$K_DTYPE_T .byte DSC$K_CLASS_S .long addr .endm .sbttl Data .psect data,wrt,noexe,noshr ; ; Offsets to elements within old process array ; oldprc_pid = 0 ; long oldprc_cputim = 4 ; long oldprc_coms = 8 ; long oldprc_low_pass = 12 ; long oldprc_size = 16 ; Size of old process element - must be power of 2 oldprc_shift = 4 ; 2^oldprc_shift = oldprc_size oldprcbuff: .blkb oldprc_size * 1024 ; ; $GETJPI item lists and buffers ; pid: .long -1 item_list: item JPI$_PROC_INDEX, 4, jpi_index item JPI$_PID, 4, jpi_pid item JPI$_PRIB, 4, jpi_prib item JPI$_MODE, 4, jpi_mode item JPI$_STATE, 4, jpi_state item JPI$_CPUTIM, 4, jpi_cputim item JPI$_MASTER_PID, 4, jpi_master_pid .if ne log_changes item JPI$_USERNAME, 12, jpi_username item JPI$_IMAGNAME, 255, jpi_imagname, jpi_imagname_d .endc item 0,0,0 item_list_1: item JPI$_MODE, 4, jpi_mode item 0,0,0 jpi_index: .blkl jpi_pid: .blkl jpi_prib: .blkl jpi_mode: .blkl jpi_state: .blkl jpi_cputim: .blkl jpi_master_pid: .blkl process_state: .blkb iosb: .blkw 4 ; ; Other storage ; delta_cpu: .blkl state_table: .byte 0[16] computable = 1 wait = 2 time_fao: .ascid |0 00:00:!2ZL.00| time_buff: descriptor 1$, 13 1$: .blkb 13 wait_time: .blkq ; ; Data for error logging routine ; getmsg_text: descriptor 1$, 80 1$: .blkb 80 sndopr_fao: .ascid |%PRIMGR-W-ERROR, Priority manager reported error, | - |continuing !/!AS| sndopr_text_d: descriptor sndopr_text, 160 sndopr_msg: descriptor 1$, 168 1$: .byte OPC$_RQ_RQST .word OPC$M_NM_CENTRL .byte 0 .long 0 sndopr_text: .blkb 160 cr_time_buff: .ascid |0 00:00:30.00| cr_wait_time: .blkq ; ; Data for logging routine if used ; .if ne log_changes setpri_fao: .ascid |!23AS !12AS PID: !XL !16AS Pri: !1UL --> !1UL| setpri_out: descriptor 1$, 80 1$: .blkb 80 time_string: descriptor 1$, 23 1$: .blkb 23 image_string: descriptor 1$, 16 1$: .blkb 16 jpi_username: .blkb 12 jpi_username_d: descriptor jpi_username, 12 jpi_imagname: .blkb 255 jpi_imagname_d: descriptor jpi_imagname, 255 filescan_item: .blkw .word FSCN$_NAME .blkl .long 0 .endc ; ; Data for debug routines if used ; .if ne debug info_fao: .ascid |!12AS !XL !XL !5UL| info_out: descriptor 1$, 36 1$: .blkb 36 oddprimsg: .ascid |Strange interactive priority| newprocmsg: .ascid |New process logged| endpassmsg: .ascid |End of pass| .endc .sbttl Code .psect code,nowrt,exe,shr .entry primgr, ^M<> ; ; Set up states table. Values are initialised at assembly time to zero, ; so just set the values for a "computable" job and "waiting" job. ; Processes in the LEF, CEF and HIB states are considered "wait" states, worth ; giving a priority boost to get them underway when they become computable. ; Processes in the CUR, COM and PFW states are considered as processing, and ; are candidates for priority reduction. ; restart: movb #wait, state_table+SCH$C_LEF movb #wait, state_table+SCH$C_HIB movb #wait, state_table+SCH$C_CEF movb #computable, state_table+SCH$C_CUR movb #computable, state_table+SCH$C_COM movb #computable, state_table+SCH$C_PFW ; ; Convert time value in seconds to text time format ; Convert text time to binary time ; $FAO_S ctrstr=time_fao, outbuf=time_buff, - p1=#pass_time status R0 $BINTIM_S timbuf=time_buff, timadr=wait_time status R0 ; ; Schedule regular wakeups for $HIBER call at bottom of outer loop. ; $SCHDWK_S daytim=wait_time, reptim=wait_time status R0 ; ; Top of outer loop: Move -1 to pid value, so that a new wildcard operation ; is started each time. ; pass_loop: movl #-1, pid ; ; Get process information ; Finish pass if status is SS$_NOMOREPROC ; Try again if suspended. ; getjpi_loop: $GETJPIW_S pidadr=pid, itmlst=item_list, iosb=iosb cmpl R0, #SS$_SUSPENDED beql getjpi_loop cmpl R0, #SS$_NOMOREPROC bneq 1$ brw end_of_pass 1$: status R0 status iosb ; ; If the pid and master pid don't match (or master pid = 0 for odd-balls ; like NULL & SWAPPER), then this is a subprocess, so extract the mode ; of the master process, as SPAWN/LIB$SPAWN creates non-interactive ; processes if the input is other than the keyboard. ; cmpl jpi_pid, jpi_master_pid beql 6$ cmpl jpi_master_pid, #0 beql 6$ $GETJPIW_S pidadr=jpi_master_pid, itmlst=item_list_1, iosb=iosb cmpl R0, #SS$_SUSPENDED beql 7$ status R0 status iosb 6$: ; ; If this is not an interactive process, then don't process it. ; cmpl jpi_mode, #JPI$K_INTERACTIVE beql 8$ 7$: brw getjpi_loop 8$: ; ; Debug code - output useful info to screen ; .if ne debug $FAO_S ctrstr=info_fao, outbuf=info_out, - p1=#jpi_username_d,p2=jpi_pid, - p3=jpi_index,p4=jpi_prib status R0 call LIB$PUT_OUTPUT, info_out status R0 .endc ; ; Check if process state is computable, waiting, or irrelevant. ; movl jpi_state, R5 movb state_table(R5), process_state ; ; Calculate base address in R4 for the old processes table entry specified ; by index. ; ashl #oldprc_shift, jpi_index, R4 addl2 #oldprcbuff, R4 ; ; Check if the PID for this process matches the corresponding entry in ; the old processes table. If not, then the process is a new process, so ; copy data to the array element. ; cmpl jpi_pid, oldprc_pid(R4) beql 2$ .if ne debug call LIB$PUT_OUTPUT, newprocmsg status R0 .endc movl #0, oldprc_coms(R4) brw end_getjpi 2$: ; ; If process is computable, then increment computables count in preparation ; for a possible move to priority 3. Otherwise, zero the counter for this ; process. ; cmpb process_state, #computable beql 3$ movl #0, oldprc_coms(R4) brb 4$ 3$: incl oldprc_coms(R4) 4$: ; ; Calculate delta value for CPU count. ; subl3 oldprc_cputim(R4), jpi_cputim, delta_cpu ; ; Branch to appropriate processing routine for priority 3, 4 or 5. ; If process is not one of these, then drop thru. ; casel jpi_prib, #3, #2 5$: .word check_pri_3 - 5$ .word check_pri_4 - 5$ .word check_pri_5 - 5$ ; ; We drop through to here if the priority is abnormal, ie < 3 or > 5. ; .if ne debug call LIB$PUT_OUTPUT, oddprimsg status R0 .endc brw end_getjpi ; ; Priority 3: Change to priority 4 if cpu usage is below threshold. ; and job has been at priority 3 for more than low_pass_min passes. ; Only check if process is still not waiting. ; check_pri_3: cmpb process_state, #wait beql 1$ incl oldprc_low_pass(R4) cmpl oldprc_low_pass(R4), #low_pass_min blss 2$ 1$: cmpl delta_cpu, #cpu_limit_34 bgtr 2$ movl #4, R6 brw set_priority 2$: brw end_getjpi ; ; Priority 4: If in a local event flag wait (ie waiting for I/O) ; and delta CPU is less than cpu_limit then crank process priority up to 5. ; Otherwise check if a drop is in order... ; check_pri_4: cmpb process_state, #wait bneq 1$ cmpl delta_cpu, #cpu_limit_45 bgtr 1$ movl #5, R6 brw set_priority 1$: ; ; If this process has been computable for the last passes, ; and has used some CPU, then drop the priority down to 3. ; cmpl oldprc_coms(R4), #com_limit blss 2$ cmpl delta_cpu, #cpu_limit_34 blss 2$ movl #3, R6 movl #0, oldprc_low_pass(R4) brw set_priority 2$: brw end_getjpi ; ; Priority 5: If delta CPU is greated than the threshold, then drop priority ; back to 4. ; check_pri_5: cmpl delta_cpu, #cpu_limit_45 blss 1$ movl #4, R6 brw set_priority 1$: brw end_getjpi ; ; Change the process priority. This is only used if a priority change ; is required. ; New priority is in R6. ; set_priority: .if eq disable_change $SETPRI_S pidadr=jpi_pid, pri=R6 status R0 .endc .if ne log_changes $FILESCAN_S srcstr=jpi_imagname_d, - valuelst=filescan_item status R0 $ASCTIM_S timbuf=time_string status R0 $FAO_S ctrstr=setpri_fao, outbuf=setpri_out, - p1=#time_string, p2=#jpi_username_d, - p3=jpi_pid, p4=#filescan_item, - p5=jpi_prib, p6=R6 status R0 call LIB$PUT_OUTPUT, setpri_out status R0 .endc ; ; Save JPI info for next pass. (Come here if new process) ; end_getjpi: movl jpi_pid, oldprc_pid(R4) movl jpi_cputim, oldprc_cputim(R4) brw getjpi_loop ; ; Bottom of main loop. Come here after all $GETJPI passes have completed. ; Go to sleep now, and hope that we get woken up by the $SCHDWK we did all ; those milliseconds ago... ; end_of_pass: .if ne debug call LIB$PUT_OUTPUT, endpassmsg status R0 .endc $HIBER_S status R0 brw pass_loop ; ; Come here on a fatal error. Report the error to the operator, wait a bit, ; and restart. Error code is passed in R7 (placed there by the STATUS macro). ; Get text format of crash message. ; Format it and and send it to the operator. ; Cancel the regular wakeup, and wait for 30 secs. ; Restart. ; crash: movw #80, getmsg_text movw #160, sndopr_text_d $GETMSG_S msgid=R7, msglen=getmsg_text, bufadr=getmsg_text fatal R0 $FAO_S ctrstr=sndopr_fao, outlen=sndopr_text_d, - outbuf=sndopr_text_d, p1=#getmsg_text fatal R0 addw3 sndopr_text_d, #8, sndopr_msg $SNDOPR_S msgbuf=sndopr_msg fatal R0 $CANWAK_S fatal R0 $BINTIM_S timbuf=cr_time_buff, timadr=cr_wait_time fatal R0 $SCHDWK_S daytim=cr_wait_time fatal R0 $HIBER_S fatal R0 brw restart .end PRIMGR