# /* * Optimized RK-11/RK03/RK05/disk driver * * Copyright (c) 1975, the Children's Museum. * This program is proprietary. Permission is hereby * granted to use this program and associated documentation * by any UNIX licensee in accordance with the provisions of * any UNIX licensing agreement. Other uses of * of this program, or distribution of this program * in violation of the provisions of the UNIX license, * must be approved by the Children's Museum in writing. * * Inquiries, bug notices, etc, should be addressed to * Computer Center * The Children's Museum * jamaicaway * Boston, MA 02130 * (617) 522-4800 x25 * Authors: Bill Mayhew and Brent Byer, September 1975 * * See "rkvolmap" for the mapping of rk "volumes" onto drives */ #include "../defines.h" #include "../param.h" #include "../lnode.h" #include "../systm.h" #include "../buf.h" #include "../conf.h" #include "../user.h" #define NO_SEEK /* disable seeks if only one drive */ /*#define RKSTATS /* collect statistics */ #define SWEEP /* enable directional optimisation */ #define ERR_FLOOR 1 /* errors allowed before report */ #define TEMP_CUT_OUT /* sensitive drive temperature cut-out ! */ #define NRK 2 /* number of RK05 volumes on system */ #define NDRV 1 /* number of drives on system */ #define INTLV 1 /* magic interleaving number */ /* This is dependant on the time to service an rk interrupt and start i/o on the next sector. * Time/sector is 3.3*10(-3) secs. for 1500 rpm., and 2*10(-3) secs. for 2500 rpm. * INTLV > (max. interrupt latency)/(time/sector). */ #define RKADDR 0177400 /* base address of RK11 control registers */ #define NRKSEC 12 /* 12 sectors per track */ #define NTRACK 2 /* 2 tracks per cylinder */ #define NRKCYLS 203 /* 203 cylinders per volume */ #define NRKBLK 4872 /* 4872 blocks per volume */ #define NHRKBLK 2435 /* offset in map algorithm */ #define NRKSKIP 10 /* number of times io can be preempted */ /* control register bits */ #define CRESET 0 /* control reset */ #define GO 01 #define SEEK 010 #define DRESET 014 /* drive reset */ #define IENABLE 0100 /* interrupt enable */ #define CTLRDY 0200 /* control ready */ #define SEEKCMP 020000 /* seek complete */ #define HARDERR 040000 /* hard error */ /* drive status bits */ #define RWS 0100 /* on cylinder */ #define DRY 0200 /* drive ready */ #define SIN 01000 /* seek incomplete */ /* error bits */ #define NXD 0200 /* non-existent drive */ #define DATALT 01000 /* data late */ #define SEEKERR 010000 /* seek error */ #define DRE 0100000 /* drive error */ #ifdef POWER_FAIL | TEMP_CUT_OUT /* retry delays */ #define PUDELAY (25*HZ) /* drive power up time */ #define PURETRY (1*HZ) /* power up test retry delay */ #endif #define B_SEEK 02000 /* seeking flag for buffer header */ struct devtab rktab; struct { int rkds; int rker; int rkcs; int rkwc; int rkba; int rkda; }; #ifndef RAW_BUFFER_POOL struct buf rrkbuf; #endif /* * structure of an RK disk queue; one queue per drive. */ struct rkq { struct buf *rk_bufp; /* pointer to first buffer in queue */ #ifdef SWEEP int rk_lcyl; /* last cylinder accessed on this drive */ char rk_dirf; /* direction of head motion */ #endif #ifdef RKSTATS char rk_nreq; /* number of requests in queue */ char rk_rmax; /* high water mark for q */ unsigned rk_cyls[NRKCYLS]; /* cylinder usage count */ #endif int rk_errcnt; /* error count */ } rk_q[ NDRV ]; struct rkq *rk_ap; /* pointer to queue of drive currently doing I/O */ struct rkq *rkvolmap[NRK] { &rk_q[0] /* PERTEC drive 1 fixed */ ,&rk_q[0] /* PERTEC drive 1 removable */ }; #ifdef ERR_FLOOR int rkreport; /* floor for error reports */ #endif #ifndef ERR_FLOOR #define rkreport 0 #endif /* * Use b_scratch in the buffer header to save cylinder address; * b_sector to save sector within cylinder; av_back to save * disk address. */ #define rkcyl b_scratch /* must be int */ #define rksec b_sector /* can be char */ #define rkaddr av_back /* * Rkstrategy() does all the block mapping for * the two varieties of RK formats supported by this driver. * The differing formats are distinguished by the minor device * number used in referencing the disk: * * minor device 0-7: "traditional" straightforward RK format. * minor device 010-017: new optimized split-up disk. In this * format, block 0 is in its standard place so that * boot programs can be put there; blocks 1 through * NHRKBLK (2435) are located beginning at block #2436, * all remaining blocks are between block 1 & 2435. the * effect of this mapping is to centralize disk head motion * about the center of the disk. * the optimization is ideal for those RK's * which serve as both root device and swap device. It * is less than ideal, although probably still an * improvement over traditional form, for RK's used * exclusively as mounted file systems. */ rkstrategy(bp) register struct buf *bp; { register unsigned p1, p2; int f; p2 = bp->b_blkno; p1 = bp->b_dev.d_minor; /* * check for validity of this request */ if ( ((p1&07) >= NRK) || (p2 >= NRKBLK) ) { bad: bp->b_flags =| B_ERROR; iodone(bp); return; } /* * Here, we do the mapping for the various formats: */ if ( (p1 & 010) && p2 && ((p2 =+ NHRKBLK) >= NRKBLK) ) p2 =- NRKBLK - 1; /* * if raw I/O, check within physical limits */ if ( bp->b_flags & B_PHYS ) { if ( p2+((-bp->b_wcount)/256) > NRKBLK ) goto bad; # ifdef _1170 mapalloc( bp ); # endif } p1 =& 07; bp->b_error = NRKSKIP; /* * b_error counts the number of times * this io has been skipped in the queue, * when it becomes 0 it cannot be ignored * anymore. NRKSKIP should be set to the * average size of contiguous command files * eg. in bin... */ /* * calculate cylinder, sector, drive and surface for * this request and save same. */ bp->rksec = p2 % NRKSEC; p2 =/ NRKSEC; bp->rkaddr = (p1 << 13) | (p2 << 4) | bp->rksec; bp->rkcyl = p2 >> 1; p2 = rkvolmap[p1]; #ifdef RKSTATS p2->rk_cyls[bp->rkcyl]++; #endif spl5(); #ifdef RKSTATS p2->rk_nreq++; #endif if ((p1 = p2->rk_bufp)==NULL) { /* queue was empty */ p2->rk_bufp = bp; bp->av_forw = 0; if ((rk_ap == NULL) && (RKADDR->rkcs & CTLRDY)) rkstart(); } else { /* non-empty; determine where to place this request * in the queue, taking into account current * direction of head motion and minimization of * head movement. */ #ifdef SWEEP f = p2->rk_dirf; #endif p2 = p1->av_forw; while ( p2 ) { /* skip any blocks overtaken too often */ if ( !p2->b_error ) p1 = p2; p2 = p2->av_forw; } for (; p2 = p1->av_forw; p1 = p2) if ( p1->rkcyl <= bp->rkcyl && bp->rkcyl <= p2->rkcyl || p1->rkcyl >= bp->rkcyl && bp->rkcyl >= p2->rkcyl ) { while (bp->rkcyl==p2->rkcyl) { /* * if a cylinder match is found, * do rotational optimization. */ if (p2->rksec > p1->rksec) { if(bp->rksec > p1->rksec +INTLV &&bp->rksec < p2 ->rksec-INTLV) break; } else if(bp->rksec>p1->rksec +INTLV ||bp->rksec<p2->rksec -INTLV) break; p1 = p2; if(!(p2 = p1->av_forw)) break; } break; } #ifdef SWEEP else if ( f ) { if(p2->rkcyl < p1->rkcyl) if(bp->rkcyl > p1->rkcyl) break; else f = 0; } else { if(p2->rkcyl > p1->rkcyl) if(bp->rkcyl < p1->rkcyl) break; else f++; } #endif bp->av_forw = p2; p1->av_forw = bp; while( p2 ) /* following blocks overtaken */ { if( p2->b_error) p2->b_error--; p2 = p2->av_forw; } } spl0(); } /* * rkstart() goes through all the queues and sets everybody * seeking that should be. */ rkstart() { register struct buf *bp; register struct rkq *qp; for (qp = rk_q; qp < &rk_q[ NDRV ]; qp++) #ifndef NO_SEEK if ((bp = qp->rk_bufp) && (bp->b_flags&B_SEEK)==0) #else NO_SEEK if ( bp = qp->rk_bufp ) #endif NO_SEEK { #ifdef SWEEP if(bp->rkcyl > qp->rk_lcyl) qp->rk_dirf = 1; else if(bp->rkcyl < qp->rk_lcyl) qp->rk_dirf = 0; #endif SWEEP #ifndef NO_SEEK bp->b_flags =| B_SEEK; while((RKADDR->rkcs&CTLRDY)==0); RKADDR->rkda = bp->rkaddr; RKADDR->rkcs = IENABLE|SEEK|GO; #else NO_SEEK #ifdef SWEEP qp->rk_lcyl = bp->rkcyl; #endif SWEEP rk_ap = qp; bp->b_error = 0; devstart(bp, &RKADDR->rkda, bp->rkaddr, 0); return; #endif NO_SEEK } } /* ** Command completion/error interrupt handler ** Rewritten -- Piers Lauder Jul '78 */ rkintr() { register union { int i; int *ip; } n; register struct rkq *qp; register struct buf *bp; int dreset = 0; int rkerr; if(RKADDR->rkcs < 0) /* error bit */ { if ( rk_ap && (rk_ap->rk_errcnt >= rkreport) ) { n.ip = RKADDR; printf("\nRKDA=%o,BA=%o,WC=%o,CS=%o,ER=%o,DS=%o\n", *n.ip,*n.ip++,*n.ip++,*n.ip++,*n.ip++,*n.ip++); } rkerr = RKADDR->rker; #ifndef NO_SEEK for(qp=rk_q; qp < &rk_q[ NDRV ]; qp++) if(qp->rk_bufp) qp->rk_bufp->b_flags =& ~B_SEEK; #endif NO_SEEK n.i = (RKADDR->rkda >> 13) & 07; if ( RKADDR->rkcs & HARDERR ) { RKADDR->rkcs = IENABLE|CRESET|GO; if ( rkerr & SEEKERR ) { while ( (RKADDR->rkcs & CTLRDY)==0 ); #ifndef NO_SEEK rtz: #endif RKADDR->rkda = n.i << 13; RKADDR->rkcs = IENABLE|DRESET|GO; dreset = 1; rk_ap = NULL; } #ifdef TEMP_CUT_OUT else if ( rkerr & NXD ) { extern char *panicstr; rkpowerf(); panicstr++; printf( "\nRK drive %d off line!\n", n.i ); panicstr = 0; return; } #endif TEMP_CUT_OUT } #ifndef NO_SEEK else if(RKADDR->rkds & SIN) { /* Seek Incomplete */ n.i = (RKADDR->rkds >> 13) & 07; goto rtz; } #endif NO_SEEK qp = rkvolmap[n.i]; if ( !(bp = qp->rk_bufp) ) return; /* dummy interrupt ? */ if(++(qp->rk_errcnt) == 10) { bp->b_flags =| B_ERROR; goto out; } if ( dreset ) { if ( n.ip = bp->av_forw ) { /** try next block first **/ bp->av_forw = n.ip->av_forw; n.ip->av_forw = bp; qp->rk_bufp = n.ip; qp->rk_errcnt = 0; } return; /* wait for ready interrupt */ } #ifndef NO_SEEK goto xfer; #else NO_SEEK rkstart(); #endif } #ifdef NO_SEEK else if ( RKADDR->rkcs & SEEKCMP ) rkstart(); #else NO_SEEK if ( RKADDR->rkcs & SEEKCMP ) { n.i = (RKADDR->rkds >> 13) & 07; if ( bp = (qp = rkvolmap[n.i])->rk_bufp ) { bp->b_flags =& ~B_SEEK; xfer: #ifdef SWEEP qp->rk_lcyl = bp->rkcyl; #endif rk_ap = qp; bp->b_error = 0; devstart(bp, &RKADDR->rkda, bp->rkaddr, 0); } } #endif else { if(qp = rk_ap) { /* we get here if this is an I/O completion interrupt */ bp = qp->rk_bufp; out: qp->rk_bufp = bp->av_forw; #ifdef RKSTATS if ( qp->rk_nreq-- > qp->rk_rmax ) qp->rk_rmax = qp->rk_nreq+1; #endif rk_ap = NULL; qp->rk_errcnt = 0; if ( !dreset ) rkstart(); iodone(bp); } } } /* * Raw interfaces. Note that the raw interface will NOT work with * the shuffled format if the i/o overflows the physical disk. * However, one may still use the raw device * interface for such applications as backups even when the disk in * question is a shuffled one, since all one wants to do in such a * case is copy the disk directly without caring at all what's there * and in what order. Raw device files should only be made with * minor devices zero through NRK. */ #ifndef RAW_BUFFER_POOL rkread(dev) { physio(rkstrategy, &rrkbuf, dev, B_READ); } rkwrite(dev) { physio(rkstrategy, &rrkbuf, dev, B_WRITE); } #endif #ifdef RAW_BUFFER_POOL rkread(dev) { physio(rkstrategy, 0, dev, B_READ); } rkwrite(dev) { physio(rkstrategy, 0, dev, B_WRITE); } #endif #ifdef POWER_FAIL | TEMP_CUT_OUT /* * restart after a power restore */ rkpowerf() { extern rkrestart(); register struct rkq *qp; register struct buf *bp; #ifndef NO_SEEK for ( qp=rk_q ; qp < &rk_q[NDRV] ; qp++ ) if ( bp = qp->rk_bufp ) bp->b_flags =& ~B_SEEK; #endif rk_ap = NULL; timeout( rkrestart , 0 , PUDELAY ); } rkrestart() { register i, j; for ( i=0 ; i<NRK ; i++ ) if ( rkvolmap[i]->rk_bufp ) { RKADDR->rkda = i<<13; for ( j=10 ; --j ; ); if ( (RKADDR->rkds & (RWS|DRY)) != (RWS|DRY) ) { timeout( rkrestart , 0 , PURETRY ); return; } } rkstart(); } #endif