Ausam/sys/dmr/unused/xy.c
#
/*
** Optimised Xylogics Phoenix 211 Disk Controller Driver
**
** Bugs & comments to: Piers Lauder
** Dept. of Computer Science
** Sydney University
*/
#include "../defines.h"
#include "../param.h"
#include "../conf.h"
#include "../buf.h"
#include "../user.h"
#define NDRV 1 /* Drives on controller */
/* (not MANY_DRIVES)
#define MANY_DRIVES /* if NDRV > 1 (otherwise save some code)
/* (not DIFFERENT_DRIVES)
#define DIFFERENT_DRIVES /* if physically different drives attched to controller */
/* (not VOLUME_STATS)
#define VOLUME_STATS /* record disc usage by volume */
/* (not DRIVE_STATS)
#define DRIVE_STATS /* record disc accesses by cylinder and queue size */
/*
** assignment of minor device bits
**
** bits 030 are drives,
** bits 007 are volumes.
*/
#define NVOL 7 /* max allowable volumes per drive */
#define INTLV 1 /* interleave factor for rotational optimisation */
#define XYAGE 10 /* number of times i/o may be preempted */
#define XYOPENPRI 10 /* sleep priority whilst waiting for drive ready */
#define XYADDR 0164000 /* controller address */
#define SPLXY spl5 /* interrupt priority */
#include "xy.h" /* definitions of controller registers */
#include "ok.h" /* Okidata 3300 Disc Drive parameters */
/*
** Structure to describe drive states and conditions
*/
struct drive
{
char dr_state; /* drive status */
char dr_qstate; /* queue status */
struct buf *dr_qp; /* head of i/o queue */
int dr_cyl; /* current cylinder */
char dr_dir; /* current direction of head sweep */
char dr_com; /* last command to this drive */
char dr_errcnt; /* count of errors on current i/o */
char dr_rtzcnt; /* count of drive resets on current i/o */
char dr_id; /* this drive number */
# ifdef DIFFERENT_DRIVES
int dr_trksecs; /* sectors per track */
int dr_cylsecs; /* sectors per cylinder */
# endif DIFFERENT_DRIVES
unsigned dr_errors[16]; /* drive errors per XYERR */
# ifdef DRIVE_STATS
unsigned dr_cylref[NOKCYL/8]; /* count of accesses to cylinder areas */
unsigned dr_qref[NBUF]; /* count of queue sizes */
int dr_qsize; /* current queue size */
unsigned dr_rotopt; /* count of rotational optimisations */
# endif DRIVE_STATS
}
xydrv[NDRV]
#ifdef DIFFERENT_DRIVES
{
{ 0,0,0,0,0,0,0,0,0,NOKSEC,NOKSEC*NOKHDS }
}
#endif DIFFERENT_DRIVES
;
/** drive states **/
#define DR_RDY 0 /* up and running */
#define DR_UNR 1 /* unready */
#define DR_ERR 2 /* hard error */
/** queue (i/o) states **/
#define Q_EMPTY 0 /* guess */
#define Q_WAIT 1 /* waiting for i/o */
#ifdef MANY_DRIVES
#define Q_SEEK 2 /* seeking */
#define Q_RDY 3 /* on cylinder */
#else MANY_DRIVES
#define Q_RDY Q_WAIT /* do i/o immediately */
#endif MANY_DRIVES
#define Q_IO 4 /* i/o in progress */
/** sweep direction **/
#define IN 0 /* increasing cylinder number */
#define OUT 1 /* decreasing cylinder number */
/*
** pointer to current drive doing i/o
*/
struct drive *xydp;
/*
** Structure to map logical volumes onto drives
*/
struct volume
{
unsigned v_blocks; /* max. blocks this volume */
int v_cyloff; /* 1st. cylinder address */
# ifdef VOLUME_STATS
long v_ref; /* count of accesses to this volume */
# endif VOLUME_STATS
}
xymap[NDRV][NVOL]
{
{ /** Drive 1 -- Okidata 3300 **/
{ 32256, 0 } /* 0: 84 cylinders */
,{ 13824, 84 } /* 1: 36 */
,{ 3840, 120 } /* 2: 10 */
,{ 14592, 130 } /* 3: 38 */
,{ 33408, 168 } /* 4: 87 */
,{ 32256, 255 } /* 5: 84 */
/* 130176, 339 1-1 mapping */
,{ 65536, 168 } /* 6: 171 (max. volume) */
}
};
/*
** Error Recovery Strategy for read errors
*/
int xyretrycm[]
{
0 /* 3 in-place retrys */
,02000 /* data late, servo normal */
,04000 /* data early, servo normal */
,(SEEKINH|0400) /* data normal, servo minus */ /* single sector only */
,(SEEKINH|01000) /* data normal, servo plus */ /* single sector only */
,(SEEKINH|02400) /* data late, servo minus */ /* single sector only */
,(SEEKINH|03000) /* data late, servo plus */ /* single sector only */
,(SEEKINH|04400) /* data early, servo minus */ /* single sector only */
,(SEEKINH|05000) /* data early, servo plus */ /* single sector only */
};
#define RECALTRYS 3 /* number of recalibrates to be attempted after RECAL error */
/*
** Use b_scratch in the buffer header to hold cylinder address,
** b_resid to hold i/o skip count,
** av_forw to hold pointer to next buffer in i/o queue,
** and av_back to hold unit_sector_head address.
*/
#define b_next av_forw /* must be "struct buf *" */
#define b_ush av_back /* 16 bits */
#define b_age b_resid /* must be int */
#define b_cyl b_scratch /* 16 bits */
/*
** Raw buffer for physical i/o
*/
#ifdef RAW_BUFFER_POOL
#define XYRAWBUF 0
#else RAW_BUFFER_POOL
struct buf xyrawbuf;
#define XYRAWBUF &xyrawbuf
#endif RAW_BUFFER_POOL
/*
** Device Table
*/
struct devtab xytab;
/*
** Strategy routine
**
** -- sort buffer into optimised i/o queue
*/
xystrategy( bp )
register struct buf *bp;
{
int unit;
{
register unsigned b;
register struct volume *vp;
b = bp->b_dev.d_minor;
unit = (b>>3)&7;
b =& 7;
# ifdef MANY_DRIVES
vp = &xymap[unit][b];
# else MANY_DRIVES
vp = &xymap[0][b];
# endif MANY_DRIVES
/*
** Check for validity of this request
*/
if ( unit >= NDRV || b >= NVOL || (b = bp->b_blkno) >= vp->v_blocks )
{
bad:
bp->b_flags =| B_ERROR;
iodone( bp );
return;
}
/*
** if raw i/o, check within limits
*/
if ( bp->b_flags & B_PHYS )
{
if ( (b+(-bp->b_wcount)/256) > vp->v_blocks )
goto bad;
# ifdef UNIBUS_MAP
mapalloc( bp );
# endif UNIBUS_MAP
}
# ifdef VOLUME_STATS
vp->v_ref++;
# endif VOLUME_STATS
/*
** Calculate sector, cylinder, and disk address
*/
# ifdef DIFFERENT_DRIVES
# define TRKSECS trksecs
# define CYLSECS cylsecs
# else DIFFERENT_DRIVES
# define TRKSECS NOKSEC
# define CYLSECS (NOKSEC*NOKHDS)
# endif DIFFERENT_DRIVES
{
# ifdef DIFFERENT_DRIVES
register trksecs = xydrv[unit].dr_trksecs;
register cylsecs = xydrv[unit].dr_cylsecs;
# endif DIFFERENT_DRIVES
bp->b_cyl = b / CYLSECS + vp->v_cyloff;
# ifdef MANY_DRIVES
bp->b_ush = (unit << 12) | ((b % CYLSECS)/TRKSECS << 7) | (bp->b_sector = b % TRKSECS);
# else MANY_DRIVES
bp->b_ush = ((b % CYLSECS)/TRKSECS << 7) | (bp->b_sector = b % TRKSECS);
# endif MANY_DRIVES
bp->b_age = XYAGE;
}
}
SPLXY();
{
register struct buf *p1, *p2;
int d;
# ifdef MANY_DRIVES
p2 = &xydrv[unit];
# else MANY_DRIVES
p2 = xydrv;
# endif MANY_DRIVES
# ifdef DRIVE_STATS
p2->dr_qref[p2->dr_qsize++]++;
p2->dr_cylref[bp->b_cyl/8]++;
# endif DRIVE_STATS
if ( (p1 = p2->dr_qp) == NULL )
{
/** Start up empty queue **/
p2->dr_qp = bp;
p2->dr_qstate = Q_WAIT;
xystart();
}
else
{
/*
** Non-empty queue - determine where to place this request
** in the queue, taking into account current direction of
** head movement, and minimisation of head movement.
*/
d = p2->dr_dir;
if ( p2 = p1->b_next )
do
if ( p2->b_age == 0 )
p1 = p2; /* skip this block overtaken too often */
while
( p2 = p2->b_next );
for ( ; p2 = p1->b_next ; p1 = p2 )
{
if ( p1->b_cyl <= bp->b_cyl
&& bp->b_cyl <= p2->b_cyl
|| p1->b_cyl >= bp->b_cyl
&& bp->b_cyl >= p2->b_cyl
)
{
for ( p1 = p2 ; (p2 = p1->b_next) && ( bp->b_cyl == p2->b_cyl ) ; p1 = p2 )
{
/** Cylinder match -- do rotational optimisation **/
# ifdef DRIVE_STATS
xydrv[unit].dr_rotopt++;
# endif DRIVE_STATS
if ( p2->b_sector > p1->b_sector )
{
if ( bp->b_sector > p1->b_sector + INTLV
&& bp->b_sector < p2->b_sector - INTLV
)
break;
}
else
if ( bp->b_sector > p1->b_sector + INTLV
|| bp->b_sector < p2->b_sector - INTLV
)
break;
}
break;
}
else
if ( d == IN )
{
if ( p2->b_cyl < p1->b_cyl )
if ( bp->b_cyl > p1->b_cyl )
break;
else
d = OUT;
}
else
if ( p2->b_cyl > p1->b_cyl )
if ( bp->b_cyl < p1->b_cyl )
break;
else
d = IN;
}
p1->b_next = bp;
if ( bp->b_next = p2 )
do
/** following blocks overtaken **/
p2->b_age--;
while
( p2 = p2->b_next );
}
}
spl0();
}
/*
#ifdef MANY_DRIVES
** Start seeks on all drives waiting that are not on cylinder
#endif MANY_DRIVES
** Start i/o on first drive ready on cylinder
*/
xystart()
{
register struct drive *dp = xydrv;
# ifdef MANY_DRIVES
register x, i;
static struct drive *lastdrive xydrv;
/*
** Start seeks on waiting queues
*/
for ( ; dp < &xydrv[NDRV] ; dp++ )
if ( (dp->dr_state == DR_RDY) && (dp->dr_qstate == Q_WAIT) )
if ( dp->dr_cyl != dp->dr_qp->b_cyl )
{
/** Start this drive seeking **/
xycommand( dp, XY_SEEK );
dp->dr_qstate = Q_SEEK;
}
else
dp->dr_qstate = Q_RDY;
/*
** Attend to "seek done" status for other drives
*/
if ( (x = XYSTAT) & ANYSKDN )
for ( dp = xydrv, i = FSTSKDN ; dp < &xydrv[NDRV] ; i =<< 1, dp++ )
if ( x & i )
{
dp->dr_qstate = Q_RDY;
break;
}
/*
** "Round robin" poll of ready drives
*/
for ( dp = lastdrive ; ((dp == &xydrv[NDRV-1]) ? (dp = xydrv) : ++dp) && (dp != lastdrive) ; )
# endif MANY_DRIVES
if ( (xydp == NULL) && (dp->dr_state == DR_RDY) && (dp->dr_qstate == Q_RDY) )
{
/** Start i/o on this drive **/
xycommand( dp, (dp->dr_qp->b_flags & B_READ ? XY_READ : XY_WRITE) );
dp->dr_qstate = Q_IO;
# ifdef MANY_DRIVES
lastdrive = dp;
break;
# endif MANY_DRIVES
}
}
/*
** Set up command in controller registers
*/
xycommand( dp, com )
register struct drive *dp;
{
register struct buf *bp = dp->dr_qp;
{
register x;
if ( (x = com & XY_COM) == XY_READ || x == XY_WRITE )
xydp = dp; /** indicate controller busy on this i/o **/
else
xydp = NULL;
dp->dr_com = x;
}
{
register *rp = &XYCYL;
*rp = bp->b_cyl; /* XYCYL */
# ifdef MANY_DRIVES
if ( xydp )
{
# endif MANY_DRIVES
*--rp = bp->b_wcount; /* XYWCNT */
*--rp = bp->b_addr; /* XYCAR */
# ifdef MANY_DRIVES
}
else
--rp--;
# endif MANY_DRIVES
*--rp = bp->b_ush; /* XYUSH */
*--rp = (bp->b_xmem<<12)|com|INTEB|GO; /* XYCSR */
}
if ( bp->b_cyl > dp->dr_cyl )
dp->dr_dir = IN;
else
if ( dp->dr_cyl > bp->b_cyl )
dp->dr_dir = OUT;
dp->dr_cyl = bp->b_cyl;
}
/*
** Command completion interrupt handler
*/
xyintr()
{
register struct drive *dp;
register struct buf *bp;
register unsigned x;
extern unsigned xyerrors();
if ( XYCSR > 0 )
{
/** Last command completed ok **/
if ( dp = xydp )
{
/** I/O command completion **/
bp = dp->dr_qp;
done:
xydp = NULL;
dp->dr_errcnt = 0;
dp->dr_rtzcnt = 0;
dp->dr_qstate = ( (dp->dr_qp = bp->b_next) != 0 ); /* Q_EMPTY or Q_WAIT */
# ifdef DRIVE_STATS
dp->dr_qsize--;
# endif DRIVE_STATS
xystart();
bp->b_resid = 0;
iodone( bp );
}
else
{
if ( (x = XYSTAT) & SEEKDON )
{
/** Seek complete **/
# ifdef MANY_DRIVES
dp = &xydrv[x & SEEKID];
# else MANY_DRIVES
dp = xydrv;
# endif MANY_DRIVES
if ( dp->dr_state == DR_RDY )
/** Normal seek complete **/
dp->dr_qstate = Q_RDY;
else
{
/** Drive error fixed or Drive come on line **/
dp->dr_state = DR_RDY;
if ( bp = dp->dr_qp )
bp->b_flags =& ~B_ERROR;
}
}
xystart();
}
}
else
{
/*
** ERROR
*/
# ifdef MANY_DRIVES
dp = &xydrv[(XYUSH>>12)&3];
# else MANY_DRIVES
dp = xydrv;
# endif MANY_DRIVES
bp = dp->dr_qp;
/*
** log errors and check for non-recoverable errors
*/
if ( (x = xyerrlog( dp->dr_errors )) == 0 )
{
/** Soft error recoverable **/
/** CRC Error Correction ? **/
if ( (x = dp->dr_errcnt++) < (((sizeof xyretrycm)/2)*3) )
{
/** In place retry from Error Recovery Strategy table **/
if ( ((x = xyretrycm[x/3]) & SEEKINH)
&& ((dp->dr_qstate != Q_IO) || ((-bp->b_wcount) > 256))
)
goto error; /* unsuitable strategy */
xycommand( dp, dp->dr_com|x );
}
else
{
/** Retry Strategy exhausted -- recalibrate ? **/
error:
if ( dp->dr_rtzcnt++ == 0 )
{
/** Recalibrate drive and try again **/
dp->dr_errcnt = 0;
rtz:
xycommand( dp, XY_RTZ );
dp->dr_cyl = 0;
dp->dr_qstate = Q_WAIT;
}
else
{
/** Too many errors, clear controller and give up on this i/o **/
xyerrprt(); /* print out controller registers */
XYCSR = XY_CLEAR|GO;
bp->b_flags =| B_ERROR;
goto done;
}
}
}
else
{
/*
** HARD error
*/
xyerrprt(); /* Print controller registers */
bp->b_flags =| B_ERROR;
if ( x & PROGERR )
goto done; /* Can't fix this one ! */
if ( (dp->dr_com == XY_WRITE) && (x & REWRITE) )
{
/** Almost certainly a BUSERR **/
# ifdef RE_WRITE_FIX
/** Must do a rewrite to avoid an unreadable sector **/
register unsigned y;
x = bp->b_addr; bp->b_addr = 0;
y = bp->b_xmem; bp->b_xmem = 0;
xycommand( dp, XY_WRITE );
bp->b_addr = x;
bp->b_xmem = y;
# else RE_WRITE_FIX
goto done;
# endif RE_WRITE_FIX
}
else
{
dp->dr_state = DR_ERR;
if ( x & RECAL )
if ( dp->dr_rtzcnt++ < RECALTRYS )
/*
** Attempt recalibration
*/
goto rtz;
else
{
/** Drive has unsolvable problem **/
faultclear:
xycommand( dp, XY_FLTCL );
goto done;
}
else
if ( x & DRVUNR )
{
/** Drive off-line **/
extern char *panicstring;
dp->dr_state = DR_UNR;
panicstring++;
printf( "\nXY drive %d off line!\n", dp->dr_id );
panicstring = 0;
}
else
/*
** Hard drive error
*/
if ( x & DRVFLT )
goto faultclear;
else
{
/** Clear controller **/
XYCSR = XY_CLEAR|GO;
goto done;
}
}
}
}
}
/*
** Log errors and return any hard errors
*/
unsigned xyerrlog( ep )
register unsigned *ep;
{
register unsigned e = XYERR;
register unsigned i = 1;
do
if ( e & i )
if ( ++*ep == 0 )
--*ep;
while
( ep++, i =<< 1 );
return( e & ~RETRYER );
}
/*
** Print out controller registers
*/
xyerrprt()
{
register unsigned *ep = XYADDR;
printf( "\nXYERR=%o,ST=%o,CL=%o,WC=%o,BA=%o,DA=%o,CS=%o\n"
,*ep,*ep++,*ep++,*ep++,*ep++,*ep++,*ep++ /* (stacked in reverse) */
);
}
/*
** Physical I/O
*/
xyread( dev )
{
physio( xystrategy, XYRAWBUF, dev, B_READ );
}
xywrite( dev )
{
physio( xystrategy, XYRAWBUF, dev, B_WRITE );
}