Ausam/sys/dmr/unused/rk.c
#
/*
* 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)
{
baderr:
bp->b_flags =| B_ERROR;
goto out;
}
if ( dreset )
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