# /* * 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" #ifdef AUSAML #include "../lnode.h" #endif AUSAML #include "../systm.h" #include "../buf.h" #include "../conf.h" #include "../user.h" /* #define NO_SEEK /* disable seeks */ /* #define RKSTATS /* collect statistics */ #define SWEEP /* enable directional optimisation */ #ifdef PERTEC #define ERR_FLOOR 1 /* errors allowed before report */ #endif PERTEC #define NRK 3 /* number of RK05 volumes on system */ #define NDRV 3 /* number of drives on system */ #define INTLV 2 /* magic interleaving number - see code */ #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 SIN 01000 /* seek incomplete */ #define SEEKCMP 020000 /* seek complete */ #ifdef PERTEC & POWER_FAIL #define DRY 0200 /* drive ready */ #define PUDELAY 25*HZ /* drive power up time */ #define PURETRY 5*HZ /* power up test retry delay */ #endif POWER_FAIL & PERTEC #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], /* RK drive 0 */ &rk_q[1], /* RK drive 1 */ &rk_q[2], /* RK drive 2 */ }; #ifdef ERR_FLOOR int rkreport ERR_FLOOR; /* floor for error reports */ #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+1 (2436) 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) ) { bp->b_flags =| B_ERROR; iodone(bp); return; } #ifdef _1170 if ( bp->b_flags & B_PHYS ) mapalloc( bp ); #endif /* * Here, we do the mapping for the various formats: */ if ( (p1 & 010) && p2 && ((p2 =+ NHRKBLK) >= NRKBLK) ) p2 =- NRKBLK - 1; p1 =& 07; bp->av_forw = 0; 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; 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. #ifndef PERTEC * INTLV is set to 2 because system * timing constraints dictate * a two-block gap between * sequential blocks to be read * or written for optimum efficiency. #else * INTLV is set to 1 because * a one block gap between * sequential blocks will allow * quickest access as the disk * rotates. #endif PERTEC */ if (p2->rksec > p1->rksec) { if(bp->rksec > p1->rksec +INTLV &&bp->rksec < p2 ->rksec-INTLV) goto out; } else if(bp->rksec>p1->rksec +INTLV ||bp->rksec<p2->rksec -INTLV) goto out; p1 = p2; if(!(p2 = p1->av_forw)) goto out; } goto out; } #ifdef SWEEP else if ( f ) { if(p2->rkcyl < p1->rkcyl) if(bp->rkcyl > p1->rkcyl) goto out; else f = 0; } else { if(p2->rkcyl > p1->rkcyl) if(bp->rkcyl < p1->rkcyl) goto out; else f++; } #endif out: 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) { #endif #ifdef NO_SEEK if ( bp = qp->rk_bufp ) { #endif #ifdef SWEEP if(bp->rkcyl > qp->rk_lcyl) qp->rk_dirf = 1; else if(bp->rkcyl < qp->rk_lcyl) qp->rk_dirf = 0; #endif #ifndef NO_SEEK bp->b_flags =| B_SEEK; while((RKADDR->rkcs&CTLRDY)==0); RKADDR->rkda = bp->rkaddr; RKADDR->rkcs = IENABLE|SEEK|GO; #endif #ifdef NO_SEEK #ifdef SWEEP qp->rk_lcyl = bp->rkcyl; #endif rk_ap = qp; bp->b_error = 0; devstart(bp, &RKADDR->rkda, bp->rkaddr, 0); return; #endif } } rkintr() { register union { int i, *ip; } n; register struct rkq *qp; register struct buf *bp; #ifndef NO_SEEK int dreset; #endif if(RKADDR->rkcs < 0) { /* error bit */ #ifndef PERTEC 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++); #else 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++); } #endif PERTEC #ifndef NO_SEEK for(qp=rk_q; qp < &rk_q[ NDRV ]; qp++) if(qp->rk_bufp) qp->rk_bufp->b_flags =& ~B_SEEK; if(RKADDR->rkds & SIN) { /* Seek Incomplete */ n.i = (RKADDR->rkds >> 13) & 07; RKADDR->rkda = n.i<<13; RKADDR->rkcs = IENABLE|DRESET|GO; /* will generate SEEKCMP interrupt */ dreset = 1; }else { #endif /* other errors */ n.i = (RKADDR->rkda >> 13) & 07; RKADDR->rkcs = IENABLE|CRESET|GO; #ifndef NO_SEEK dreset = 0; } #endif qp = rkvolmap[n.i]; bp = qp->rk_bufp; if(++(qp->rk_errcnt) == 10) { bp->b_flags =| B_ERROR; goto out; } #ifndef NO_SEEK if ( dreset ) return; /* wait for ready interrupt */ #endif while ( (RKADDR->rkcs & CTLRDY)==0 ); #ifndef NO_SEEK goto xfer; #endif #ifdef NO_SEEK rkstart(); #endif } #ifndef NO_SEEK if(RKADDR->rkcs & SEEKCMP) { n.i = (RKADDR->rkds >> 13) & 07; if(n.i >= NRK || rk_ap) { printf("\nRK SOFTERR"); return; } if((bp = (qp = rkvolmap[n.i])->rk_bufp)->b_flags & B_SEEK) { 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 { /* we get here if this is an I/O completion interrupt */ if(qp = rk_ap) { 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; rkstart(); iodone(bp); } } } /* * Raw interfaces. Note that the raw interface does NOT work with * either of the "funny" RK formats, because mapping must be done on * a block-by-block basis, 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 & PERTEC /* * 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 & DRY) ) { timeout( rkrestart , 0 , PURETRY ); return; } } rkstart(); } #endif POWER_FAIL & PERTEC