X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/7bc8d9855e51104e84e05ea52e6e4ed38a0322c7..2a69f1e0b9c3340819accd877f7add23b39324ca:/usr/src/sys/vax/uba/up.c diff --git a/usr/src/sys/vax/uba/up.c b/usr/src/sys/vax/uba/up.c index a6933a498e..a79d82c0ac 100644 --- a/usr/src/sys/vax/uba/up.c +++ b/usr/src/sys/vax/uba/up.c @@ -1,63 +1,26 @@ -int cs1del; -int printsw; -/* %H% 3.2 %G% */ +/* up.c 4.3 %G% */ +#include "../conf/up.h" +#if NUP > 0 /* - * Emulex UNIBUS disk driver with overlapped seeks and ECC recovery. - * - * NB: This device is very sensitive: be aware that the code is the way - * it is for good reason and that there are delay loops here which may - * have to be lengthened if your processor is faster and which should - * probably be shortened if your processor is slower. - * - * This driver has been tested on a SC-11B Controller, configured - * with the following internal switch settings: - * SW1-1 5/19 surfaces (off, 19 surfaces on Ampex 9300) - * SW1-2 chksum enable (off, checksum disabled) - * SW1-3 volume select (off, 815 cylinders) - * SW1-4 sector select (on, 32 sectors) - * SW1-5 unused (off) - * SW1-6 port select (on, single port) - * SW1-7 npr delay (off, disable) - * SW1-8 ecc test mode (off, disable) - * and top mounted switches: - * SW2-1 extend opcodes (off=open, disable) - * SW2-2 extend diag (off=open, disable) - * SW2-3 4 wd dma burst (off=open, disable) - * SW2-4 unused (off=open) - * - * The controller transfers data much more rapidly with SW2-3 set, - * but we have previously experienced problems with it set this way. - * We intend to try this again in the near future. - * - * wnj June 14, 1980 + * UNIBUS disk driver with overlapped seeks and ECC recovery. */ +#define DELAY(N) { register int d; d = N; while (--d > 0); } #include "../h/param.h" #include "../h/systm.h" +#include "../h/dk.h" #include "../h/buf.h" #include "../h/conf.h" #include "../h/dir.h" #include "../h/user.h" #include "../h/map.h" +#include "../h/pte.h" #include "../h/mba.h" #include "../h/mtpr.h" -#include "../h/pte.h" #include "../h/uba.h" #include "../h/vm.h" -/* - * Define number of drives, and range of sampling information to be used. - * - * Normally, DK_N .. DK_N+NUP-1 gather individual drive stats, - * and DK_N+NUP gathers controller transferring stats. - * - * If DK_N+NUP > DK_NMAX, then transfer stats are divided per drive. - * If DK_NMAX is yet smaller, some drives are not monitored. - */ -#define DK_N 1 -#define DK_NMAX 2 - #define ushort unsigned short struct device @@ -84,9 +47,18 @@ struct device ushort upec2; /* burst error bit pattern */ }; -#define UPADDR ((struct device *)(UBA0_DEV + 0176700)) +/* + * Software extension to the upas register, so we can + * postpone starting SEARCH commands until the controller + * is not transferring. + */ +int upsoftas; -#define NUP 2 /* Number of drives this installation */ +/* + * If upseek then we don't issue SEARCH commands but rather just + * settle for a SEEK to the correct cylinder. + */ +int upseek; #define NSECT 32 #define NTRAC 19 @@ -94,14 +66,18 @@ struct device /* * Constants controlling on-cylinder SEARCH usage. * - * We assume that it takes SDIST sectors of time to set up a transfer. - * If a drive is on-cylinder, and between SDIST and SDIST+RDIST sectors - * from the first sector to be transferred, then we just perform the - * transfer. SDIST represents interrupt latency, RDIST the amount - * of rotation which is tolerable to avoid another interrupt. + * upSDIST/2 msec time needed to start transfer + * upRDIST/2 msec tolerable rotational latency when on-cylinder + * + * If we are no closer than upSDIST sectors and no further than upSDIST+upRDIST + * and in the driver then we take it as it is. Otherwise we do a SEARCH + * requesting an interrupt upSDIST sectors in advance. */ -#define SDIST 3 /* 2-3 sectors 1-1.5 msec */ -#define RDIST 6 /* 5-6 sectors 2.5-3 msec */ +#define _upSDIST 2 /* 1.0 msec */ +#define _upRDIST 4 /* 2.0 msec */ + +int upSDIST = _upSDIST; +int upRDIST = _upRDIST; /* * To fill a 300M drive: @@ -111,6 +87,9 @@ struct device * On systems with RP06'es, we normally use only 291346 blocks of the H * area, and use DEF or G to cover the rest of the drive. The C system * covers the whole drive and can be used for pack-pack copying. + * + * Note: sizes here are for AMPEX drives with 815 cylinders. + * CDC drives can make the F,G, and H areas larger as they have 823 cylinders. */ struct size { @@ -119,13 +98,11 @@ struct size } up_sizes[8] = { 15884, 0, /* A=cyl 0 thru 26 */ 33440, 27, /* B=cyl 27 thru 81 */ - 494912, 0, /* C=cyl 0 thru 814 */ + 495520, 0, /* C=cyl 0 thru 814 */ 15884, 562, /* D=cyl 562 thru 588 */ 55936, 589, /* E=cyl 589 thru 680 */ 81472, 681, /* F=cyl 681 thru 814 */ 153824, 562, /* G=cyl 562 thru 814 */ - 445664, 82, /* H=cyl 82 thru 814 */ -/* Later, and more safely for H area... 291346, 82, /* H=cyl 82 thru 561 */ }; @@ -135,7 +112,7 @@ struct size * +/- microinches. Note that header compare inhibit (HCI) is not * tried (this makes sense only during read, in any case.) * - * ARE ALL THESE IMPLEMENTED ON 9300? + * NB: Not all drives/controllers emulate all of these. */ #define P400 020 #define M400 0220 @@ -174,6 +151,7 @@ struct buf rupbuf; /* Buffer for raw i/o */ #define OFFSET 014 /* Offset heads to try to recover error */ #define RTC 016 /* Return to center-line after OFFSET */ #define SEARCH 030 /* Search for cylinder+sector */ +#define SEEK 04 /* Seek to cylinder */ #define RECAL 06 /* Recalibrate, needed after seek error */ #define DCLR 010 /* Drive clear, after error */ #define WCOM 060 /* Write */ @@ -182,7 +160,7 @@ struct buf rupbuf; /* Buffer for raw i/o */ /* Other bits of upcs1 */ #define IE 0100 /* Controller wide interrupt enable */ #define TRE 040000 /* Transfer error */ -#define RDY 020 /* Transfer terminated */ +#define RDY 0200 /* Transfer terminated */ /* Drive status bits of upds */ #define PIP 020000 /* Positioning in progress */ @@ -192,6 +170,8 @@ struct buf rupbuf; /* Buffer for raw i/o */ #define MOL 010000 /* Drive is online, heads loaded, etc */ #define DRY 0200 /* Drive ready */ +/* Bits of upcs2 */ +#define CLR 040 /* Controller clear */ /* Bits of uper1 */ #define DCK 0100000 /* Ecc error occurred */ #define ECH 0100 /* Ecc error was unrecoverable */ @@ -203,25 +183,10 @@ struct buf rupbuf; /* Buffer for raw i/o */ #define b_cylin b_resid int up_ubinfo; /* Information about UBA usage saved here */ -/* - * The EMULEX controller balks if accessed quickly after - * certain operations. The exact timing has not yet been - * determined, but delays are known to be needed when changing - * the selected drive (by writing in upcs2), and thought to be - * needed after operations like PRESET and DCLR. The following - * variables control the delay, DELAY(n) is approximately n usec. - */ -int idelay = 500; /* Delay after PRESET or DCLR */ -int sdelay = 500; /* Delay after selecting drive in upcs2 */ -int iedel1 = 500; -int iedel2 = 500; -int iedel3 = 0; -int iedel4 = 500; -#define DELAY(N) { register int d; d = N; while (--d > 0); } - -int nwaitcs2; /* How many sdelay loops ? */ -int neasycs2; /* How many sdelay loops not needed ? */ +int up_wticks; /* Ticks waiting for interrupt */ +int upwstart; /* Have started guardian */ +int upwatch(); #ifdef INTRLVE daddr_t dkblock(); @@ -242,6 +207,10 @@ register struct buf *bp; register unit, xunit; long sz, bn; + if (upwstart == 0) { + timeout(upwatch, (caddr_t)0, HZ); + upwstart++; + } xunit = minor(bp->b_dev) & 077; sz = bp->b_bcount; sz = (sz+511) >> 9; /* transfer size in 512 byte sectors */ @@ -253,14 +222,16 @@ register struct buf *bp; iodone(bp); return; } + if (DK_N+unit <= DK_NMAX) + dk_mspw[DK_N+unit] = .0000020345; bp->b_cylin = bn/(NSECT*NTRAC) + up_sizes[xunit&07].cyloff; dp = &uputab[unit]; (void) spl5(); disksort(dp, bp); if (dp->b_active == 0) { - upustart(unit); + (void) upustart(unit); if (uptab.b_actf && uptab.b_active == 0) - upstart(); + (void) upstart(); } (void) spl0(); } @@ -277,118 +248,114 @@ register unit; register struct device *upaddr = UPADDR; daddr_t bn; int sn, cn, csn; + int didie = 0; - if (printsw&1) printf("upustart\n"); - if (unit >= NUP) - return; /* - * Whether or not it was before, this unit is no longer busy. - * Check to see if there is (still or now) a request in this - * drives queue, and if there is, select this unit. + * Other drivers tend to say something like + * upaddr->upcs1 = IE; + * upaddr->upas = 1<= NUP) + goto out; if (unit+DK_N <= DK_NMAX) dk_busy &= ~(1<<(unit+DK_N)); dp = &uputab[unit]; if ((bp = dp->b_actf) == NULL) - return; - if ((upaddr->upcs2 & 07) != unit) { + goto out; + /* + * Most controllers don't start SEARCH commands when transfers are + * in progress. In fact, some tend to get confused when given + * SEARCH'es during transfers, generating interrupts with neither + * RDY nor a bit in the upas register. Thus we defer + * until an interrupt when a transfer is pending. + */ + if (uptab.b_active) { + upsoftas |= 1<b_active) + goto done; + dp->b_active = 1; + if ((upaddr->upcs2 & 07) != unit) upaddr->upcs2 = unit; - DELAY(sdelay); - nwaitcs2++; - } else - neasycs2++; /* * If we have changed packs or just initialized, - * the the volume will not be valid; if so, clear + * then the volume will not be valid; if so, clear * the drive, preset it and put in 16bit/word mode. */ if ((upaddr->upds & VV) == 0) { upaddr->upcs1 = IE|DCLR|GO; - DELAY(idelay); upaddr->upcs1 = IE|PRESET|GO; - DELAY(idelay); upaddr->upof = FMT22; + didie = 1; } - /* - * We are called from upstrategy when a new request arrives - * if we are not already active (with dp->b_active == 0), - * and we then set dp->b_active to 1 if we are to SEARCH - * for the desired cylinder, or 2 if we are on-cylinder. - * If we SEARCH then we will later be called from upintr() - * when the search is complete, and will link this disk onto - * the uptab. We then set dp->b_active to 2 so that upintr() - * will not call us again. - * - * NB: Other drives clear the bit in the attention status - * (i.e. upas) register corresponding to the drive when they - * place the drive on the ready (i.e. uptab) queue. This does - * not work with the Emulex, as the controller hangs the UBA - * of the VAX shortly after the upas register is set, for - * reasons unknown. This only occurs in multi-spindle configurations, - * but to avoid the problem we use the fact that dp->b_active is - * 2 to replace the clearing of the upas bit. - */ - if (dp->b_active) - goto done; - dp->b_active = 1; if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL)) - goto done; /* Will redetect error in upstart() soon */ - + goto done; /* * Do enough of the disk address decoding to determine * which cylinder and sector the request is on. - * Then compute the number of the sector SDIST sectors before - * the one where the transfer is to start, this being the - * point where we wish to attempt to begin the transfer, - * allowing approximately SDIST/2 msec for interrupt latency - * and preparation of the request. - * * If we are on the correct cylinder and the desired sector - * lies between SDIST and SDIST+RDIST sectors ahead of us, then + * lies between upSDIST and upSDIST+upRDIST sectors ahead of us, then * we don't bother to SEARCH but just begin the transfer asap. + * Otherwise ask for a interrupt upSDIST sectors ahead. */ bn = dkblock(bp); cn = bp->b_cylin; sn = bn%(NSECT*NTRAC); - sn = (sn+NSECT-SDIST)%NSECT; + sn = (sn+NSECT-upSDIST)%NSECT; if (cn - upaddr->updc) goto search; /* Not on-cylinder */ + else if (upseek) + goto done; /* Ok just to be on-cylinder */ csn = (upaddr->upla>>6) - sn - 1; if (csn < 0) csn += NSECT; - if (csn > NSECT-RDIST) + if (csn > NSECT-upRDIST) goto done; search: upaddr->updc = cn; - upaddr->upda = sn; - if (cs1del&8) DELAY(500); - upaddr->upcs1 = IE|SEARCH|GO; - if (cs1del&8) DELAY(500); + if (upseek) + upaddr->upcs1 = IE|SEEK|GO; + else { + upaddr->upda = sn; + upaddr->upcs1 = IE|SEARCH|GO; + } + didie = 1; /* * Mark this unit busy. */ unit += DK_N; if (unit <= DK_NMAX) { dk_busy |= 1<b_active = 2; dp->b_forw = NULL; if (uptab.b_actf == NULL) uptab.b_actf = dp; else uptab.b_actl->b_forw = dp; uptab.b_actl = dp; + +out: + return (didie); } /* @@ -402,7 +369,6 @@ upstart() daddr_t bn; int dn, sn, tn, cn, cmd; - if (printsw&2) printf("upstart\n"); loop: /* * Pick a drive off the queue of ready drives, and @@ -413,7 +379,7 @@ loop: * request queue. */ if ((dp = uptab.b_actf) == NULL) - return; + return (0); if ((bp = dp->b_actf) == NULL) { uptab.b_actf = dp->b_forw; goto loop; @@ -432,13 +398,9 @@ loop: tn = sn/NSECT; sn %= NSECT; upaddr = UPADDR; - if ((upaddr->upcs2 & 07) != dn) { + if ((upaddr->upcs2 & 07) != dn) upaddr->upcs2 = dn; - DELAY(sdelay); - nwaitcs2++; - } else - neasycs2++; - up_ubinfo = ubasetup(bp, 1); /* In a funny place for delay... */ + up_ubinfo = ubasetup(bp, 1); /* * If drive is not present and on-line, then * get rid of this with an error and loop to get @@ -446,23 +408,28 @@ loop: * (Then on to any other ready drives.) */ if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL)) { - uptab.b_active = 0; - uptab.b_errcnt = 0; - dp->b_actf = bp->av_forw; - dp->b_active = 0; - bp->b_flags |= B_ERROR; - iodone(bp); - ubafree(up_ubinfo), up_ubinfo = 0; /* A funny place ... */ - goto loop; + printf("!DPR || !MOL, unit %d, ds %o", dn, upaddr->upds); + if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL)) { + printf("-- hard\n"); + uptab.b_active = 0; + uptab.b_errcnt = 0; + dp->b_actf = bp->av_forw; + dp->b_active = 0; + bp->b_flags |= B_ERROR; + iodone(bp); + /* A funny place to do this ... */ + ubafree(up_ubinfo), up_ubinfo = 0; + goto loop; + } + printf("-- came back\n"); } /* * If this is a retry, then with the 16'th retry we * begin to try offsetting the heads to recover the data. */ - if (uptab.b_errcnt >= 16) { + if (uptab.b_errcnt >= 16 && (bp->b_flags&B_WRITE) == 0) { upaddr->upof = up_offset[uptab.b_errcnt & 017] | FMT22; upaddr->upcs1 = IE|OFFSET|GO; - DELAY(idelay); while (upaddr->upds & PIP) DELAY(25); } @@ -474,16 +441,13 @@ loop: upaddr->updc = cn; upaddr->upda = (tn << 8) + sn; upaddr->upba = up_ubinfo; - if (cs1del&1) DELAY(500); upaddr->upwc = -bp->b_bcount / sizeof (short); cmd = (up_ubinfo >> 8) & 0x300; if (bp->b_flags & B_READ) cmd |= IE|RCOM|GO; else cmd |= IE|WCOM|GO; - if (cs1del&1) DELAY(500); upaddr->upcs1 = cmd; - if (cs1del&1) DELAY(500); /* * This is a controller busy situation. * Record in dk slot NUP+DK_N (after last drive) @@ -492,13 +456,12 @@ loop: * (if there is room for that). */ unit = dn+DK_N; - if (NUP+DK_N == DK_NMAX) - unit = NUP+DK_N; if (unit <= DK_NMAX) { dk_busy |= 1<b_bcount>>6; } + return (1); } /* @@ -516,8 +479,11 @@ upintr() register unit; register struct device *upaddr = UPADDR; int as = upaddr->upas & 0377; + int oupsoftas; + int needie = 1; - if (printsw&4) printf("upintr as=%d act %d %d %d\n", as, uptab.b_active, uputab[0].b_active, uputab[1].b_active); + (void) spl6(); + up_wticks = 0; if (uptab.b_active) { /* * The drive is transferring, thus the hardware @@ -525,36 +491,30 @@ upintr() * completes; check for it anyways. */ if ((upaddr->upcs1 & RDY) == 0) { - printf("upintr b_active && !RDY\n"); - goto out; + printf("!RDY: cs1 %o, ds %o, wc %d\n", upaddr->upcs1, + upaddr->upds, upaddr->upwc); + printf("as=%d act %d %d %d\n", as, uptab.b_active, + uputab[0].b_active, uputab[1].b_active); } /* - * Mark controller or drive not busy, and check for an + * Mark drive not busy, and check for an * error condition which may have resulted from the transfer. */ dp = uptab.b_actf; bp = dp->b_actf; unit = dkunit(bp); - if (DK_N+NUP == DK_NMAX) - dk_busy &= ~(1<<(DK_N+NUP)); - else if (DK_N+unit <= DK_NMAX) + if (DK_N+unit <= DK_NMAX) dk_busy &= ~(1<<(DK_N+unit)); - if (upaddr->upcs1 & TRE) { + if ((upaddr->upcs2 & 07) != unit) + upaddr->upcs2 = unit; + if ((upaddr->upds&ERR) || (upaddr->upcs1&TRE)) { /* * An error occurred, indeed. Select this unit * to get at the drive status (a SEARCH may have * intervened to change the selected unit), and * wait for the command which caused the interrupt * to complete (DRY). - * - * WHY IS THE WAIT NECESSARY? */ - if ((upaddr->upcs2 & 07) != unit) { - upaddr->upcs2 = unit; - DELAY(sdelay); - nwaitcs2++; - } else - neasycs2++; while ((upaddr->upds & DRY) == 0) DELAY(25); /* @@ -569,7 +529,8 @@ upintr() else uptab.b_active = 0; /* To force retry */ if (uptab.b_errcnt > 27) - deverror(bp, upaddr->upcs2, upaddr->uper1); + deverror(bp, (int)upaddr->upcs2, + (int)upaddr->uper1); /* * If this was a correctible ECC error, let upecc * do the dirty work to correct it. If upecc @@ -585,10 +546,9 @@ upintr() * to hopefully help clear up seek positioning problems. */ upaddr->upcs1 = TRE|IE|DCLR|GO; - DELAY(idelay); + needie = 0; if ((uptab.b_errcnt&07) == 4) { upaddr->upcs1 = RECAL|GO|IE; - DELAY(idelay); while(upaddr->upds & PIP) DELAY(25); } @@ -603,17 +563,11 @@ upintr() * on this drive with the upustart routine (if any). */ if (uptab.b_active) { - if ((upaddr->upcs2 & 07) != unit) { - upaddr->upcs2 = unit; - DELAY(sdelay); - nwaitcs2++; - } else - neasycs2++; if (uptab.b_errcnt >= 16) { upaddr->upcs1 = RTC|GO|IE; - DELAY(idelay); while (upaddr->upds & PIP) DELAY(25); + needie = 0; } uptab.b_active = 0; uptab.b_errcnt = 0; @@ -622,26 +576,22 @@ upintr() dp->b_errcnt = 0; dp->b_actf = bp->av_forw; bp->b_resid = (-upaddr->upwc * sizeof(short)); - if (cs1del&2) DELAY(500); - upaddr->upcs1 = IE; - if (cs1del&2) DELAY(500); + if (bp->b_resid) + printf("resid %d ds %o er? %o %o %o\n", + bp->b_resid, upaddr->upds, + upaddr->uper1, upaddr->uper2, upaddr->uper3); iodone(bp); if(dp->b_actf) - upustart(unit); + if (upustart(unit)) + needie = 0; } as &= ~(1<upcs1); - if (upaddr->upcs1 & TRE) { + } else { + if (upaddr->upcs1 & TRE) upaddr->upcs1 = TRE; - DELAY(idelay); - if (printsw&64) printf("after TRE cs1 %o\n", upaddr->upcs1); - } } -#endif /* * If we have a unit with an outstanding SEARCH, * and the hardware indicates the unit requires attention, @@ -649,22 +599,21 @@ upintr() * Finally, if the controller is not transferring * start it if any drives are now ready to transfer. */ + as |= upsoftas; + oupsoftas = upsoftas; + upsoftas = 0; for (unit = 0; unit < NUP; unit++) - if (as & (1<upas = 1<upcs1&IE) == 0) + if (upstart()) + needie = 0; + if (needie) upaddr->upcs1 = IE; - if (cs1del&4) DELAY(500); - if (printsw&128) printf("exit cs1 %o\n", upaddr->upcs1); } upread(dev) @@ -695,7 +644,6 @@ register struct buf *bp; int reg, bit, byte, npf, mask, o, cmd, ubaddr; int bn, cn, tn, sn; - if (printsw&8) printf("upecc\n"); /* * Npf is the number of sectors transferred before the sector * containing the ECC error, and reg is the UBA register @@ -710,7 +658,6 @@ register struct buf *bp; mask = up->upec2; if (mask == 0) { up->upof = FMT22; /* == RTC ???? */ - DELAY(idelay); return (0); } /* @@ -748,7 +695,6 @@ register struct buf *bp; * restart at offset o of next sector (i.e. in UBA register reg+1). */ up->upcs1 = TRE|IE|DCLR|GO; - DELAY(idelay); bn = dkblock(bp); cn = bp->b_cylin; sn = bn%(NSECT*NTRAC) + npf + 1; @@ -765,3 +711,56 @@ register struct buf *bp; up->upcs1 = cmd; return (1); } + +/* + * Reset driver after UBA init. + * Cancel software state of all pending transfers + * and restart all units and the controller. + */ +upreset() +{ + int unit; + + printf(" up"); + uptab.b_active = 0; + uptab.b_actf = uptab.b_actl = 0; + if (up_ubinfo) { + printf("<%d>", (up_ubinfo>>28)&0xf); + ubafree(up_ubinfo), up_ubinfo = 0; + } + UPADDR->upcs2 = CLR; /* clear controller */ + for (unit = 0; unit < NUP; unit++) { + uputab[unit].b_active = 0; + (void) upustart(unit); + } + (void) upstart(); +} + +/* + * Wake up every second and if an interrupt is pending + * but nothing has happened increment a counter. + * If nothing happens for 20 seconds, reset the controller + * and begin anew. + */ +upwatch() +{ + int i; + + timeout(upwatch, (caddr_t)0, HZ); + if (uptab.b_active == 0) { + for (i = 0; i < NUP; i++) + if (uputab[i].b_active) + goto active; + up_wticks = 0; /* idling */ + return; + } +active: + up_wticks++; + if (up_wticks >= 20) { + up_wticks = 0; + printf("LOST INTERRUPT RESET"); + upreset(); + printf("\n"); + } +} +#endif