title shdvr,,02,16-feb-81,bdn ; A dynpri device driver for v7.0 and v7.1 of RSTS/E ; ; copyright (C) 1981 1982 brian nelson ; ; ; edits: ; ; date ver who why ; ; xx-apr-81 01 bdn initial coding ; 15-mar-82 02 bdn Reduce df$lev to 1 ; Check for odd addresses in case we interupted ; 'ttijob' in the middle of job creation. ; Save jobtbl to reduce conflicts with 'ttijob' ; to nill. ; ; brian nelson ; computer services ; university of toledo ; 2801 west bancroft ; toledo, ohio 43606 ; ; (419) 537-2841 ; (419) 537-2511 ; ; This software is furnished in an as-is ; condition, with no committments of sup- ; port or updates. This software may NOT ; be sold for profit nor can it be includ- ; ed in any package to be sold for profit ; without the written consent of the au- ; thor. This software may be used only ; within the above conditions of use. ; ; The information in contained herein is ; subject to change or revision at any ; time without notice. ; ; Addtionally, the author assumes absolutely ; no liablility for whatever might happen to ; your system as a result of using this code. ; It works fine here, but neverless one can ; never tell. Using this may void any DIGITAL ; software support agreements. If it does not ; work, don't call DEC. Put a normal exec in ; and then see if your problem is still there. .sbttl why ? ; This represents an attempt to place the usual DYNPRI code ; in the RSTS executive, thus removing a common source of ; high overhead on our RSTS systems. Traditionally, tasks ; which are used to adjust job priorities run as detached ; jobs, sleeping and waking up at a fixed interval. The case ; here has been to run dynpri as part of an overall system ; control and logging job called SYSMAN (or ...SYS), and to ; do the job table scans once every three (3) seconds. While ; this short of a scan period has high overhead (2% charged) ; it is needed to keep overall response times even. By ; instead placing the code in the exec, and letting the ; executives timeout code call it, we can justify shortening ; the scan time to 1 or 2 seconds, improving total system ; response. ; ; Implemented as a pseudo device driver, one can use the ; write service in the driver to control the priority ; shuffling parameters (scan interval, minimum cpu times, ; and cycle counts for middle priorities). Additionally, the ; modification of executive source (like using RJ2780 ; timeouts or NSP timeouts) is avoided. Since all device ; drivers must have a DDB, this can be used to store all the ; job related data we need between jobtbl scans, like total ; cpu time for each job and scan counts for each job. .sbttl usage of the driver ; starting DYNPRI ; ; To start it, just open the SH0: device. The open service ; routine will clear the tables in the ddb (SHDDDB), set the ; default scan time, minimum cpu time for the first priority ; cut (refered to as LEVEL) and the scan cycle limit for ; movement to the 3 priority (refered to as CYCLE). After ; that the open code (OPN$SH) will stuff the scan time, ; usually 2 or 3 seconds, into the global TIM.SH and then ; exit. The monitor will time out once every second, and ; call any drivers that have had their TIM.devname expire. ; In this case, TMO$SH (the timeout point) will reset TIM.SH ; and then proceed to do the job table scans. Note that it ; is assumed that the monitors timeout module has disabled ; all the usual interupts via a SPL 5. ; ; ; stopping DYNPRI ; ; Simply close the channel on which SH0: is open on. ; ; ; ; overhead ; ; A test program was written that had 63 fake jobs, all ; with the same jdb's and jdb2's, all 'runnable' and ; accumulating no cpu time, thus forcing the 'prior1' ; check code to be executed for each one (see D.HIP in ; TMO$SH). ; 20,000 passes on an 11/70 took 56 seconds, giving the ; time per entire scan of the jobtable of 2.8 millisec- ; onds, an improvment by an order of magnitude over the ; old way of running a detached job with peeks and sys ; calls. ; 20,000 passes on an 11/40 took 208 seconds. ; .sbttl more info ; Changing parameters ; ; Currently, the following parameters can be altered via a ; write request to SH0: ; ; CYCLES is the number of scan periods to wait before ; dropping the priority of a runnable job from -16 to -24. A ; reasonable value is one (1) or two (2). This value is ; tightly linked to the scan interval. ; ; LEVEL is the minimum amount of cpu time (in 1/10 seconds) ; that the job must get before it's priority will be dropped ; from -8 to -16. A resonable value is one (1) or two (2). ; ; SLEEP is the value that is placed in the timeout. Values ; of two or three seconds are reasonable. Anything less than ; that (ie, 1 second) and dynpri may be unable to get enough ; useful information about the jobs on the system to make ; any kind of intelligent priority choices. ; ; The default values are DF$LEV, DF$CYC and DF$SLP. ; ; To change them under program control, try: ; ; 2000 open '_SH0:' as file 1, recordsize 12 ; \field #1, 2% as cycle$, 2% as level$, 2% as slp$, ; 2% as burst1$, 2% as burst2$, 2% as burst3$ ; \lset cycle$ = cvt%$( swap%(1%) ) ; \lset level$ = cvt%$( swap%(2%) ) ; \lset slp$ = cvt%$( swap%(2%) ) ; \lset burst1$= cvt%$( swap%(6%) ) ; \lset burst2$= cvt%$( swap%(5%) ) ; \lset burst3$= cvt%$( swap%(4%) ) ; \put #1, count 12% ; ! values of zero are invalid ; ! the bytecount must be eq 12 bytes .sbttl putting it into RSTS, continued ; Step 1: get the psuedo device into the device tables ; ; create a small prefix file for inclusion in the assembly ; of TBL.MAC . The example here has also the definition for ; the HASPBOX DR11 device driver. ; ; ; the dynpri driver ; ; DEVICE SH ; CNT.SH= 1 ; DDS.SH=640 ; CCC.SH=0 ; ; ; THIS IS THE MODULE INSERTED IN THE TBL ASSEMBLY FOR ; ; THE HASPBOX DMA CHANNEL DEVICE "FE" ; ; ; ; DEVICE FE ; CNT.FE= 1 ; DDS.FE= 524 ; CCC.FE= 1 ; ; ; END OF HSPTBL.MAC and dynpri definition .sbttl link it all up please ; ; assemble driver, create libr for it to link it into RSTS ; module but outside of perm mapped memory to minimize the ; loss of small buffers ; For 7.1, you have to put it there in order to use i/d ; space support. ; ; ; ; R MACRO.SAV ; SHDVR=IN:$COMMON,IN:$KERNEL,SY:[1,8]SHDVR ; ^Z ; ;! assume rk05 input ; ; R LIBR.SAV ; IN:$SH=SHDVR ; IN:$FE=IN:$FEDVR ; ^Z ; ; R MACRO.SAV ; TTDVR,TTDVR/C=IN:$COMMON,$KERNEL,DK:CONFIG,IN:$CHECK,$KBDEF,$TTDVR ; ^Z ; R MACRO.SAV ; TTDINT,TTDINT/C=IN:$COMMON,$KERNEL,DK:CONFIG,IN:$CHECK,$KBDEF,$TTDINT ; ^Z ; R MACRO.SAV ; TBL,TBL/C=IN:$COMMON,$KERNEL,DK:CONFIG,IN:$CHECK,$XTBL,$TBL ; ^Z ; R LINK.SAV ; RSTS/Z,RSTS/A/W,RSTS=TBL,$ERR.STB/X/B:0/U:#1000/I/C ; TTDINT/C ; IN:$RSTS/C ; IN:$FE,IN:$SH ; MORBUF ; SYDLRG ; BUFEXT ; LPDVRX ; FEDVR ; SHDVR ; ; ^Z ; R LINK.SAV ; DSK/Z,DSK/A/W,DSK=IN:$DSK,DK:RSTS.STB/X/B:#117000/U:#100/I/C ; IN:$RSTS ; DSKPAT ; DKSEEK ; DBSEEK ; ; ^Z ; R LINK.SAV ; TER/Z,TER/A/W,TER=IN:$TER,DK:RSTS.STB/X/B:#117000/U:#100/I/C ; TTDVR/C ; IN:$RSTS ; LFTOVR ; TTSYST ; ; ^Z ; R LINK.SAV ; CRA/Z,CRA/A/W,CRA=IN:$CRA,DK:RSTS.STB/X/B:#117000/U:#100 ; CRAPAT ; ^Z ; R LINK.SAV ; GEN/Z,GEN/A/W,GEN=IN:$GEN,DK:RSTS.STB/X/B:#117000/U:#100/I/C ; IN:$RSTS ; GENPAT ; EMU ; ; ^Z ; R LINK.SAV ; EMT/Z,EMT/A/W,EMT=IN:$EMT,DK:RSTS.STB/X/B:#117000/U:#100/I/C ; IN:$RSTS ; EMTPAT ; LIB ; ; ^Z ; R LINK.SAV ; FIP/Z,FIP/A/W,FIP=IN:$FIPLRG,DK:CRA.STB/X/B:#137000/U:#100/I/C ; IN:$RSTS ; FIPPAT ; ; ^Z ; R LINK.SAV ; FMS/Z,FMS/A/W,FMS=IN:$FMS,DK:TER.STB/X/B:#117000/U:#100/C ; IN:$RSTS ; FMSPAT ; ^Z ; R LINK.SAV ; OVR/Z,OVR/A/W,OVR=IN:$OVRLRG,DK:FIP.STB/X/B:#1000/Q/C ; IN:$RSTS ; TBLEXT:3000 ; ; ^Z ; R SILUS.SAV ; SY0:[0,1]7170A.SIL,TT:=RSTS/C ; CRA/M/C/A ; FMS/M/C ; EMT/M/C ; GEN/M/C ; TER/M/C ; DSK/M/C ; FIP/M/C ; OVR/M/C/A ; IN:$DEFALT/A ; ; .sbttl the ddb defined deforg shdvr .dsect ddidx: .blkb ; driver index ddsts: .blkb ; status and access control byte ddjbno: .blkb ; owner job number times 2 (0 if free) ddunt: .blkb ; device unit number ddtime: .blkw ; time assigned or inited ddcnt: .blkw ; init count and assignment control ddflag: .blkw ; device flags ddlev: .blkw ; cpu time level word ddcyc: .blkw ddslp: .blkw ; time to wait between scans ddbur1: .blkw ; burst for level 1 priority ddbur2: .blkw ; burst for level 2 priority ddbur3: .blkw ; burst for level 3 priority ddpri: .blkw ; save the job's priority state: .blkw elptim: .blkw ; j2cpu-timevector ddcycv: .blkw 64. ; cycle count vector for all jobs ddtimv: .blkw 64. ; lsw of total job time for all jobs ddjobt: .blkw 64. ; save old jobtbl here .blkw 1 ; reserved ddend = . sts.sh == 0/400 ; no special flags for us flg.sh == ddnfs!flgmod!376 siz.sh == 5*14.+1 ; no width please buf.sh == 8. ; buffer size (for parameter control) df$cyc = 1 ; default for minimum cycles df$lev = 1 ; default for minimum cpu time df$slp = 2 ; default for scan sleep time prior1 = -8. ; normal job priority prior2 = -16. ; and then some prior3 = -24. ; even lower still prior4 = -32. ; the pits burst1 = 6. ; default runburst for prior1 burst2 = 5. ; default runburst for prior2 burst3 = 4. ; default runburst for prior3 burst4 = 3. ; default runburst for prior4 waitb = 32766. ; mask for jbwait global .sbttl interupts (?????) tmporg shdint callx intsav ,r5 .word 1200 .word int unorg .sbttl special function service spc$sh::error prviol ; no special functions allowed int: return ; bye .sbttl assign and deassign us asn$sh::return ; does nothing at all dea$sh::clr tim.sh ; get rid of timeouts return ; bye .sbttl error logging ??? erl$sh::return ; nothing to do .sbttl open the psuedo device up please ; clear parameters, start timeouts ; ; r0 = unit number times 2 (always 0) ; r1 > ddb/fcb ; r5 > ddb/fcb (must always save this one) opn$sh::mov #ddcyc ,r2 ; first, clear out accumulated mov #ddend ,r3 ; cycle count and total jobtime add r1 ,r2 ; point to the cycles vector add r1 ,r3 ; point to the end of the DDB 10$: clr (r2)+ ; clear a word from ddb cmp r2 ,r3 ; reach the end of it all ? blo 10$ ; no mov r1 ,r2 ; stuff the ddb address in add #ddlev ,r2 ; point to the area mov #df$lev ,(r2)+ ; set default cputime minimum mov #df$cyc ,(r2)+ ; set default cycle count. mov #df$slp ,(r2)+ ; default sleep mov #burst1 ,(r2)+ ; burst for level 1 mov #burst2 ,(r2)+ ; burst for level 2 mov #burst3 ,(r2)+ ; burst for level 3 mov #df$slp ,tim.sh ; enable once a second timeouts return ; thats all we need to do here cls$sh::clr tim.sh ; close service, disable timeoutt return ; so simple .assume ddcyc eq .assume ddslp eq .assume ddbur1 eq .assume ddbur2 eq .assume ddbur3 eq .sbttl read/write service ; ser$sh ; ; All we want to do here is to allow writes to set the ; various parameters that control DYNPRI, such as: ; ; 1. DDCYC number of cycles at a priority before we kill ; the job a little bit more ; 2. DDLEV minimum cpu time to get before we go down to ; a priority of -16 from -8 (assumed job default) ; 3. DDSLP wait time between scans (put into TIM.SH) ; ; 4. DDBUR1 runburst for priority -8 ; 5. DDBUR2 runburst for priority -16 ; 6. DDBUR3 runburst for lower priorities ; ; ; Errors: BADCNT odd bytecount or bytecount <> 12 dec ; DATERR zero value for CYC,LEV or SLP ; ; ; r0 = unit number times 2 ; r1 > ddb/fcb ; r2 = 2 for .read, 4 for .write ; r3 > users xrb ; r4 = calling job number times two ; r5 > users buffer ser$sh::clr xrblk(r3) ; indicate seq device cmpb r2 ,#.read ; read or write this time beq s.read ; a read .sbttl write service s.put: cmp xrbc(r3),#6*2 ; number of things to put ;- bne 10$ ; oops ;- bit #1 ,xrbc(r3) ; odd byte count ? beq 20$ ; ok 10$: error badcnt ; no, flag illegal byte count return ; and exit 20$: mov (r5)+ ,r2 ; copy the first word over ble 100$ ; which is DDCYC (<>0) mov r2 ,ddcyc(r1) ; and stuff it in please mov (r5)+ ,r2 ; get the next one now ble 100$ ; which is DDLEV (<>0) mov r2 ,ddlev(r1) ; ok, so copy it in please mov (r5)+ ,r2 ; next thing is the sleep time ble 100$ ; oops mov r2 ,ddslp(r1) ; ok, the next timeout gets it. mov (r5)+ ,r2 ; next is burst for high prior. ble 30$ ; if zero, ignore it mov r2 ,ddbur1(r1) ; nonzero, stuff it in please. 30$: mov (r5)+ ,r2 ; next is burst for middle prior ble 40$ ; if zero, skip this one mov r2 ,ddbur2(r1) ; > 0, so stuff it in please 40$: mov (r5)+ ,r2 ; lowest priority now ble 50$ ; nope mov r2 ,ddbur3(r1) ; yep 50$: jmpx ioexit ; all done bye now 100$: error daterr ; flag an error please return ; and exit s.read: jmpx ioexit ; nothing to do .sbttl timeouts ; tmo$sh ; ; r0 = unit number times 2 (always 0) ; r1 > ddb/fcb rot = 250. ; patchable, 'rot' cycle count tmo$sh::mov ddslp(r1),tim.sh ; reset the timeout please mov r0 ,-(sp) ; save a few registers mov r2 ,-(sp) ; mov r3 ,-(sp) ; mov r4 ,-(sp) mov r5 ,-(sp) ; clr r5 ; r5 is the current job number 10$: add #2 ,r5 ; next job please mov jobtbl(r5),r2 ; point to the next job beq 10$ ; no job of this number active cmp r2 ,#-1 ; is this the end of jobtbl ? beq 20$ ; yes, so exit back to RSTS mov r1 ,r3 ; check to see if this job is the add r5 ,r3 ; same as one as last time to avoid cmp ddjobt(r3),r2 ; potential confict with 'ttijob' bne 15$ ; job creation in ttdvr. skip if ne call dyn ; no, check this job out please 15$: mov r1 ,r3 ; save the jobs jdb address for the add r5 ,r3 ; next time thru mov r2 ,ddjobt(r3) ; save jdb address br 10$ ; next job please 20$: mov (sp)+ ,r5 ; pop the registers now please mov (sp)+ ,r4 ; mov (sp)+ ,r3 ; mov (sp)+ ,r2 ; mov (sp)+ ,r0 ; return ; for now .sbttl check out the current job (r2 > jdb) ; register usage ; ; r1 > ddb/fcb ; r2 > jdb ; r3 > jdb2 ; r4 > ddb + jobnumbertimes2 ; r5 = job number dyn: bit #jfnopr ,jdflg(r2) ; job not yet logged in ? bne 100$ ; no, could have been 'ttijob' mov jdjdb2(r2),r3 ; address of job's JDB2 block mov r1 ,r4 ; point to the ddb offset by add r5 ,r4 ; the job number times two. bit #1 ,r3 ; avoid crash if 'ttijob' was bne 100$ ; interupted in the middle. mov j2cpu(r3),r0 ; compute elasped cpu time. sub ddtimv(r4),r0 ; subtract off time last used. mov r0 ,elptim(r1) ; save elapsed cpu time now. clr r0 ; r3 now has pointer to jdb2 bisb jdpri(r2),r0 ; the job's current priority cmp r0 ,#128. ; would like this as a signed blt 10$ ; integer please sub #256. ,r0 ; make it into -128....128 10$: mov r0 ,ddpri(r1) ; we may need this later bge 100$ ; > -8 priority, so do nothing cmp r0 ,#prior4 ; at the lowest priority we blt 100$ ; are looking at today ? bit #4 ,r0 ; job has special run priority? bne 100$ ; yep bit #jfcc!jf2cc!jfspcl,jdflg(r2) ; a special condition exist beq 20$ ; no jmp d.rst ; yes, restore job to -08 prior 20$: clr r0 ; next figure out if job might bit jbstat(r5),jbwait(r5) ; be stalled or something. bne 30$ ; stalled com r0 ; flag as being so 30$: bit #waitb ,jbwait(r5) ; stalled, but for what reason? bne 40$ ; stalled for non disk waits. clr r0 ; 40$: mov r0 ,state(r1) ; save the 'job stalled' flag br d.chek ; ok, set new priority up please 100$: return global .sbttl actually do the changes now. ; r1 > ddb / fcb ; r2 > jdb ; r3 > jdb2 d.chek: inc ddcycv(r4) ; at last mov ddpri(r1),r0 ; get the current job priority sub #prior4 ,r0 ; change -32...-8 to 0..24 ash #-3 ,r0 ; /8 to 0...3 dec r0 ; to -1...2 dec r0 ; to -2...1 bgt d.hip ; job is at the highest prior tst state(r1) ; at a lower priority, is the bne d.rst ; stalled (but not for FIP) tst r0 ; again, 0 if priority eq -16 beq d.midp ; blt d.lowp ; again, lt 0 if prior lt -16 d.hip: clr ddcycv(r4) ; clear out job's cycle count tst state(r1) ; is the job stalled ? bne skipjb ; yes, skip this one then. cmp elptim(r1),ddlev(r1) ; did they get the minimum time ble skipjb ; no, skip the job then mov #prior2 ,-(sp) ; yes, drop the job's priority mov ddbur2(r1),r0 ; and set the new run burst br d.chan ; do it then d.midp: cmp ddcycv(r4),ddcyc(r1) ; lower priority, have we made ble skipjb ; a reasonable (?) # of passes? mov #prior3 ,-(sp) ; perhaps, so drop the priority mov ddbur3(r1),r0 ; some more and set run burst. br d.chan ; and change it ! d.lowp: mov #rot ,r0 ; try to avoid constant bubbling cmp #prior4 ,ddpri(r1) ; for very long cpu bound jobs. beq 20$ ; priority is currently -32 ? bit #jfpriv ,jdflg(r2) ; does this job have perm privs? bne 10$ ; yep, do not drop to the bottom mov #prior4 ,-(sp) ; no, drop to the bottom prior. clr r0 ; no change for the run burst. br d.chan ; and change it 10$: mov #32766. ,r0 ; priv user at -24 priority 20$: tst elptim(r1) ; no cpu time increment yet ? bne skipjb ; yes, do not bubble up 1 level. mov ddpri(r1),-(sp) ; no, so set new prior eq old cmp ddcycv(r4),r0 ; ok, how long at lowest prior? bhis 30$ ; too long add #8. ,(sp) ; priority plus 8 30$: clr r0 ; no change in run burst though. br d.chan ; and do it d.rst: mov #prior1 ,-(sp) ; restore priority to sys def. mov ddbur1(r1),r0 ; same for the job's run burst. clr ddcycv(r4) ; and clear out cycle count. d.chan: tst r0 ; change burst. if zero, leave ble 10$ ; it alone. movb r0 ,jdbrst(r2) ; no, stuff new run burst in. 10$: bic #^C3 ,ddpri(r1) ; mask off exec's priority bits bisb ddpri(r1),(sp) ; and stuff those bits in. movb (sp)+ ,jdpri(r2) ; al last......... .br skipjb skipjb: mov j2cpu(r3),ddtimv(r4) ; save the current cpu time return ; at last .end