X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/5dec97d39baf6926ee7bdd7fa2547094df86a788..de59ccc2375aad4c9f0ff41f3291584ead755f15:/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 d60fa3165e..44e1bdbeb2 100644 --- a/usr/src/sys/vax/uba/up.c +++ b/usr/src/sys/vax/uba/up.c @@ -1,680 +1,679 @@ -int csdel0 = 30; -int asdel = 500; -/* %H% 3.7 %G% */ +/* up.c 4.53 82/05/27 */ +#include "up.h" +#if NSC > 0 /* - * Emulex UNIBUS disk driver with overlapped seeks and ECC recovery. + * 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 + * TODO: + * Add bad sector forwarding code + * Check that offset recovery code works */ #include "../h/param.h" #include "../h/systm.h" +#include "../h/cpu.h" +#include "../h/nexus.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/mba.h" -#include "../h/mtpr.h" #include "../h/pte.h" -#include "../h/uba.h" +#include "../h/mtpr.h" #include "../h/vm.h" +#include "../h/ubavar.h" +#include "../h/ubareg.h" +#include "../h/cmap.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 -{ - ushort upcs1; /* control and status register 1 */ - short upwc; /* word count register */ - ushort upba; /* UNIBUS address register */ - ushort upda; /* desired address register */ - ushort upcs2; /* control and status register 2 */ - ushort upds; /* drive Status */ - ushort uper1; /* error register 1 */ - ushort upas; /* attention summary */ - ushort upla; /* look ahead */ - ushort updb; /* data buffer */ - ushort upmr; /* maintenance */ - ushort updt; /* drive type */ - ushort upsn; /* serial number */ - ushort upof; /* offset register */ - ushort updc; /* desired cylinder address register */ - ushort upcc; /* current cylinder */ - ushort uper2; /* error register 2 */ - ushort uper3; /* error register 3 */ - ushort upec1; /* burst error bit position */ - ushort upec2; /* burst error bit pattern */ -}; - -#define UPADDR ((struct device *)(UBA0_DEV + 0176700)) - -#define NUP 2 /* Number of drives this installation */ +#include "../h/upreg.h" -#define NSECT 32 -#define NTRAC 19 +struct up_softc { + int sc_softas; + int sc_ndrive; + int sc_wticks; + int sc_recal; +} up_softc[NSC]; -/* - * 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. - */ -#define SDIST 3 /* 2-3 sectors 1-1.5 msec */ -#define RDIST 6 /* 5-6 sectors 2.5-3 msec */ - -/* - * To fill a 300M drive: - * A is designed to be used as a root. - * B is suitable for a swap area. - * H is the primary storage area. - * 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. - */ +/* THIS SHOULD BE READ OFF THE PACK, PER DRIVE */ struct size { daddr_t nblocks; int cyloff; } up_sizes[8] = { +#ifdef ERNIE + 49324, 0, /* A=cyl 0 thru 26 */ +#else 15884, 0, /* A=cyl 0 thru 26 */ +#endif 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... +#ifndef NOBADSECT + 81376, 681, /* F=cyl 681 thru 814 */ + 153728, 562, /* G=cyl 562 thru 814 */ +#else + 81472, 681, + 153824, 562, +#endif 291346, 82, /* H=cyl 82 thru 561 */ +}, fj_sizes[8] = { + 15884, 0, /* A=cyl 0 thru 49 */ + 33440, 50, /* B=cyl 50 thru 154 */ + 263360, 0, /* C=cyl 0 thru 822 */ + 0, 0, + 0, 0, + 0, 0, + 0, 0, +#ifndef NOBADSECT + 213664, 155, /* H=cyl 155 thru 822 */ +#else + 213760, 155, +#endif +}, upam_sizes[8] = { + 15884, 0, /* A=cyl 0 thru 31 */ + 33440, 32, /* B=cyl 32 thru 97 */ + 524288, 0, /* C=cyl 0 thru 1023 */ + 27786, 668, + 27786, 723, + 125440, 778, + 181760, 668, /* G=cyl 668 thru 1022 */ + 291346, 98, /* H=cyl 98 thru 667 */ }; +/* END OF STUFF WHICH SHOULD BE READ IN PER DISK */ /* - * The following defines are used in offset positioning - * when trying to recover disk errors, with the constants being - * +/- 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? + * On a 780 upSDIST could be 2, but + * in the interest of 750's... */ -#define P400 020 -#define M400 0220 -#define P800 040 -#define M800 0240 -#define P1200 060 -#define M1200 0260 -#define HCI 020000 - -int up_offset[16] = -{ - P400, M400, P400, M400, - P800, M800, P800, M800, - P1200, M1200, P1200, M1200, - 0, 0, 0, 0, -}; +#define _upSDIST 3 /* 1.5 msec */ +#define _upRDIST 4 /* 2.0 msec */ -/* - * Each drive has a table uputab[i]. On this table are sorted the - * pending requests implementing an elevator algorithm (see dsort.c.) - * In the upustart() routine, each drive is independently advanced - * until it is on the desired cylinder for the next transfer and near - * the desired sector. The drive is then chained onto the uptab - * table, and the transfer is initiated by the upstart() routine. - * When the transfer is completed the driver reinvokes the upustart() - * routine to set up the next transfer. - */ -struct buf uptab; +int upSDIST = _upSDIST; +int upRDIST = _upRDIST; + +int upprobe(), upslave(), upattach(), updgo(), upintr(); +struct uba_ctlr *upminfo[NSC]; +struct uba_device *updinfo[NUP]; +#define UPIPUNITS 8 +struct uba_device *upip[NSC][UPIPUNITS]; /* fuji w/fixed head gives n,n+4 */ + +u_short upstd[] = { 0776700, 0774400, 0776300, 0 }; +struct uba_driver scdriver = + { upprobe, upslave, upattach, updgo, upstd, "up", updinfo, "sc", upminfo }; struct buf uputab[NUP]; -struct buf rupbuf; /* Buffer for raw i/o */ - -/* Drive commands, placed in upcs1 */ -#define GO 01 /* Go bit, set in all commands */ -#define PRESET 020 /* Preset drive at init or after errors */ -#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 RECAL 06 /* Recalibrate, needed after seek error */ -#define DCLR 010 /* Drive clear, after error */ -#define WCOM 060 /* Write */ -#define RCOM 070 /* Read */ - -/* Other bits of upcs1 */ -#define IE 0100 /* Controller wide interrupt enable */ -#define TRE 040000 /* Transfer error */ -#define RDY 020 /* Transfer terminated */ - -/* Drive status bits of upds */ -#define PIP 020000 /* Positioning in progress */ -#define ERR 040000 /* Error has occurred, DCLR necessary */ -#define VV 0100 /* Volume is valid, set by PRESET */ -#define DPR 0400 /* Drive has been preset */ -#define MOL 010000 /* Drive is online, heads loaded, etc */ -#define DRY 0200 /* Drive ready */ - -/* Bits of uper1 */ -#define DCK 0100000 /* Ecc error occurred */ -#define ECH 0100 /* Ecc error was unrecoverable */ -#define WLE 04000 /* Attempt to write read-only drive */ - -/* Bits of upof; the offset bits above are also in this register */ -#define FMT22 010000 /* 16 bits/word, must be always set */ +struct upst { + short nsect; + short ntrak; + short nspc; + short ncyl; + struct size *sizes; +} upst[] = { + 32, 19, 32*19, 823, up_sizes, /* 9300/cdc */ +/* 9300 actually has 815 cylinders... */ + 32, 10, 32*10, 823, fj_sizes, /* fujitsu 160m */ + 32, 16, 32*16, 1024, upam_sizes, /* ampex capricorn */ +}; -#define b_cylin b_resid +u_char up_offset[16] = { + UPOF_P400, UPOF_M400, UPOF_P400, UPOF_M400, + UPOF_P800, UPOF_M800, UPOF_P800, UPOF_M800, + UPOF_P1200, UPOF_M1200, UPOF_P1200, UPOF_M1200, + 0, 0, 0, 0 +}; -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 = 150; /* Delay after selecting drive in upcs2 */ +struct buf rupbuf[NUP]; -#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 ? */ +#define b_cylin b_resid #ifdef INTRLVE daddr_t dkblock(); #endif - + +int upwstart, upwatch(); /* Have started guardian */ +int upseek; +int upwaitdry; + +/*ARGSUSED*/ +upprobe(reg) + caddr_t reg; +{ + register int br, cvec; + +#ifdef lint + br = 0; cvec = br; br = cvec; +#endif + ((struct updevice *)reg)->upcs1 = UP_IE|UP_RDY; + DELAY(10); + ((struct updevice *)reg)->upcs1 = 0; + return (1); +} + +upslave(ui, reg) + struct uba_device *ui; + caddr_t reg; +{ + register struct updevice *upaddr = (struct updevice *)reg; + + upaddr->upcs1 = 0; /* conservative */ + upaddr->upcs2 = ui->ui_slave; + upaddr->upcs1 = UP_NOP|UP_GO; + if (upaddr->upcs2&UPCS2_NED) { + upaddr->upcs1 = UP_DCLR|UP_GO; + return (0); + } + return (1); +} + +upattach(ui) + register struct uba_device *ui; +{ + register struct updevice *upaddr; + + if (upwstart == 0) { + timeout(upwatch, (caddr_t)0, hz); + upwstart++; + } + if (ui->ui_dk >= 0) + dk_mspw[ui->ui_dk] = .0000020345; + upip[ui->ui_ctlr][ui->ui_slave] = ui; + up_softc[ui->ui_ctlr].sc_ndrive++; + upaddr = (struct updevice *)ui->ui_addr; + upaddr->upcs1 = 0; + upaddr->upcs2 = ui->ui_slave; + upaddr->uphr = UPHR_MAXTRAK; + if (upaddr->uphr == 9) + ui->ui_type = 1; /* fujitsu hack */ + else if (upaddr->uphr == 15) + ui->ui_type = 2; /* ampex hack */ + upaddr->upcs2 = UPCS2_CLR; /* - * Queue an i/o request for a drive, checking first that it is in range. - * - * A unit start is issued if the drive is inactive, causing - * a SEARCH for the correct cylinder/sector. If the drive is - * already nearly on the money and the controller is not transferring - * we kick it to start the transfer. - */ + upaddr->uphr = UPHR_MAXCYL; + printf("maxcyl %d\n", upaddr->uphr); + upaddr->uphr = UPHR_MAXTRAK; + printf("maxtrak %d\n", upaddr->uphr); + upaddr->uphr = UPHR_MAXSECT; + printf("maxsect %d\n", upaddr->uphr); +*/ +} + upstrategy(bp) -register struct buf *bp; + register struct buf *bp; { + register struct uba_device *ui; + register struct upst *st; + register int unit; register struct buf *dp; - register unit, xunit; - long sz, bn; + int xunit = minor(bp->b_dev) & 07; + long bn, sz; - xunit = minor(bp->b_dev) & 077; - sz = bp->b_bcount; - sz = (sz+511) >> 9; /* transfer size in 512 byte sectors */ + sz = (bp->b_bcount+511) >> 9; unit = dkunit(bp); - if (unit >= NUP || - bp->b_blkno < 0 || - (bn = dkblock(bp))+sz > up_sizes[xunit&07].nblocks) { - bp->b_flags |= B_ERROR; - iodone(bp); - return; - } - bp->b_cylin = bn/(NSECT*NTRAC) + up_sizes[xunit&07].cyloff; - dp = &uputab[unit]; + if (unit >= NUP) + goto bad; + ui = updinfo[unit]; + if (ui == 0 || ui->ui_alive == 0) + goto bad; + st = &upst[ui->ui_type]; + if (bp->b_blkno < 0 || + (bn = dkblock(bp))+sz > st->sizes[xunit].nblocks) + goto bad; + bp->b_cylin = bn/st->nspc + st->sizes[xunit].cyloff; (void) spl5(); + dp = &uputab[ui->ui_unit]; disksort(dp, bp); if (dp->b_active == 0) { - (void) upustart(unit); - if (uptab.b_actf && uptab.b_active == 0) - (void) upstart(); + (void) upustart(ui); + bp = &ui->ui_mi->um_tab; + if (bp->b_actf && bp->b_active == 0) + (void) upstart(ui->ui_mi); } (void) spl0(); + return; + +bad: + bp->b_flags |= B_ERROR; + iodone(bp); + return; } /* - * Start activity on specified drive; called when drive is inactive - * and new transfer request arrives and also when upas indicates that - * a SEARCH command is complete. + * Unit start routine. + * Seek the drive to be where the data is + * and then generate another interrupt + * to actually start the transfer. + * If there is only one drive on the controller, + * or we are very close to the data, don't + * bother with the search. If called after + * searching once, don't bother to look where + * we are, just queue for transfer (to avoid + * positioning forever without transferrring.) */ -upustart(unit) -register unit; +upustart(ui) + register struct uba_device *ui; { register struct buf *bp, *dp; - register struct device *upaddr = UPADDR; + register struct uba_ctlr *um; + register struct updevice *upaddr; + register struct upst *st; daddr_t bn; - int sn, cn, csn; - int didie = 0; - - if (unit >= NUP) - goto out; + int sn, csn; /* - * 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. + * The SC21 cancels commands if you just say + * cs1 = UP_IE + * so we are cautious about handling of cs1. + * Also don't bother to clear as bits other than in upintr(). */ - if (unit+DK_N <= DK_NMAX) - dk_busy &= ~(1<<(unit+DK_N)); - dp = &uputab[unit]; + int didie = 0; + + if (ui == 0) + return (0); + um = ui->ui_mi; + dk_busy &= ~(1<ui_dk); + dp = &uputab[ui->ui_unit]; if ((bp = dp->b_actf) == NULL) goto out; - 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 - * the drive, preset it and put in 16bit/word mode. + * If the controller is active, just remember + * that this device would like to be positioned... + * if we tried to position now we would confuse the SC21. */ - if ((upaddr->upds & VV) == 0) { - upaddr->upcs1 = IE|DCLR|GO; - DELAY(idelay); - upaddr->upcs1 = IE|PRESET|GO; - DELAY(idelay); - upaddr->upof = FMT22; - didie = 1; + if (um->um_tab.b_active) { + up_softc[um->um_ctlr].sc_softas |= 1<ui_slave; + return (0); } /* - * 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 we have already positioned this drive, + * then just put it on the ready queue. */ 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 */ - + upaddr = (struct updevice *)um->um_addr; + upaddr->upcs2 = ui->ui_slave; + /* + * If drive has just come up, + * setup the pack. + */ + if ((upaddr->upds & UPDS_VV) == 0) { + /* SHOULD WARN SYSTEM THAT THIS HAPPENED */ + upaddr->upcs1 = UP_IE|UP_DCLR|UP_GO; + upaddr->upcs1 = UP_IE|UP_PRESET|UP_GO; + upaddr->upof = UPOF_FMT22; + didie = 1; + } /* - * 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 - * we don't bother to SEARCH but just begin the transfer asap. + * If drive is offline, forget about positioning. */ + if ((upaddr->upds & (UPDS_DPR|UPDS_MOL)) != (UPDS_DPR|UPDS_MOL)) + goto done; + /* + * If there is only one drive, + * dont bother searching. + */ + if (up_softc[um->um_ctlr].sc_ndrive == 1) + goto done; + /* + * Figure out where this transfer is going to + * and see if we are close enough to justify not searching. + */ + st = &upst[ui->ui_type]; bn = dkblock(bp); - cn = bp->b_cylin; - sn = bn%(NSECT*NTRAC); - sn = (sn+NSECT-SDIST)%NSECT; - - if (cn - upaddr->updc) + sn = bn%st->nspc; + sn = (sn + st->nsect - upSDIST) % st->nsect; + if (bp->b_cylin - 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) + csn += st->nsect; + if (csn > st->nsect - upRDIST) goto done; - search: - upaddr->updc = cn; - upaddr->upda = sn; - upaddr->upcs1 = IE|SEARCH|GO; + upaddr->updc = bp->b_cylin; + /* + * Not on cylinder at correct position, + * seek/search. + */ + if (upseek) + upaddr->upcs1 = UP_IE|UP_SEEK|UP_GO; + else { + upaddr->upda = sn; + upaddr->upcs1 = UP_IE|UP_SEARCH|UP_GO; + } didie = 1; /* - * Mark this unit busy. + * Mark unit busy for iostat. */ - unit += DK_N; - if (unit <= DK_NMAX) { - dk_busy |= 1<ui_dk >= 0) { + dk_busy |= 1<ui_dk; + dk_seek[ui->ui_dk]++; } - if (csdel0) DELAY(csdel0); goto out; - done: /* - * This unit is ready to go. Make active == 2 so - * we won't get called again (by upintr() because upas&(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; - + if (dp->b_active != 2) { + dp->b_forw = NULL; + if (um->um_tab.b_actf == NULL) + um->um_tab.b_actf = dp; + else + um->um_tab.b_actl->b_forw = dp; + um->um_tab.b_actl = dp; + dp->b_active = 2; + } out: return (didie); } /* - * Start a transfer; call from top level at spl5() or on interrupt. + * Start up a transfer on a drive. */ -upstart() +upstart(um) + register struct uba_ctlr *um; { register struct buf *bp, *dp; - register unit; - register struct device *upaddr; + register struct uba_device *ui; + register struct updevice *upaddr; + struct upst *st; daddr_t bn; - int dn, sn, tn, cn, cmd; + int dn, sn, tn, cmd, waitdry; loop: /* - * Pick a drive off the queue of ready drives, and - * perform the first transfer on its queue. - * - * Looping here is completely for the sake of drives which - * are not present and on-line, for which we completely clear the - * request queue. + * Pull a request off the controller queue */ - if ((dp = uptab.b_actf) == NULL) { + if ((dp = um->um_tab.b_actf) == NULL) return (0); - } if ((bp = dp->b_actf) == NULL) { - uptab.b_actf = dp->b_forw; + um->um_tab.b_actf = dp->b_forw; goto loop; } /* - * Mark the controller busy, and multi-part disk address. - * Select the unit on which the i/o is to take place. + * Mark controller busy, and + * determine destination of this request. */ - uptab.b_active++; - unit = minor(bp->b_dev) & 077; - dn = dkunit(bp); + um->um_tab.b_active++; + ui = updinfo[dkunit(bp)]; bn = dkblock(bp); - cn = up_sizes[unit&07].cyloff; - cn += bn/(NSECT*NTRAC); - sn = bn%(NSECT*NTRAC); - tn = sn/NSECT; - sn %= NSECT; - upaddr = UPADDR; - if ((upaddr->upcs2 & 07) != dn) { - upaddr->upcs2 = dn; - DELAY(sdelay); - nwaitcs2++; - } else - neasycs2++; - up_ubinfo = ubasetup(bp, 1); /* In a funny place for delay... */ + dn = ui->ui_slave; + st = &upst[ui->ui_type]; + sn = bn%st->nspc; + tn = sn/st->nsect; + sn %= st->nsect; + upaddr = (struct updevice *)ui->ui_addr; /* - * If drive is not present and on-line, then - * get rid of this with an error and loop to get - * rid of the rest of its queued requests. - * (Then on to any other ready drives.) + * Select drive if not selected already. */ - 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; - } + if ((upaddr->upcs2&07) != dn) + upaddr->upcs2 = dn; /* - * If this is a retry, then with the 16'th retry we - * begin to try offsetting the heads to recover the data. + * Check that it is ready and online */ - if (uptab.b_errcnt >= 16) { - upaddr->upof = up_offset[uptab.b_errcnt & 017] | FMT22; - upaddr->upcs1 = IE|OFFSET|GO; - DELAY(idelay); - while (upaddr->upds & PIP) - DELAY(25); + waitdry = 0; + while ((upaddr->upds&UPDS_DRY) == 0) { + if (++waitdry > 512) + break; + upwaitdry++; + } + if ((upaddr->upds & UPDS_DREADY) != UPDS_DREADY) { + printf("up%d: not ready", dkunit(bp)); + if ((upaddr->upds & UPDS_DREADY) != UPDS_DREADY) { + printf("\n"); + um->um_tab.b_active = 0; + um->um_tab.b_errcnt = 0; + dp->b_actf = bp->av_forw; + dp->b_active = 0; + bp->b_flags |= B_ERROR; + iodone(bp); + goto loop; + } + /* + * Oh, well, sometimes this + * happens, for reasons unknown. + */ + printf(" (flakey)\n"); } /* - * Now set up the transfer, retrieving the high - * 2 bits of the UNIBUS address from the information - * returned by ubasetup() for the cs1 register bits 8 and 9. + * Setup for the transfer, and get in the + * UNIBUS adaptor queue. */ - upaddr->updc = cn; + upaddr->updc = bp->b_cylin; upaddr->upda = (tn << 8) + sn; - upaddr->upba = up_ubinfo; upaddr->upwc = -bp->b_bcount / sizeof (short); - cmd = (up_ubinfo >> 8) & 0x300; if (bp->b_flags & B_READ) - cmd |= IE|RCOM|GO; + cmd = UP_IE|UP_RCOM|UP_GO; else - cmd |= IE|WCOM|GO; - upaddr->upcs1 = cmd; - /* - * This is a controller busy situation. - * Record in dk slot NUP+DK_N (after last drive) - * unless there aren't that many slots reserved for - * us in which case we record this as a drive busy - * (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; - } + cmd = UP_IE|UP_WCOM|UP_GO; + um->um_cmd = cmd; + (void) ubago(ui); return (1); } /* - * Handle a device interrupt. - * - * If the transferring drive needs attention, service it - * retrying on error or beginning next transfer. - * Service all other ready drives, calling ustart to transfer - * their blocks to the ready queue in uptab, and then restart - * the controller if there is anything to do. + * Now all ready to go, stuff the registers. + */ +updgo(um) + struct uba_ctlr *um; +{ + register struct updevice *upaddr = (struct updevice *)um->um_addr; + + um->um_tab.b_active = 2; /* should now be 2 */ + upaddr->upba = um->um_ubinfo; + upaddr->upcs1 = um->um_cmd|((um->um_ubinfo>>8)&0x300); +} + +/* + * Handle a disk interrupt. */ -upintr() +upintr(sc21) + register sc21; { register struct buf *bp, *dp; + register struct uba_ctlr *um = upminfo[sc21]; + register struct uba_device *ui; + register struct updevice *upaddr = (struct updevice *)um->um_addr; register unit; - register struct device *upaddr = UPADDR; - int as = upaddr->upas & 0377; - int needie = 1; + struct up_softc *sc = &up_softc[um->um_ctlr]; + int as = (upaddr->upas & 0377) | sc->sc_softas; + int needie = 1, waitdry; - if (uptab.b_active) { - /* - * The drive is transferring, thus the hardware - * (say the designers) will only interrupt when the transfer - * completes; check for it anyways. - */ - if ((upaddr->upcs1 & RDY) == 0) { - printf("!RDY in upintr: cs1 %o\n", upaddr->upcs1); -printf("as=%d act %d %d %d\n", as, uptab.b_active, uputab[0].b_active, uputab[1].b_active); + sc->sc_wticks = 0; + sc->sc_softas = 0; + /* + * If controller wasn't transferring, then this is an + * interrupt for attention status on seeking drives. + * Just service them. + */ + if (um->um_tab.b_active != 2 && !sc->sc_recal) { + if (upaddr->upcs1 & UP_TRE) + upaddr->upcs1 = UP_TRE; + goto doattn; + } + um->um_tab.b_active = 1; + /* + * Get device and block structures, and a pointer + * to the uba_device for the drive. Select the drive. + */ + dp = um->um_tab.b_actf; + bp = dp->b_actf; + ui = updinfo[dkunit(bp)]; + dk_busy &= ~(1 << ui->ui_dk); + if ((upaddr->upcs2&07) != ui->ui_slave) + upaddr->upcs2 = ui->ui_slave; + /* + * Check for and process errors on + * either the drive or the controller. + */ + if ((upaddr->upds&UPDS_ERR) || (upaddr->upcs1&UP_TRE)) { + waitdry = 0; + while ((upaddr->upds & UPDS_DRY) == 0) { + if (++waitdry > 512) + break; + upwaitdry++; } - /* - * Mark controller or 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) - dk_busy &= ~(1<<(DK_N+unit)); - if (upaddr->upcs1 & TRE) { + if (upaddr->uper1&UPER1_WLE) { /* - * 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? + * Give up on write locked devices + * immediately. */ - if ((upaddr->upcs2 & 07) != unit) { - upaddr->upcs2 = unit; - DELAY(sdelay); - nwaitcs2++; - } else - neasycs2++; - while ((upaddr->upds & DRY) == 0) - DELAY(25); + printf("up%d: write locked\n", dkunit(bp)); + bp->b_flags |= B_ERROR; + } else if (++um->um_tab.b_errcnt > 27) { /* - * After 28 retries (16 w/o servo offsets, and then - * 12 with servo offsets), or if we encountered - * an error because the drive is write-protected, - * give up. Print an error message on the last 2 - * retries before a hard failure. + * After 28 retries (16 without offset, and + * 12 with offset positioning) give up. */ - if (++uptab.b_errcnt > 28 || upaddr->uper1&WLE) - bp->b_flags |= B_ERROR; - else - uptab.b_active = 0; /* To force retry */ - if (uptab.b_errcnt > 27) - deverror(bp, upaddr->upcs2, upaddr->uper1); + harderr(bp, "up"); + printf("cs2=%b er1=%b er2=%b\n", + upaddr->upcs2, UPCS2_BITS, + upaddr->uper1, UPER1_BITS, + upaddr->uper2, UPER2_BITS); + bp->b_flags |= B_ERROR; + } else { /* - * If this was a correctible ECC error, let upecc - * do the dirty work to correct it. If upecc - * starts another READ for the rest of the data - * then it returns 1 (having set uptab.b_active). - * Otherwise we are done and fall through to - * finish up. + * Retriable error. + * If a soft ecc, correct it (continuing + * by returning if necessary. + * Otherwise fall through and retry the transfer */ - if ((upaddr->uper1&(DCK|ECH))==DCK && upecc(upaddr, bp)) - return; - /* - * Clear the drive and, every 4 retries, recalibrate - * 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); - } + um->um_tab.b_active = 0; /* force retry */ + if ((upaddr->uper1&(UPER1_DCK|UPER1_ECH))==UPER1_DCK) + if (upecc(ui)) + return; } /* - * If we are still noted as active, then no - * (further) retries are necessary. - * - * Make sure the correct unit is selected, - * return it to centerline if necessary, and mark - * this i/o complete, starting the next transfer - * on this drive with the upustart routine (if any). + * Clear drive error and, every eight attempts, + * (starting with the fourth) + * recalibrate to clear the slate. */ - 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; - uptab.b_actf = dp->b_forw; - dp->b_active = 0; - dp->b_errcnt = 0; - dp->b_actf = bp->av_forw; - bp->b_resid = (-upaddr->upwc * sizeof(short)); - iodone(bp); - if(dp->b_actf) - if (upustart(unit)) - needie = 0; + upaddr->upcs1 = UP_TRE|UP_IE|UP_DCLR|UP_GO; + needie = 0; + if ((um->um_tab.b_errcnt&07) == 4 && um->um_tab.b_active == 0) { + upaddr->upcs1 = UP_RECAL|UP_IE|UP_GO; + sc->sc_recal = 0; + goto nextrecal; } - as &= ~(1<upcs1 & TRE) { - upaddr->upcs1 = TRE; - DELAY(idelay); + /* + * Advance recalibration finite state machine + * if recalibrate in progress, through + * RECAL + * SEEK + * OFFSET (optional) + * RETRY + */ + switch (sc->sc_recal) { + + case 1: + upaddr->updc = bp->b_cylin; + upaddr->upcs1 = UP_SEEK|UP_IE|UP_GO; + goto nextrecal; + case 2: + if (um->um_tab.b_errcnt < 16 || (bp->b_flags&B_READ) == 0) + goto donerecal; + upaddr->upof = up_offset[um->um_tab.b_errcnt & 017] | UPOF_FMT22; + upaddr->upcs1 = UP_IE|UP_OFFSET|UP_GO; + goto nextrecal; + nextrecal: + sc->sc_recal++; + um->um_tab.b_active = 1; + return; + donerecal: + case 3: + sc->sc_recal = 0; + um->um_tab.b_active = 0; + break; + } + /* + * If still ``active'', then don't need any more retries. + */ + if (um->um_tab.b_active) { + /* + * If we were offset positioning, + * return to centerline. + */ + if (um->um_tab.b_errcnt >= 16) { + upaddr->upof = UPOF_FMT22; + upaddr->upcs1 = UP_RTC|UP_GO|UP_IE; + while (upaddr->upds & UPDS_PIP) + DELAY(25); + needie = 0; } + um->um_tab.b_active = 0; + um->um_tab.b_errcnt = 0; + um->um_tab.b_actf = dp->b_forw; + dp->b_active = 0; + dp->b_errcnt = 0; + dp->b_actf = bp->av_forw; + bp->b_resid = (-upaddr->upwc * sizeof(short)); + iodone(bp); + /* + * If this unit has more work to do, + * then start it up right away. + */ + if (dp->b_actf) + if (upustart(ui)) + needie = 0; } -#endif + as &= ~(1<ui_slave); /* - * If we have a unit with an outstanding SEARCH, - * and the hardware indicates the unit requires attention, - * the bring the drive to the ready queue. - * Finally, if the controller is not transferring - * start it if any drives are now ready to transfer. + * Release unibus resources and flush data paths. */ - for (unit = 0; unit < NUP; unit++) - if (as & (1<upas = 1<upas = 1<upas = 1<upcs1 = IE; } + /* + * If the controller is not transferring, but + * there are devices ready to transfer, start + * the controller. + */ + if (um->um_tab.b_actf && um->um_tab.b_active == 0) + if (upstart(um)) + needie = 0; + if (needie) + upaddr->upcs1 = UP_IE; } upread(dev) + dev_t dev; { + register int unit = minor(dev) >> 3; - physio(upstrategy, &rupbuf, dev, B_READ, minphys); + if (unit >= NUP) + u.u_error = ENXIO; + else + physio(upstrategy, &rupbuf[unit], dev, B_READ, minphys); } upwrite(dev) + dev_t dev; { + register int unit = minor(dev) >> 3; - physio(upstrategy, &rupbuf, dev, B_WRITE, minphys); + if (unit >= NUP) + u.u_error = ENXIO; + else + physio(upstrategy, &rupbuf[unit], dev, B_WRITE, minphys); } /* @@ -683,11 +682,14 @@ upwrite(dev) * the transfer may be going to an odd memory address base and/or * across a page boundary. */ -upecc(up, bp) -register struct device *up; -register struct buf *bp; +upecc(ui) + register struct uba_device *ui; { - struct uba_regs *ubp = (struct uba_regs *)UBA0; + register struct updevice *up = (struct updevice *)ui->ui_addr; + register struct buf *bp = uputab[ui->ui_unit].b_actf; + register struct uba_ctlr *um = ui->ui_mi; + register struct upst *st; + struct uba_regs *ubp = ui->ui_hd->uh_uba; register int i; caddr_t addr; int reg, bit, byte, npf, mask, o, cmd, ubaddr; @@ -700,23 +702,22 @@ register struct buf *bp; * O is offset within a memory page of the first byte transferred. */ npf = btop((up->upwc * sizeof(short)) + bp->b_bcount) - 1; - reg = btop(up_ubinfo&0x3ffff) + npf; + reg = btop(um->um_ubinfo&0x3ffff) + npf; o = (int)bp->b_un.b_addr & PGOFSET; - printf("%D ", bp->b_blkno+npf); - prdev("ECC", bp->b_dev); + printf("up%d%c: soft ecc sn%d\n", dkunit(bp), + 'a'+(minor(bp->b_dev)&07), bp->b_blkno + npf); mask = up->upec2; - if (mask == 0) { - up->upof = FMT22; /* == RTC ???? */ - DELAY(idelay); - return (0); - } +#ifdef UPECCDEBUG + printf("npf %d reg %x o %d mask %o pos %d\n", npf, reg, o, mask, + up->upec1); +#endif /* * Flush the buffered data path, and compute the * byte and bit position of the error. The variable i * is the byte offset in the transfer, the variable byte * is the offset from a page boundary in main memory. */ - ubp->uba_dpr[(up_ubinfo>>28)&0x0f] |= BNE; + ubapurge(um); i = up->upec1 - 1; /* -1 makes 0 origin */ bit = i&07; i = (i&~07)>>3; @@ -730,12 +731,19 @@ register struct buf *bp; while (i < 512 && (int)ptob(npf)+i < bp->b_bcount && bit > -11) { addr = ptob(ubp->uba_map[reg+btop(byte)].pg_pfnum)+ (byte & PGOFSET); +#ifdef UPECCDEBUG + printf("addr %x map reg %x\n", + addr, *(int *)(&ubp->uba_map[reg+btop(byte)])); + printf("old: %x, ", getmemc(addr)); +#endif putmemc(addr, getmemc(addr)^(mask<upwc == 0) return (0); /* @@ -744,21 +752,191 @@ register struct buf *bp; * We have completed npf+1 sectors of the transfer already; * restart at offset o of next sector (i.e. in UBA register reg+1). */ - up->upcs1 = TRE|IE|DCLR|GO; - DELAY(idelay); +#ifdef notdef + up->uper1 = 0; + up->upcs1 |= UP_GO; +#else + up->upcs1 = UP_TRE|UP_IE|UP_DCLR|UP_GO; bn = dkblock(bp); + st = &upst[ui->ui_type]; cn = bp->b_cylin; - sn = bn%(NSECT*NTRAC) + npf + 1; - tn = sn/NSECT; - sn %= NSECT; - cn += tn/NTRAC; - tn %= NTRAC; + sn = bn%st->nspc + npf + 1; + tn = sn/st->nsect; + sn %= st->nsect; + cn += tn/st->ntrak; + tn %= st->ntrak; up->updc = cn; up->upda = (tn << 8) | sn; ubaddr = (int)ptob(reg+1) + o; up->upba = ubaddr; cmd = (ubaddr >> 8) & 0x300; - cmd |= IE|GO|RCOM; + cmd |= UP_IE|UP_GO|UP_RCOM; + um->um_tab.b_active = 2; /* continuing transfer ... */ up->upcs1 = cmd; +#endif return (1); } + +/* + * Reset driver after UBA init. + * Cancel software state of all pending transfers + * and restart all units and the controller. + */ +upreset(uban) + int uban; +{ + register struct uba_ctlr *um; + register struct uba_device *ui; + register sc21, unit; + + for (sc21 = 0; sc21 < NSC; sc21++) { + if ((um = upminfo[sc21]) == 0 || um->um_ubanum != uban || + um->um_alive == 0) + continue; + printf(" sc%d", sc21); + um->um_tab.b_active = 0; + um->um_tab.b_actf = um->um_tab.b_actl = 0; + up_softc[sc21].sc_recal = 0; + up_softc[sc21].sc_wticks = 0; + if (um->um_ubinfo) { + printf("<%d>", (um->um_ubinfo>>28)&0xf); + ubadone(um); + } + ((struct updevice *)(um->um_addr))->upcs2 = UPCS2_CLR; + for (unit = 0; unit < NUP; unit++) { + if ((ui = updinfo[unit]) == 0) + continue; + if (ui->ui_alive == 0 || ui->ui_mi != um) + continue; + uputab[unit].b_active = 0; + (void) upustart(ui); + } + (void) upstart(um); + } +} + +/* + * 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 UNIBUS + * and begin anew. + */ +upwatch() +{ + register struct uba_ctlr *um; + register sc21, unit; + register struct up_softc *sc; + + timeout(upwatch, (caddr_t)0, hz); + for (sc21 = 0; sc21 < NSC; sc21++) { + um = upminfo[sc21]; + if (um == 0 || um->um_alive == 0) + continue; + sc = &up_softc[sc21]; + if (um->um_tab.b_active == 0) { + for (unit = 0; unit < NUP; unit++) + if (uputab[unit].b_active && + updinfo[unit]->ui_mi == um) + goto active; + sc->sc_wticks = 0; + continue; + } +active: + sc->sc_wticks++; + if (sc->sc_wticks >= 20) { + sc->sc_wticks = 0; + printf("sc%d: lost interrupt\n", sc21); + ubareset(um->um_ubanum); + } + } +} + +#define DBSIZE 20 + +updump(dev) + dev_t dev; +{ + struct updevice *upaddr; + char *start; + int num, blk, unit; + struct size *sizes; + register struct uba_regs *uba; + register struct uba_device *ui; + register short *rp; + struct upst *st; + register int retry; + + unit = minor(dev) >> 3; + if (unit >= NUP) + return (ENXIO); +#define phys(cast, addr) ((cast)((int)addr & 0x7fffffff)) + ui = phys(struct uba_device *, updinfo[unit]); + if (ui->ui_alive == 0) + return (ENXIO); + uba = phys(struct uba_hd *, ui->ui_hd)->uh_physuba; + ubainit(uba); + upaddr = (struct updevice *)ui->ui_physaddr; + DELAY(5000000); + num = maxfree; + upaddr->upcs2 = unit; + DELAY(100); + upaddr->upcs1 = UP_DCLR|UP_GO; + upaddr->upcs1 = UP_PRESET|UP_GO; + upaddr->upof = UPOF_FMT22; + retry = 0; + do { + DELAY(25); + if (++retry > 527) + break; + } while ((upaddr->upds & UP_RDY) == 0); + if ((upaddr->upds & UPDS_DREADY) != UPDS_DREADY) + return (EFAULT); + start = 0; + st = &upst[ui->ui_type]; + sizes = phys(struct size *, st->sizes); + if (dumplo < 0 || dumplo + num >= sizes[minor(dev)&07].nblocks) + return (EINVAL); + while (num > 0) { + register struct pte *io; + register int i; + int cn, sn, tn; + daddr_t bn; + + blk = num > DBSIZE ? DBSIZE : num; + io = uba->uba_map; + for (i = 0; i < blk; i++) + *(int *)io++ = (btop(start)+i) | (1<<21) | UBAMR_MRV; + *(int *)io = 0; + bn = dumplo + btop(start); + cn = bn/st->nspc + sizes[minor(dev)&07].cyloff; + sn = bn%st->nspc; + tn = sn/st->nsect; + sn = sn%st->nsect; + upaddr->updc = cn; + rp = (short *) &upaddr->upda; + *rp = (tn << 8) + sn; + *--rp = 0; + *--rp = -blk*NBPG / sizeof (short); + *--rp = UP_GO|UP_WCOM; + retry = 0; + do { + DELAY(25); + if (++retry > 527) + break; + } while ((upaddr->upcs1 & UP_RDY) == 0); + if ((upaddr->upds & UPDS_DREADY) != UPDS_DREADY) { + printf("up%d: not ready", unit); + if ((upaddr->upds & UPDS_DREADY) != UPDS_DREADY) { + printf("\n"); + return (EIO); + } + printf(" (flakey)\n"); + } + if (upaddr->upds&UPDS_ERR) + return (EIO); + start += blk*NBPG; + num -= blk; + } + return (0); +} +#endif